実行できるかのチェック

権限によって実行させたくないActionがある場合って、普通どういう風にコーディングするんだろ?


前は、抽象クラスのActionをつくって実装してた。

public abstract AdminServiceAction extends Action {
    protected ActionForward executeLogic(ActionMapping mapping,
            ActionForm form, HttpServletRequest request,
            HttpServletResponse response) throws Exception {

        User logonUser = (User) request.getSession().getAttribute("logonUser");
        if (logonUser != null && logonUser.isAdmin()) {
            return doExecute(mapping, form, request, response);
        }
        return mapping.findForward("deny");
    }

    protected abstract ActionForward doExecute(ActionMapping mapping,
            ActionForm form, HttpServletRequest request,
            HttpServletResponse response) throws Exception;


Tomcatとかの認証機能を利用して実装する方法もあるのかなー。でも知らない・・・勉強不足・・・



今回は、勉強をかねてInterceptorで実装してみた。
POJOのDipatchActionを利用してたので、メソッド単位で実行できるかをチェックするようにした。


こんな感じでInterceptorを作ってみた。2回目の作成だったからすんなり作れた記憶があるけど、今見ると気になる点が何箇所かある。まっいっか。

public class PermitServiceInterceptor extends AbstractInterceptor {

    private static final String DEFAULT_PERMIT = "DEFAULT_PERMIT";

    private static final String PERMIT_SUFFIX = "_PERMIT";

    // -----------------------------------------------------------------------

    public String getKey() {
        return "role";
    }

    public String getForward() {
        return "deny";
    }

    // -----------------------------------------------------------------------

    public Object invoke(MethodInvocation invocation) throws Throwable {
        String methodName = invocation.getMethod().getName();
        Class actionClazz = getTargetClass(invocation);

        List allowRoles = getAllowRoles(actionClazz, methodName);
        if (isPermit(allowRoles, getRole())) {
            return invocation.proceed();
        } else {
            return getForward();
        }
    }

    private List getAllowRoles(Class clazz, String methodName) {
        BeanDesc beanDesc = new BeanDescImpl(clazz);

        String fieldName = methodName + PERMIT_SUFFIX;
        if (beanDesc.hasField(fieldName)) {
            Field field = beanDesc.getField(fieldName);
            return AnnotationConfigUtil.getParameterList(field);
        } else if (beanDesc.hasField(DEFAULT_PERMIT)) {
            Field field = beanDesc.getField(DEFAULT_PERMIT);
            return AnnotationConfigUtil.getParameterList(field);
        }

        return new ArrayList();
    }

    private String getRole() {
        S2Container container = SingletonS2ContainerFactory.getContainer();
        HttpServletRequest request = container.getRequest();

        return (String) request.getSession().getAttribute(getKey());
    }

    private boolean isPermit(List allowRoles, String role) {
        if (allowRoles.contains("ALL")) {
            return true;
        }

        return allowRoles.contains(role);
    }

}

嫌な点は、権限の確認を文字列の比較で行っているところ。
だから、logonの処理で

    request.getSession().setAttribute("role", logonUser.getRole().toString());

とかして、sessionにRoleの文字列を設定する必要がある。
かっこわるい。


最初はメソッドごとにスタティック変数アノテーションを記述してたんだけど、だんだんメンドくさくなってきて、DEFAULT_PERMITってのを作った。


結局、Actionインターフェースにこんな風に、

public interface AuthAction {

    public static final String DEFAULT_PERMIT = "ALL";

    /**
     * ログオンする
     */
    public String logon();

    /**
     * ログオフする
     */
    public String logoff();

    /**
     * 他のユーザーになりかわる。
     */
    public String transform();

    public static final String transform_PERMIT = "ADMIN";

}

それで、DICONファイルをこんな感じで記述すれば、完成。

<components>
    <include path="aop.dicon"/>

    <component name="permitServiceInterceptor" class="study.xxx.extention.struts.interceptors.PermitServiceInterceptor"/>
    <component name="actionInterceptorChain" class="org.seasar.framework.aop.interceptors.InterceptorChain">
        <initMethod name="add"><arg>aop.traceInterceptor</arg></initMethod>
        <initMethod name="add"><arg>permitServiceInterceptor</arg></initMethod>
    </component>

    <!-- action -->
    <component name="/auth"
               class="study.xxx.party.web.impl.AuthActionImpl" instance="prototype">
        <aspect>actionInterceptorChain</aspect>
    </component>

</components>


権限の確認はもっと動的にできても良いと思うけど、この程度で十分のときもあると思うから、これはこれでありかもね。って思った。
Interceptorを作る勉強としてはちょうど良かったなー。


っていうか、これは独自のスタティック変数アノテーションのつくり方の勉強だったのかも。終わってから思ったけど。

うーむ。もっときれいに作れそうな気がしてきた。
少し見直してみよっと。





そういえばJ2SE5.0のアノテーションってどんな感じなんだろう。
ふーん。インターフェースみたいに定義するんだー。



じゃぁ、S2Daoを参考にしてインターフェースを使えばいいのができるかも。

public interface Permit {

    public String TARGET = "METHOD";

    public String SUFFIX = "PERMIT";

    public List getValue();

}

って感じのインターフェースを作って、

    String methodName = invocation.getMethod().getName();
    Class clazz= getTargetClass(invocation);
    Permit permit = (Permit) AnnotatilUtil.getMethodAnnotationClass(
            Permit.class, clazz, methodName);

とするみたいなのかなー


っていうかこんな風に作る意味があるのか?
明日になってから作ることにしよう。ほかのいい案が出るかもしれないし。