前回までで、DBへの書き込みや検索、認証などを解説してきました。今回は、@Security.Authenticatedアノテーションを利用することで、ページへのアクセスを簡単に制御する方法を解説します。
@Security.Authenticated
@Security.Authenticatedアノテーションは、コントローラークラスやメソッドに付与するだけで、認可判定と認可されていなかった場合の処理を共通化してくれる優れものです。これを付与するだけで、メンバーページへのアクセス制限を簡潔に書くことができます。
認証判定クラスの実装
@Security.Authenticatedアノテーションを使うためには、Security.Authenticatorクラスを継承した認証クラスが必要です。
package authenticator;
import play.mvc.*;
import views.html.*;
import controllers.*;
public class MemberAuthenticator extends Security.Authenticator{
@Override
public String getUsername(Http.Context ctx){
return ctx.session().get("userName");
}
@Override
public Result onUnauthorized(Http.Context ctx){
return redirect(routes.HomeController.index());
}
}
getUsernameは、認可判定のメソッドです。ここで何らかのString値を返すと、権限のあるユーザーであると見なされます。
ここでは、必ずしもusernameを返す必要はありません。”OK”という文字列や、空文字を返したって構わないのです。(なぜgetUsernameなどという名前にしたのでしょう…)
権限ごとに判定クラスを作成する必要はありますが、ここで権限の振り分けを行うことが出来るわけです。
onUnauthorizedは、権限のないユーザーがアクセスしようとしたときに呼ばれるメソッドです。例えば、悪意を持ったユーザーが認可のないページへアクセスしようとしたことを記録するために、ユーザーの情報をログに残したりする事も出来ます。
アノテーションを付与する
アノテーションは、クラスかメソッドのいずれかに付与することができます。メソッドに付与するとそのメソッドだけですが、クラスに付与することで配下のメソッド全てに認証機能を追加することができます。
実際にやってみる
今回は、会員専用ページとしてmember.scala.htmlと、管理者専用ページとしてadmin.scala.htmlを新たに作成します。
views.member.scala.html
<!DOCTYPE html>
<h1>ここは会員専用ページです。</h1>
views.admin.scala.html
<!DOCTYPE html>
<h1>ここは管理者専用ページです!</h1>
セッションへ入れる値を追加する
今回は、adminかどうかの判別にuserNameを利用しますので、LoginControllerの変更が必要です。
controllers.LoginController
setSessionメソッドを変更します。
private void setSession(User user) {
session("fullName",user.fullName);
session("userName",user.userName);
session("id",user.id.toString());
}
Authenticatorを定義する
会員ロールと管理者ロールのgetUsernameメソッドを定義します。認証失敗時に飛ばしたいところは同じですが、管理者ページへのアクセス失敗時は、IPアドレスをログに記録するようにしましょう。
authenticator.MemberAuthenticator.java
こちらは、セッションからidが取得出来れば、権限があると判断します。
package authenticator;
import play.mvc.*;
import views.html.*;
import controllers.*;
public class MemberAuthenticator extends Security.Authenticator{
@Override
public String getUsername(Http.Context ctx){
return ctx.session().get("userName");
}
@Override
public Result onUnauthorized(Http.Context ctx){
return redirect(routes.HomeController.index());
}
}
authenticator.AdminAuthenticator.java
こちらは、ユーザー名がadminであれば権限があるとします。
package authenticator;
import play.mvc.*;
import play.Logger;
import views.html.*;
import controllers.*;
public class AdminAuthenticator extends Security.Authenticator{
@Override
public String getUsername(Http.Context ctx){
String userName = ctx.session().get("userName");
return ("admin".equals(userName)) ? userName : null;
}
@Override
public Result onUnauthorized(Http.Context ctx){
Logger.info("unauthorized access : " + ctx.request().remoteAddress());
return redirect(routes.HomeController.index());
}
}
コントローラーを定義する
今回は、コントローラークラスを権限ごとに分けてみましょう。
controllers.MemberController.java
package controllers;
import play.mvc.*;
import javax.inject.*;
import authenticator.MemberAuthenticator;
import views.html.*;
@Security.Authenticated(MemberAuthenticator.class)
public class MemberController extends Controller {
public Result index() {
return ok(member.render());
}
}
controllers.AdminController.java
package controllers;
import play.mvc.*;
import javax.inject.*;
import views.html.*;
import authenticator.*;
@Security.Authenticated(AdminAuthenticator.class)
public class AdminController extends Controller {
public Result index() {
return ok(admin.render());
}
}
権限ごとに分けたので、クラスへ付与することで全てのメソッドを保護することができました。
routesを記述する
routesも忘れてはいけません。
conf.routes
GET /admin controllers.AdminController.index
GET /member controllers.MemberController.index
テストする
会員登録ページで、ユーザー名をadminとして登録し、adminページとmemberページへアクセスできることを確認します。
また、その他のユーザー名でログインして、adminページにアクセス出来ないことと、不正アクセス試行のログが出力されることを確認します。
まとめ
このように、PlayFrameworkでは、アノテーションを付与するだけで、メソッドへのアクセス制限を容易に行うことが出来ます。
非常に読みやすく、簡単なので、積極的に使っていきたいですね。