Play Framework で作る簡易認証システム(その2: ログイン機能の実装)
前回から引き続き、簡易認証システムシステムを開発していく。
今回は登録したユーザ情報に基づいてログインできるような機能を開発していく。
1. ログイン画面表示用URL"/login"のリクエストを処理するController の
まずは、下記の通りroutesから登録しよう。
■routes
GET /login controllers.Application.login()
次にコントローラ(Application.login)を実装していくが、その前にFormクラスを用意しよう。
ログイン画面ではアカウント名(username)とパスワード(password)を持つのでForm クラスは下記となる。
ここで、validate()メソッドが定義されているが、ユーザ/パスワードの組み合わせでDBを参照し、
正しいかどうかを判断してくれるようになる。
このクラスがFormのコンストラクタに渡されると、実際にフォームをPOSTする前に、
このvalidate()メソッドを実行し、ユーザ/パスワードの組み合わせが正しいかどうかを判断してくれるのである。
■forms/LoginForm.java
package forms; import play.data.validation.*; import play.db.ebean.*; import java.security.NoSuchAlgorithmException; import models.*; public class LoginForm { @Constraints.Required private String username; @Constraints.Required private String password; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String validate() throws NoSuchAlgorithmException{ if(authenticate(username,password)==null){ return "Invalid user or password"; } return null; } public static User authenticate(String username, String password) throws java.security.NoSuchAlgorithmException{ Model.Finder<Long, User> find = new Model.Finder<Long, User>(Long.class, User.class); return find.where().eq("username", username).eq("password", password).findUnique(); } }
続いて、"/login"にアクセスした際に呼び出されるコントローラ(Application.login())は下記の通りになる。
■Application.login()
public static Result login(){ Form<LoginForm> loginForm = new Form(LoginForm.class); return ok(login.render(loginForm)); }
2. ログイン画面用のView の実装
シンプルに下記の通り。
@(loginForm: Form[forms.LoginForm]) @import helper._ @form(routes.Application.authenticate) { @inputText(userForm("username")) @inputText(userForm("password")) <br> <button id="submit" type="submit" value="Submit" >Login</button> }
3. セッション情報の付与
上記2.のフォームをPOSTした際に呼び出すコントローラ authenticate メソッドを下記の通り実装する。
■Application.authenticate()
public static Result authenticate() { Form<LoginForm> loginForm = form(LoginForm.class).bindFromRequest(); if (loginForm.hasErrors()) { return badRequest(login.render(loginForm)); } else { session().clear(); session("username", loginForm.get().getUsername()); String returnUrl = ctx().session().get("returnUrl"); if(returnUrl == null || returnUrl.equals("") || returnUrl.equals(routes.Application.login().absoluteURL(request()))) { returnUrl = "/inputchat"; } return redirect(returnUrl); } }
ここでのポイントは下記2行だけで、ここで、セッション"username" にフォームに入力した値(username)を格納している。つまり、セッション情報を搭載しているだけである。
session().clear();
session("username", loginForm.get().getUsername());
3. 認証ロジックの実装
次に認証の肝であるクラスを実装する。models以下に下記クラスを準備する。
■models/Secured.java
package models; import play.mvc.Http.Context; import play.mvc.Result; import play.mvc.Security.Authenticator; public class Secured extends Authenticator { @Override public String getUsername(Context ctx){ return ctx.session().get("username"); } @Override public Result onUnauthorized(Context ctx){ String returnUrl = ctx.request().uri(); if(returnUrl == null){ returnUrl="/"; } ctx.session().put("returnUrl", returnUrl); return redirect(controllers.routes.Application.login()); } }
このクラスはAuthenticator を継承したもので、getUsernameとonUnauthorized という
二つのメソッドをOveride する必要がある。
開発者は、getUsernameメソッドで認証OKとする条件を実装する必要がある。
ここでは、セッションから"username" の値が取得できたら認証OKとしている。
また、onUnauthorizedメソッドには認証NGだった場合の処理を実装するが、"/login"へリダイレクトされていることが分かる。
3. アノテーションの追加
最後に閲覧制限をかける画面を指定するためにアノテーションをControllerに設定する。
Application.inputchat()メソッドに"@Security.Authenticated(models.Secured.class)" アノテーションを追加すればよい。これで"/inputchat" にアクセスした時、セッション情報"username"が載っていないとはじかれるようになる。
つまり、Securedクラスでセッション情報"username"を取得できた場合にのみ、指定のリクエスト"/inputchat"を受け入れられるようになる。
■Application.java
@Security.Authenticated(models.Secured.class) public static Result inputchat() { //中略 }