wicket-auth-rolesをカスタマイズする
wicket-auth-rolesは便利なんだけどもう少し痒いところに手が届かない感がある。
wicket-auth-rolesを使った場合はページクラスに対して@AuthorizeInstantiationを使用すれば簡単に認証制御が可能なのですが
「全てのPageクラスに対してアノテーション付けるの('A`)マンドクセ」
という人も多いかと思います。そういう場合は下記のような方法を検討するとよいかなと思います。
wicket-auth-rolesのまとめ - その2 - ひたすらプログラミング日記
- 親クラスで権限を設定する
- パッケージレベルで権限を設定する
ここに書いてあるように親クラスやパッケージレベルで指定すれば少しは設定が楽になるのだが、まだ不満が残る。
親クラスで権限を設定した場合、アプリを作っていってページ共通の処理を親クラスに追加した後に権限を変更しようとするとめんどくさいことになる気がする。権限に関係ない処理を親クラスに書かなければ良いのだが、多重継承が出来ないJavaでうまく整理する方法が思い付かない。
パッケージレベルで設定した場合は、権限が増えていったときに混乱する恐れがある。せっかくGmailのタグのように権限を設定できるのに、パッケージレベルで設定してしまうと権限構成がツリー構造になってしまう。
そこで権限設定を一カ所に集約する方法で書いてみる。
wicket-auth-rolesを使う場合はWebApplicationにAuthenticatedWebApplicationクラスを使うことになる。
このクラスのinit()メソッドは
@Override protected void init() { super.init(); // Set authorization strategy and unauthorized instantiation listener getSecuritySettings().setAuthorizationStrategy(new RoleAuthorizationStrategy(this)); getSecuritySettings().setUnauthorizedComponentInstantiationListener(this); }
このようになっていて、AuthorizationStrategyにRoleAuthorizationStrategyを設定している。
このクラスはCompoundAuthorizationStrategyクラスを継承しているので、CompoundAuthorizationStrategy#addメソッドを使って任意のStrategyを追加することができる。
まずはロールを定義しているRole.java
public class Role { public static final String ADMIN = "admin"; public static final String STAFF = "staff"; public static final String MANAGER = "manager"; public static final Roles MANAGER_ROLE = new Roles(); static { MANAGER_ROLE.add(ADMIN); MANAGER_ROLE.add(MANAGER); } }
とりあえずMANAGERを追加。
そして新たなStrategyのクラスAppAuthorizationStrategy.javaを作る。
import java.util.HashMap; import java.util.Map; import org.apache.wicket.Component; import org.apache.wicket.authorization.Action; import org.apache.wicket.authroles.authorization.strategies.role.AbstractRoleAuthorizationStrategy; import org.apache.wicket.authroles.authorization.strategies.role.IRoleCheckingStrategy; import org.apache.wicket.authroles.authorization.strategies.role.Roles; import org.apache.wicket.markup.html.link.BookmarkablePageLink; import org.apache.wicket.request.component.IRequestableComponent; /** * 各ページの権限 * * \begin{itemize} * \item 管理権限 * \begin{itemize} * \item ManagerPage: マネージャー用 * \end{itemize} * \end{itemize} * * @spec Role */ public class AppAuthorizationStrategy extends AbstractRoleAuthorizationStrategy { private static final Map<Class<?>, Roles> ROLES = new HashMap<Class<?>, Roles>(); static { ROLES.put(ManagerPage.class, Role.MANAGER_ROLE); } public AppAuthorizationStrategy(IRoleCheckingStrategy roleCheckingStrategy) { super(roleCheckingStrategy); } @Override public <T extends IRequestableComponent> boolean isInstantiationAuthorized( Class<T> componentClass) { if (ROLES.containsKey(componentClass)) return hasAny(ROLES.get(componentClass)); return true; } @Override public boolean isActionAuthorized(Component component, Action action) { if (action.getName().equals(Action.RENDER) && ROLES.containsKey(component.getClass())) return hasAny(ROLES.get(component.getClass())); // ページクラスの表示権限がない場合はそれに対するリンクも表示しない if (component instanceof BookmarkablePageLink){ if (ROLES.containsKey(((BookmarkablePageLink<?>) component).getPageClass())){ return hasAny(ROLES.get(((BookmarkablePageLink<?>) component).getPageClass())); } } return true; } }
このクラスにページと権限を追加していくことによって、全Componentクラスの権限をこのクラスだけで管理できる。
ここではページクラスを直接書いているが、別にinterfaceでもクラス名のprefixでも色々応用できるはず。
これをWebApplicationで追加する。
public class WebApp extends AuthenticatedWebApplication { @Override protected void init(){ super.init(); ((RoleAuthorizationStrategy)getSecuritySettings().getAuthorizationStrategy()).add(new AppAuthorizationStrategy(this));
ここまで書いておいてアレだが…欲しかったのは権限一カ所集約クラスよりもPageクラスの権限を見てBookmarkablePageLinkの表示/非表示を切り替える機能だったかもしれない。
もう一度あとで書く。