SAStrutsを勉強してみる (6) - LoginInterceptor -
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();
}
}