T2+GAE+guice+AIR 多分その3 認証処理を作ってみる。

まえのエントリーでも書きましたが、
T2はその拡張性の高さも使いやすい理由の一つだと思います。
T2では、各処理のポイントに拡張する方法を提供しているので、
非常に扱いやすのです。

拡張を差し込むという意味では、インターセプターもいますが、
利用してみたところ、動きが見えずらい、いつ動くの?
との声が多く結構難しんだな〜と思いました。

てことで、T2のPluginを使って、認証処理を作成してみました。

実装

前回のエントリーにも書いてますが、若干GuiceAdapterを拡張してますので、
あしからず。

まずAnnotationです。
Pageクラスのクラスまたはメソッドにつけて、
認証するかしないかを判断します。

package jp.stk.gae.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE , ElementType.METHOD} )
public @interface Auth {
	public boolean onlyAdmin() default false;
	public boolean canUseNonUser() default false;
}

次にPluginです。

抽象クラス:AuthPlugin

package jp.stk.gae.plugin;

import org.t2framework.t2.plugin.AbstractPlugin;

public abstract class AuthPlugin extends AbstractPlugin {

}

実装クラス:AuthPluginImpl

package jp.stk.gae.plugin.impl;

import java.lang.reflect.Method;

import javax.servlet.http.HttpServletRequest;

import jp.stk.gae.annotation.Auth;
import jp.stk.gae.exception.AuthException;
import jp.stk.gae.plugin.AuthPlugin;

import org.t2framework.commons.meta.MethodDesc;
import org.t2framework.commons.util.Logger;
import org.t2framework.commons.util.StringUtil;
import org.t2framework.t2.action.ActionContext;
import org.t2framework.t2.contexts.HttpMethod;
import org.t2framework.t2.spi.Navigation;

import com.google.appengine.api.users.UserService;
import com.google.inject.Inject;

public class AuthPluginImpl extends AuthPlugin {
	@SuppressWarnings("unused")
	private final static Logger logger = Logger.getLogger(AuthPluginImpl.class);

	@Inject
	UserService userService;

	@Inject
	HttpServletRequest request;

	@Override
	public Navigation beforeActionInvoke(ActionContext actionContext,
			MethodDesc targetMethod, Object page, Object[] args) {
		Method m = targetMethod.getMethod();
		Class<?> c = m.getDeclaringClass();
		Auth classAnn = c.getAnnotation(Auth.class);
		Auth methodAnn = m.getAnnotation(Auth.class);
		if (classAnn == null && methodAnn == null) {
			return super.beforeActionInvoke(actionContext, targetMethod, page,
					args);
		}

		Auth auth = methodAnn != null ? methodAnn : classAnn;

		if (!userService.isUserLoggedIn()) {
			addLoginLink2Request(actionContext);
			if (auth.onlyAdmin()) {
				throw new AuthException("この画面は利用できません。");
			}
			if (!auth.canUseNonUser()) {
				throw new AuthException("ログインしていない方は、利用できません。");
			}
			return super.beforeActionInvoke(actionContext, targetMethod, page,
					args);
		}
		addLogoutLink2Request(actionContext);
		if (auth.onlyAdmin() && !userService.isUserAdmin()) {
			throw new AuthException("この画面は利用できません。");
		}

		return super.beforeActionInvoke(actionContext, targetMethod, page, args);
	}

	private void addLoginLink2Request(ActionContext actionContext) {
		if (actionContext.getRequest().isAjaxRequest()
				|| actionContext.getRequest().isAmfRequest()) {
			return;
		}

		String url = actionContext.getRequest().getRequestURI();
		String queryString = null;
		if (actionContext.getRequest().getMethod() != HttpMethod.GET) {
			url = "/";
			queryString = "";
		} else {
			queryString = request.getQueryString();
		}

		String redirectUrl = url;
		if (!StringUtil.isEmpty(queryString)) {
			redirectUrl += url.contains("?") ? "&" : "?";
			redirectUrl += queryString;
		}
		String loginUrl = userService.createLoginURL(redirectUrl);
		actionContext.getRequest().setAttribute("loginUrl", loginUrl);
	}

	private void addLogoutLink2Request(ActionContext actionContext) {
		if (actionContext.getRequest().isAjaxRequest()
				|| actionContext.getRequest().isAmfRequest()) {
			return;
		}

		String logoutUrl = userService.createLogoutURL("/");
		actionContext.getRequest().setAttribute("logoutUrl", logoutUrl);
	}
}


であとはModuleとかでこのPluginを設定して、
認証したい、ページにAuthアノテーションを付ければ
OKです。