SAStrutsを勉強してみる (6) - LoginInterceptor -

2012-10-07T00:00:00+00:00 Java SAStruts

http://d.hatena.ne.jp/idesaku/20081226/1230263190 をそのまま試してみる。まぁちょっとだけ違いますけど

要件

sample.action.dashboardなパッケージにあるアクションを参照する場合にはログインが必要なのでLoginActionに飛ばす。但し、sample.action.dashboard.LoginActionだけは非認証でアクセス出来るようにしないといけないので(ry

んまぁ上記のリンクにあるようにインターセプタでそこら辺をコントロールする。で、必要なのはインターセプタ及びその設定と各種アクションクラスとビュー、んでユーザー情報を参照するEntityとその情報をセッションで管理するDtoオブジェクトクラス

User.java

ユーザー情報を参照するエンティティクラス

package sample.entity;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "user")
public class User implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    public int id;

    @Column(name = "username")
    public String userName;

    @Column(name = "password")
    public String password;
}

というような感じでテーブルも作っておく

UserDto.java

ユーザー情報を保持しているEntityから情報をセッションで管理するクラス

package sample.dto;

import java.io.Serializable;

import org.seasar.framework.container.annotation.tiger.Component;
import org.seasar.framework.container.annotation.tiger.InstanceType;

@Component(instance = InstanceType.SESSION)
public class UserDto implements Serializable {

    private static final long serialVersionUID = 1L;

    public int id;
    public String userName;

}

http://sastruts.seasar.org/featureReference.html#Action によると「ログインしたユーザに関する情報など、 アクションフォーム以外をセッションで管理する場合、 ルートパッケージ.dtoにXxxDto作成し、@Componentでセッションで管理されるように定義します」との事らしい。でこれを下にインターセプタで認証済みユーザーセッションがあるかないかとかをチェックする

LoginInterceptor.java

package sample.interceptor;

import javax.annotation.Resource;

import org.aopalliance.intercept.MethodInvocation;
import org.seasar.framework.aop.interceptors.AbstractInterceptor;
import org.seasar.struts.annotation.Execute;

import sample.dto.UserDto;

public class LoginInterceptor extends AbstractInterceptor {

    private static final long serialVersionUID = 1L;

    @Resource
    protected UserDto userDto;

    @Override
    public Object invoke(MethodInvocation inv) throws Throwable {
        // @Executeアノテーションをあるかチェック
        if (inv.getMethod().isAnnotationPresent(Execute.class)) {
            // ログインユーザーセッションがあるかをチェック
            if (userDto != null && userDto.userName != null) {
                // あればそのままリクエストを流す
                return inv.proceed();
            }

            // なければログインなアクションにぶっ飛ばす
            return "/dashboard/login?redirect=true";
        }

        return null;
    }
}

nullを返すべきかで悩んだのですが、とりあえずそのままにしておく。でこれのパッケージ名なんすけど、「NamingContextImpl.addRootPackageName(convension.dicon) + ".interceptor"」という感じにすれば、規約に沿って注入される模様げ。でこいつを作動させる為にcustomizer.diconをいじる

customizer.dicon

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN" "http://www.seasar.org/dtd/components24.dtd">
<components>
    <include path="default-customizer.dicon" />

    <component
        name="actionCustomizer"
        class="org.seasar.framework.container.customizer.CustomizerChain">

        <initMethod name="addAspectCustomizer">
            <arg>"aop.traceInterceptor"</arg>
            <arg>true</arg>
        </initMethod>
        <initMethod name="addAspectCustomizer">
            <arg>"actionMessagesThrowsInterceptor"</arg>
        </initMethod>
        <initMethod name="addCustomizer">
            <arg>
                <component class="org.seasar.framework.container.customizer.TxAttributeCustomizer" />
            </arg>
        </initMethod>
        <initMethod name="addCustomizer">
            <arg>
                <component class="org.seasar.struts.customizer.ActionCustomizer" />
            </arg>
        </initMethod>
        <initMethod name="addCustomizer">
            <arg>
                <component class="org.seasar.framework.container.customizer.AspectCustomizer">
                    <property name="interceptorName">"loginInterceptor"</property>
                    <!-- 対象とするパッケージ名及びクラス名を設定 -->
                    <initMethod name="addClassPattern">
                        <arg>"sample.action.dashboard"</arg>
                        <arg>".*"</arg>
                    </initMethod>

                    <!-- 除外するパッケージ名及びクラス名を設定 -->
                    <initMethod name="addIgnoreClassPattern">
                        <arg>"sample.action.dashboard"</arg>
                        <arg>"LoginAction"</arg>
                    </initMethod>
                </component>
            </arg>
        </initMethod>
    </component>

    <!-- 以下省略 -->
</components>

IndexAction.java

package sample.action.dashboard;

import org.seasar.framework.aop.annotation.RemoveSession;
import org.seasar.struts.annotation.Execute;

public class IndexAction {

    @Execute(validator = false)
    public String index() {
        return "index.jsp";
    }

    @Execute(validator = false, redirect = true)
    @RemoveSession(name = "userDto") // userDtoなセッションを削除
    public String logout() {
        return "";
    }
}

sample.action.dashboard.IndexActionなんでLoginInterceptorをもろにくらうので非認証状態でアクセスしてもLoginActionにぶっ飛ばされる事を確認する

LoginAction.java

package sample.action.dashboard;

import javax.annotation.Resource;

import org.seasar.framework.aop.annotation.RemoveSession;
import org.seasar.struts.annotation.Execute;
import org.seasar.struts.annotation.Required;
import org.seasar.struts.enums.SaveType;

import sample.dto.UserDto;
import sample.entity.User;
import sample.service.UserService;

public class LoginAction {

    @Required
    public String userName;

    @Required
    public String password;

    @Resource
    private UserService userService;

    @Resource
    private UserDto userDto;

    @Execute(validator = false)
    @RemoveSession(name = "userDto")
    public String index() {
        return "index.jsp";
    }

    @Execute(
        input = "?redirect=true",
        saveErrors = SaveType.SESSION,
        redirect = true
    )
    public String login() {
        try {
            User user = userService.getUser(userName,  password);

            if (user != null) {
                userDto.id = user.id;
                userDto.userName = user.userName;

                // ActionFormがあるならこれで良いらしい
                // Beans.copy(userForm, userDto).execute();
            }
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        }

        return "/dashboard";
    }
}

セッション管理されているDtoオブジェクトは値をぶち込むだけで良いみたい。という感じで作ればセッション管理による認証な所を作る事は可能な模様。まぁインターセプタなテスト法とかまだ調べてないんすけど

余談

UserService.javaはこんな感じ

package sample.service;

import org.seasar.extension.jdbc.service.S2AbstractService;
import org.seasar.extension.jdbc.where.SimpleWhere;

import sample.entity.User;
import sample.util.SampleUtil;

public class UserService extends S2AbstractService<User> {
    public User getUser(String username, String password) {
        if (username == null) {
            throw new IllegalArgumentException("required username");
        }

        if (password == null) {
            throw new IllegalArgumentException("required password");
        }

        SimpleWhere where = new SimpleWhere();
        where.eq("username", username);
        where.eq("password", SampleUtil.toSHA1(password.getBytes()));

        return select().where(where).getSingleResult();
    }
}

SAStrutsを勉強してみる (7) - LoginInterceptorをテスト - android.support.v13.dreams.BasicDream