appengineのfederatedLoginの機能を使ったアプリケーションの場合、開発環境のログイン機能ではcom.google.appengine.api.users.User
からfederatedIdentityの値などが取得できずに不便です。実行時のUserService#isLoggedIn()
, UserService#isAdmin()
, UserService#getAuthDomain()
等はApiProxy.Environment
を自身の実装に置き換えるだけだったので簡単でしたが、federatedLoginの場合はこれらの値を触るだけでは不十分です。
ではいつものようにApiProxy#setDelegate(ApiProxy.Delegte)
でいじってやるか!というワケにもいきません。UserService#getCurrentUser()
等はRPCされないためです(以前、本家MLで「UserServiceはコストが低い」という話がありましたね。その理由はRPCしない=コストが低い、ということです。このブログでもいつだったかに書いた記憶があります。)。
んじゃどーするのか?といいますと、ApiProxy#getCurrentEnvironment()
で取得できるApiProxy#Environment<ApiProxy.Delegate>
のMap<String, Object>#getAttributes()
メソッドが返す値を操作してやるのです。
ApiProxy#Environment<ApiProxy.Delegate>
の#Map<String, Object>#getAttributes()
に以下の要素を設定してやることで、federatedLoginの機能が開発環境でも有効になります。
- com.google.appengine.api.users.UserService.is_federated_user
- com.google.appengine.api.users.UserService.user_id_key
- com.google.appengine.api.users.UserService.federated_identity
- com.google.appengine.api.users.UserService.federated_authority
com.google.appengine.api.users.UserService.is_federated_user
についてはBoolean.TRUE
を設定しておく必要があります。他はすべて文字列を設定すればよく、それぞれの値はfederatedLogin機能を使うときのアレです。
単体テスト環境では、ApiProxy#Environment<ApiProxy.Delegate>
のMap<String, Object>#getAttributes()
が返す値に上記を追加し、ApiProxy#setEnvironmentForCurrentThred()
してやれば良いです。
開発サーバ環境でも同じことをすれば良いのですが、私が使っている「専用のFilterとして実装する」という方法のサンプルコードを掲載しておきます。
「独自のApiProxy#Environment<ApiProxy.Delegate>
を常に使用する」「createLoginURL()すると、専用のフォームを表示し、そこからのPostの内容に従ってfederatedLoginUser情報を設定する」「web.xml内でinit-paramにfederatedLoginUser情報があれば、それを読み込んで起動時からログイン状態にしておく」等をやっています。
単体テスト環境では、下記のサンプルコードの内部staticクラスとして定義されているFederatedLoginEnvironment
をインスタンス化して、loginメソッドなどでfederatedLogin状態を作り出し、ApiProxy#setEnvironmentForCurrentThred()
すれば良いですね。
import java.io.IOException; | |
import java.io.PrintWriter; | |
import java.util.Iterator; | |
import java.util.Map; | |
import java.util.Map.Entry; | |
import java.util.logging.Logger; | |
import javax.servlet.Filter; | |
import javax.servlet.FilterChain; | |
import javax.servlet.FilterConfig; | |
import javax.servlet.ServletException; | |
import javax.servlet.ServletRequest; | |
import javax.servlet.ServletResponse; | |
import javax.servlet.http.HttpServletRequest; | |
import javax.servlet.http.HttpServletResponse; | |
import org.apache.commons.lang.StringUtils; | |
import com.google.appengine.repackaged.com.google.common.collect.Maps; | |
import com.google.apphosting.api.ApiProxy; | |
import com.google.apphosting.api.ApiProxy.Environment; | |
/** | |
* 開発サーバ環境でFederatedLoginの機能を使用するための{@link Filter}。 | |
* @author shin1ogawa | |
*/ | |
public class FederatedLoginFilter implements Filter { | |
static final Logger logger = Logger.getLogger(FederatedLoginFilter.class.getName()); | |
static final String USER_ID_KEY = "com.google.appengine.api.users.UserService.user_id_key"; | |
static final String FEDERATED_IDENTITY_KEY = | |
"com.google.appengine.api.users.UserService.federated_identity"; | |
static final String FEDERATED_AUTHORITY_KEY = | |
"com.google.appengine.api.users.UserService.federated_authority"; | |
static final String IS_FEDERATED_USER_KEY = | |
"com.google.appengine.api.users.UserService.is_federated_user"; | |
static FederatedLoginEnvironment environment; | |
@Override | |
public void init(FilterConfig filterConfig) { | |
if (StringUtils.equalsIgnoreCase(runtimeEnv, "Production")) { | |
return; | |
} | |
environment = new FederatedLoginEnvironment(ApiProxy.getCurrentEnvironment()); | |
String email = filterConfig.getInitParameter("email"); | |
String authDomain = filterConfig.getInitParameter("auth_domain"); | |
if (StringUtils.isEmpty(email) || StringUtils.isEmpty(authDomain)) { | |
return; | |
} | |
environment.federatedLogin(email, authDomain, filterConfig.getInitParameter("user_id_key"), | |
filterConfig.getInitParameter("federated_identity"), filterConfig | |
.getInitParameter("federated_authority")); | |
} | |
final String runtimeEnv = System.getProperty("com.google.appengine.runtime.environment"); | |
@Override | |
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) | |
throws IOException, ServletException { | |
if (StringUtils.equalsIgnoreCase(runtimeEnv, "Production")) { | |
chain.doFilter(request, response); | |
return; | |
} | |
HttpServletRequest httpServletRequest = (HttpServletRequest) request; | |
String servletPath = httpServletRequest.getServletPath(); | |
environment.env = ApiProxy.getCurrentEnvironment(); | |
ApiProxy.setEnvironmentForCurrentThread(environment); | |
if (servletPath.equals("/_ah/login")) { | |
login(request, response, httpServletRequest); | |
return; | |
} | |
if (servletPath.equals("/_ah/logout")) { | |
environment.logout(); | |
((HttpServletResponse) response).sendRedirect(httpServletRequest | |
.getParameter("continue")); | |
return; | |
} | |
chain.doFilter(request, response); | |
} | |
@Override | |
public void destroy() { | |
} | |
void login(ServletRequest request, ServletResponse response, | |
HttpServletRequest httpServletRequest) throws IOException { | |
HttpServletResponse httpServletResponse = (HttpServletResponse) response; | |
String email = request.getParameter("email"); | |
String authDomain = request.getParameter("auth_domain"); | |
if (StringUtils.isEmpty(email) || StringUtils.isEmpty(authDomain)) { | |
responseLogin(httpServletRequest, httpServletResponse); | |
return; | |
} | |
environment.federatedLogin(email, authDomain, request.getParameter("user_id_key"), request | |
.getParameter("federated_identity"), request.getParameter("federated_authority")); | |
httpServletResponse.sendRedirect(httpServletRequest.getParameter("continue")); | |
return; | |
} | |
void responseLogin(HttpServletRequest request, HttpServletResponse response) throws IOException { | |
response.setContentType("text/html"); | |
response.setCharacterEncoding("utf-8"); | |
PrintWriter w = response.getWriter(); | |
w.print("<html><head><title>"); | |
w.print(this.getClass().getSimpleName()); | |
w.println("</title></head>"); | |
w.print("<body><h1>"); | |
w.print(this.getClass().getSimpleName()); | |
w.println("</h1><div>"); | |
w.println("<form id=\"loginform\" action=\"/_ah/login\">"); | |
w.print("<p><input type=\"hidden\" name=\"continue\" value=\""); | |
w.print(request.getParameter("continue")); | |
w.println("\" /></p>"); | |
w | |
.print("<p><input type=\"text\" name=\"email\" value=\"test@localhost.com\" size=\"75\"/></p>"); | |
w | |
.print("<p><input type=\"text\" name=\"auth_domain\" value=\"localhost.com\" size=\"75\"/></p>"); | |
w | |
.print("<p><input type=\"text\" name=\"federated_identity\" value=\"http://localhost.com/openid?id=1000000/\"size=\"75\"/></p>"); | |
w | |
.print("<p><input type=\"hidden\" name=\"user_id_key\" value=\"2000000\"size=\"75\"/></p>"); | |
w.print("<p><input type=\"hidden\" name=\"federated_authority\" size=\"75\"/></p>"); | |
w.print("<p><input type=\"submit\" /></p>"); | |
w.println("</form></div>"); | |
w.println("</body></html>"); | |
response.flushBuffer(); | |
} | |
/** | |
* 開発環境用でFederatedLoginを使用する際の{@link ApiProxy.Environment}. | |
* @author shin1ogawa | |
*/ | |
public static class FederatedLoginEnvironment implements ApiProxy.Environment { | |
ApiProxy.Environment env; | |
String email; | |
String authDomain; | |
boolean isAdmin; | |
boolean isLoggedIn; | |
final Map<String, Object> attributes = Maps.newHashMap(); | |
/** | |
* the constructor. | |
* @param env | |
* @category constructor | |
*/ | |
public FederatedLoginEnvironment(Environment env) { | |
this.env = env; | |
email = env.getEmail(); | |
authDomain = env.getAuthDomain(); | |
isAdmin = env.isAdmin(); | |
isLoggedIn = env.isLoggedIn(); | |
} | |
/** | |
* logout状態にする。 | |
*/ | |
public void logout() { | |
isLoggedIn = false; | |
this.email = null; | |
this.authDomain = null; | |
attributes.remove(IS_FEDERATED_USER_KEY); | |
attributes.remove(USER_ID_KEY); | |
attributes.remove(FEDERATED_IDENTITY_KEY); | |
attributes.remove(FEDERATED_AUTHORITY_KEY); | |
} | |
/** | |
* FederatedLoginする。 | |
* @param email | |
* @param authDomain | |
* @param userId | |
* @param federatedIdentity | |
* @param federatedAuthority | |
*/ | |
public void federatedLogin(String email, String authDomain, String userId, | |
String federatedIdentity, String federatedAuthority) { | |
isLoggedIn = true; | |
this.email = email; | |
this.authDomain = authDomain; | |
attributes.put(IS_FEDERATED_USER_KEY, Boolean.TRUE); | |
attributes.put(USER_ID_KEY, userId); | |
attributes.put(FEDERATED_IDENTITY_KEY, federatedIdentity); | |
attributes.put(FEDERATED_AUTHORITY_KEY, federatedAuthority); | |
} | |
@Override | |
public String getAppId() { | |
return env.getAppId(); | |
} | |
@Override | |
public Map<String, Object> getAttributes() { | |
Map<String, Object> attributes = env.getAttributes(); | |
Iterator<Entry<String, Object>> i = this.attributes.entrySet().iterator(); | |
while (i.hasNext()) { | |
Entry<String, Object> next = i.next(); | |
Object value = next.getValue(); | |
if (value != null) { | |
attributes.put(next.getKey(), value); | |
} | |
} | |
return attributes; | |
} | |
@Override | |
public String getAuthDomain() { | |
return authDomain; | |
} | |
@Override | |
public String getEmail() { | |
return email; | |
} | |
@SuppressWarnings("deprecation") | |
@Override | |
public String getRequestNamespace() { | |
return env.getRequestNamespace(); | |
} | |
@Override | |
public String getVersionId() { | |
return env.getVersionId(); | |
} | |
@Override | |
public boolean isAdmin() { | |
return isAdmin; | |
} | |
@Override | |
public boolean isLoggedIn() { | |
return isLoggedIn; | |
} | |
} | |
} |
0 件のコメント:
コメントを投稿