OpenID認証によるシングルサインオン
App Engine 1.3.4でOpenID認証に対応しました。調べてみると意外と普通にシングルサインオンできたので、使い方と仕組みを説明します。
まず、OpenID認証を要求するURLパターンをweb.xmlに書きます。
<security-constraint> <web-resource-collection> <web-resource-name>Authentication required url</web-resource-name> <url-pattern>/user/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>*</role-name> </auth-constraint> </security-constraint>
例えば http://xxx.appspot.com/user/hoge にアクセスしようとすると、App Engineは認証が必要なページと判断して下記のURLにリダイレクトします。
そこで、/_ah/login_required に対応するサーブレットを定義します。
<servlet> <description></description> <display-name>LoginRequiredServlet</display-name> <servlet-name>LoginRequiredServlet</servlet-name> <servlet-class>org.hidetake.app.xxx.controller.LoginRequiredServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>LoginRequiredServlet</servlet-name> <url-pattern>/_ah/login_required</url-pattern> </servlet-mapping>
ログインページにフォワードするサーブレットでOKです。
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String backUrl = request.getParameter("continue"); if(backUrl == null) { response.sendRedirect("/"); return; } request.setAttribute("backUrl", backUrl); request.getRequestDispatcher("/loginRequired.jsp").forward(request, response); }
ログインページは気合いを入れて作りましょう。はてなIDとかmixi IDとかアイコンで選べると格好いいですよね。
<form action="/login" method="post"> <fieldset> <input type="text" name="uid" /> <input type="hidden" name="backUrl" value="${f:h(backUrl)}" /> <input type="submit" /> </fieldset> </form>
ログインページは下記のURLにIdentifierをPOSTします。
そこで、/login に対応するサーブレットを定義します。
// この例ではSlim3を使ってますが、サーブレットでも同じです public class LoginController extends Controller { @Override public Navigation run() throws Exception { String backUrl = asString("backUrl"); String uid = asString("uid"); if (backUrl == null || uid == null) { return redirect("/"); } UserService userService = UserServiceFactory.getUserService(); User currentUser = userService.getCurrentUser(); if (currentUser != null) { return redirect("/"); } Set<String> attributesRequest = Sets.newHashSet( "openid.mode=checkid_immediate", "openid.ns=http://specs.openid.net/auth/2.0", "openid.return_to=" + backUrl); String loginUrl = userService.createLoginURL( backUrl, request.getServerName(), uid, attributesRequest); return redirect(loginUrl); } }
サーブレットではOpenID認証に必要なURLを生成し、OpenID Providerにリダイレクトします。
OpenID Providerはパスワード認証等で本人確認を行った後、App Engineにリダイレクトします。App Engineは認証ステートをCookieに書いて、本来アクセスしようとしたURLにリダイレクトします。ここで初めて本来アクセスしようとしたページが表示されます。
認証済みのIdentifierを取得するには下記のようにすればOKです。
public Navigation run() throws Exception { requestScope("userId", request.getUserPrincipal().getName()); return forward("index.jsp"); }
App Engineで認証ステートの管理までやってくれるので便利ですね。アカウント情報管理やアクティベーションの仕組みを汎用化してライブラリにできると面白そう。