用Filter实现GAE上的User Authentication

编者按:好吧,就是spring security的雏形概念。

Google App Engine 提供了非常详细的User Authentication的方法,详情参见Users Java API 概述。主要包括了两种方法:

在 Java 中使用用户身份验证
如果你只有单个servlet,那么很简单,在逻辑处理前调用Google提供的API(UserService)判断当前用户登录情况就可以了。

import java.io.IOException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;
public class MyServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException {
UserService userService = UserServiceFactory.getUserService();
String thisURL = request.getRequestURI();
if (request.getUserPrincipal() != null) {
response.getWriter().println(“<p>Hello, ” +
request.getUserPrincipal().getName() +
“! You can <a href=\”” +
userService.createLogoutURL(thisURL) +
“\”>sign out</a>.</p>”);
} else {
response.getWriter().println(“<p>Please <a href=\”” +
userService.createLoginURL(thisURL) +
“\”>sign in</a>.</p>”);
}
}
}

使用 web.xml 强制登录和管理访问
如果你的应用比较复杂,不止一个servlet,Google App Engine支持标准部署描述符web.xml中的安全约束,但是不支持自定义角色,只支持login和admin两种权限。具体示例如下

<security-constraint>
<web-resource-collection>
<url-pattern>/profile/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>*</role-name>
</auth-constraint>
</security-constraint>
<security-constraint>
<web-resource-collection>
<url-pattern>/admin/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>admin</role-name>
</auth-constraint>
</security-constraint>

另外也可以通过google自己的部署描述符app.yaml来编写。需要注意的是App在Deploy的时候,如果应用中有app.yaml,则SDK会按照它的内容重新生成web.xml和appengine-web.xml并覆盖原来的文件。所以如果你要启用app.yaml,你需要把web.xml和appengine-web.xml中所有的内容按照YAML文件的格式全部写到app.yaml里面去。具体示例如下:

handlers:
- url: /profile/*
login: required
- url: /admin/*
servlet: com.example.AdminServlet
login: admin

编者按:需要吐槽的是app.yaml并不完全支持web.xml,至少我没有找到如何配置load-on-startup=1怎么写,所以我个人不推荐这种用法。
编者又按:app.yaml的支持功能比web.xml+appengine-web.xml还要多,所以如果你不巧正好遇到,还是得听Google的话用app.yaml.
编者最后按:我的应用比较复杂,所以Java中写code的办法没法适用,但是很努力尝试第二种方法均告失败,不管是web.xml还是app.yaml都不成功。这才总结出了上述的一些经验,以及遇到了下面两种都没有找到解法的问题。

  • 登录之后仍然显示Your client does not have permission to get URL
  • Error: User not in required role

以上两个问题均没有比较明确的解法,所以才有了本文的内容。


用Filter实现User Authentication
主要借鉴了Spring Security的思想(明明是写完才发现的。。。),然后借用了第一种方法中的code。具体的方法说起来很简单,就是在需要做权限判断的URL前添加Filter,如果权限判断失败,则跳转到Google Login的URL。先看一下实现的代码。
UserFilter.java中的实现。

@Override
public void doFilter(ServletRequest servletRequest,
ServletResponse servletResponse, FilterChain filter)
throws IOException, ServletException {
log.info(“———–UserFilter doFilter Begin————”);
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
String thisURL = request.getRequestURI();
if (request.getUserPrincipal() == null
|| (!userService.isUserLoggedIn())) {
response.setContentType(“text/html”);
response.sendRedirect(userService.createLoginURL(thisURL));
} else {
log.info(userService.getCurrentUser().getEmail());
filter.doFilter(servletRequest, servletResponse);
}
}

web.xml中相应的配置。

<filter>
<filter-name>userFilter</filter-name>
<filter-class>com.pamirhostel.booking.filters.UserFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>userFilter</filter-name>
<url-pattern>/admin/*</url-pattern>
</filter-mapping>

说穿了很简单,但是基于GAE的实现略有不同,总结下来的几点经验。

  • URL最好按照功能划分子目录。例如“/cron/*”是用来实现cron job的。由于GAE的backend,cron job, task queue都是通过调用URL来实现的,所以之前喜欢把所有URI都放在Root下的习惯在这里就不适用了。
  • Filter默认不是HTTP的,所以调用request,response前需要下溯造型成HTTPRequest.
  • request.getUserPrincipal只能用来判断是否当前有Google帐号登录,这是有逻辑漏洞的。例如我的应用是For Google Apps的,所以即使当前用户登录了它自己的Google Account也是没用的。所以需要添加进一步的判断userService.isUserLoggedIn。
  • 如果多组功能需要进行用户验证,直接多添加几个filter-mapping就好了。

目前该Filter很好用,还没有什么further question,有什么问题留下评论一起讨论把。

 

无觅相关文章插件,快速提升流量