wicket-auth-rolesのまとめ - その1
Wicketの認証機能に関して便利なクラス等が提供されている「wicket-auth-roles」。自分の今までの知識を整理するためにもエントリーを書いていこうかなと思います。
Wicketの認証機能に関して今までいろいろ参考にさせてもらったサイト、ブログ。勝手ながら一方的お礼を言わせてもらいますw。ありがとう御座いましたm(_ _)m
Fuckin’ 殴り書き
http://rio1218.blog26.fc2.com/
NAGASEYASUHiTO ’POCHi’ Hatena
あと、もちろんこちらも
オープンソース徹底活用WicketによるWebアプリケーション開発
個人的にwicket-auth-rolesを使用することで得られるメリットはなんといっても
- アノテーションを使用した制御が可能
他にはすでに認証用のIAuthorizationStrategyがあったり、そのクラス等をすでに設定済みのWebApplicationクラスがあったり。このライブラリを使いこなせば認証処理は格段に楽になると思います。
で、実際どういう感じで使うかですが、今回のエントリー用に作成するページは4つ
- トップページ(TopPage - NextPageへのリンクがある。
ADMINADMIN、USER権限をもったユーザーのみアクセス可能) - 次のページ(NextPage -
ADMIN、USERADMIN権限をもったユーザーがアクセス可能) - ログインページ(SignInPage - ログインフォームがある。)
- 権限が無かったですよページ(UnAuthorizedPage - アクセス使用としたページに対する権限をユーザーが持っていない場合に表示されるページ)
他にSpringを使用してDIされたサービスクラスを使用してSignInの処理を実行するって感じにします。
前置きが長くなってしまいましたが、wicket-auth-rolesを使ったクラスがどういう感じになるか。
とりあえず説明は後回しにして、作成するクラスを全部列挙してみます。(HTMLとimport文は省略) wicketのバージョンは1.4RC2です。
MyApplication.java
public class MyApplication extends AuthenticatedWebApplication { @Override public Class<? extends Page> getHomePage() { return TopPage.class; } public WicketApplication() { } @Override protected void init() { super.init(); addComponentInstantiationListener(new SpringComponentInjector(this)); } @Override protected Class<? extends WebPage> getSignInPageClass() { return SignInPage.class; } @Override protected Class<? extends AuthenticatedWebSession> getWebSessionClass() { return MySession.class; } @Override protected void onUnauthorizedPage(Page page) { throw new RestartResponseAtInterceptPageException(UnAuthorizedPage.class); } }
MySession.java
public class MySession extends AuthenticatedWebSession{ private Roles roles; @SpringBean private EmployeeService employeeService; public MySession(Request request) { super(request); InjectorHolder.getInjector().inject(this); } @Override public boolean authenticate(String username, String password) { if(employeeService.signIn(username, password)){ this.roles = new Roles(new String[]{Roles.ADMIN,Roles.USER}); return true; }else { return false; } } } @Override public Roles getRoles() { return roles; } public static MySession get(){ return (MySession)Session.get(); } }
TopPage.java
@AuthorizeInstantiation({Roles.ADMIN,Roles.USER}) public class TopPage extends WebPage { public TopPage() { add(new PageLink<Void>("next", NextPage.class)); } }
NextPage.java
@AuthorizeInstantiation({Roles.ADMIN}) public class NextPage extends WebPage { public NextPage() { } }
SignInPage.java
public class SignInPage extends WebPage { public SignInPage() { add(new FeedbackPanel("feedback")); Form form = new Form("form"); add(form); final TextField<String> empIdText = new TextField<String>("employeeId",new Model<String>()); form.add(empIdText); final TextField<String> passText = new PasswordTextField("password",new Model<String>()); form.add(passText); form.add(new AbstractFormValidator() { @Override public FormComponent<?>[] getDependentFormComponents() { return new FormComponent<?>[]{empIdText, passText}; } @Override public void validate(Form<?> form) { if (!MySession.get().signIn(empIdText.getInput(),passText.getInput())) error(passText); } @Override protected String resourceKey() { return "SignInValidation"; } }); form.add(new Button("submit") { @Override public void onSubmit() { if(!continueToOriginalDestination()) setResponsePage(TopPage.class); } }); } }
UnAuthorizedPage.java
public class UnAuthorizedPage extends WebPage { public UnAuthorizedPage() { } }
MyApplication.java
まず、基本のWebApplicationのサブクラス。web.xmlのfilterに登録するクラスですね。
これはwicket-auth-rolesが提供するAuthenticatedWebApplicationクラスのサブクラスとして作成します。
実装するメソッドは4つ。
getHomePage
getHomePageでは最初に表示させるページのClassオブジェクトを返します。ここはいつも通りですね。
init
initメソッドでは最初に必ずスーパークラスのinitメソッドを呼び出します。また、Springを使用するのでaddComponentInstantiationListener(new SpringComponentInjector(this))と記述します。Springを使用する際の呪文みたいなもんですねw
getSignInPageClass(AuthenticatedWebApplicationでは抽象メソッド)
名前の通りここではSignIn(ログイン)のページクラスのClassオブジェクトを返します。
未ログインの状態で、且つアノテーション等による権限の設定がされているページに対してアクセスされた場合にここで指定されたページが表示されます。
getWebSessionClass(AuthenticatedWebApplicationでは抽象メソッド)
ここには後述する認証機能を実装したAuthenticatedWebSessionのサブクラスのClassオブジェクトを返します。
onUnauthorizedPage
アクセスしようとしたページに対してユーザーの権限が不足していた場合にこのメソッドが呼ばれます。引数にはアクセスしようとしたPageクラスのオブジェクトが渡されてきます。今回は「権限ないよ〜」と書かれたページを表示するためRestartResponseAtInterceptPageExceptionをthrowしています。ちなみにオーバーライドするだけしてRestartResponseAtInterceptPageExceptionをthrowしなかった場合は何事もなかったかのように権限不足のページが表示されてしまうので気をつけましょうw
MySession.java
認証ロジックを実装したSessionクラスです。通常であればWebSessionを継承し、アプリケーションに合ったサブクラスを作成するかと思いますが、wicket-auth-rolesを使用する場合はAuthenticatedWebSessionクラスを継承し、独自のSessionクラスを作成します。
コンストラクタ
重要なのはInjectorHolder.getInjector().inject(this)です。通常Spring等のDIコンテナを使用する場合、Wicketのコンポーネントに対してはDI用のアノテーション(Springの場合は@SpringBean)を付けていれば勝手にInjectしてくれますが、コンポーネントではないSessionのサブクラスにおいては@SpringBeanを使用するだけではDIコンテナにて管理されているオブジェクトをInjectしてくれません。そういう場合はInjectorHolder.getInjector().inject(this)を記述することでインスタンス生成時にDIコンテナで管理されているオブジェクトをInjectしてくれるようになります。
追記:4/20
Guiceを使用する場合はInjectorHolder.getInjector().inject(this)だけではInjectしてくれないようです。こちらにてGuiceを使用した場合のSessionオブジェクトに対するInjectの方法に関するエントリーが書かれています。ということはSeaserを使用した場合も違うのかな??
authenticate(AuthenticatedWebSessionでは抽象メソッド)
このメソッド内にてInjectされたServiceクラスやDaoクラスを使用し、認証ロジックを実行させます。単にtrueもしくはfalseを返してもいいのですが、trueだった場合は適切なRole情報を設定しないといけません。後述するgetRolesメソッドはページ表示の際に毎回呼び出されるので、この時点で適切なRole情報を設定しておくの良いかなと。*1今回はとりあえず固定でADMINとUSERの両方の権限を持つRolesオブジェクトを返すようにしています。実際のアプリケーションではここからさらにDBにアクセスしたりして動的にRolesオブジェクトを生成するようになるかと思います。
getRoles(AuthenticatedWebSessionでは抽象メソッド)
これはそのまんまですねw
get
これもそのまんまですね、Pageクラス等で使用する際にダウンキャストを不要にするためのシンタックスシュガー的メソッドです。
TopPage.java
ADMINとUSERの権限のどちらかをユーザーが持っていればこのページは表示可能です。初回アクセス時は未ログインの状態なのでこのページを表示させようとしてもログインページ(SignInPage)に自動的に飛ばされます。
NextPage.java
ADMIN権限のみ表示可能なページです。MySession#authenticate内でRolesオブジェクトを生成する際にADMINを付加しないようにしてからログイン後にこのページにアクセスした場合はこのページは表示できず、MyApplication#onUnauthorizedPageにて設定しているUnAuthorizedPageへ遷移します。
SignInPage.java
コンポーネントの使い方とかは省略するとして、要点は下記の部分になります。
@Override public void validate(Form<?> form) { if (!MySession.get().signIn(empIdText.getInput(),passText.getInput())) error(passText); }
AbstractFormValidator#validateメソッド内にてMySessionクラスを取得し、signInメソッドを呼び出すことで認証処理を実行しています。ちなみにこのsignInメソッドはMySessionクラスの親クラスであるAuthenticatedWebSessionクラスにて定義されていて、このメソッドの中でauthenticateメソッドが呼ばれています(TemplateMethodパターンですね)。
また、下記のButtonクラスのonSubmitメソッドですが
@Override public void onSubmit() { if(!continueToOriginalDestination()) setResponsePage(TopPage.class); }
continueToOriginalDestinationメソッドを呼ぶと元々アクセスしようとしていたページが表示されます。例えば未ログインの状態でTopPageにアクセス使用とした場合、未ログインなのでSignInPageが表示されますが、このcontinueToOriginalDestinationを呼んでおくと勝手に次のページにはTopPageが表示されます。if文を使っているのは直接このページにアクセスしてきた場合はcontinueToOriginalDestinationを呼び出してもページ遷移はせずにfalseが帰ってくるので、デフォルトの動作としてTopPageを表示させるために上記のような記述をしています。
とまぁ、基本的なwicket-auth-rolesの使い方はこんな感じでしょうか。なるべく実際の構築するアプリケーションを意識して書いてみました。次回以降はカスタマイズ方法とか応用編って感じでエントリーを書いてみようと思います。何か間違って所とかあったらご指摘いただけると嬉しいです。
*1:ログイン中にもユーザーの権限情報が変更される可能性のあるアプリケーションであれば別ですが。