Spring BlazeDS Integration之spring security(4)---自定义rememberMeServices,找到自动登陆成功切入点

来源:互联网 发布:c .net数据库编程 编辑:程序博客网 时间:2024/06/06 05:17

我先来说一个现象,比如已经有一个使用Spring BlazeDS Integration配置了spring security的一个应用,

如下图是用户已经登录成功时,进入的界面,此时login按钮是个摆设,没有任何功能大笑;logout按钮是通过flex提供的api是完成登出操作:



  • 当,用户没有点击logout按钮,直接关闭了浏览器,浏览器比如是firefox。
  • 用户再次使用firefox打开此链接的时候,
  • 呈现给用户的界面依然是登陆成功的界面。


针对于此现象,我粗浅的分析了spring security的rememberMeServices源码之后分析之后,有如下结果,

关于spring security 的 Authentication处理过程大致如下:
  • “用户成功登陆之后。当用户直接关闭浏览器,再重新打开浏览器访问该web的时候”:
  • org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter过滤器的doFilter()会被调用.
  • doFilter()方法里面,会调用当前所配置的rememberMeServices类的autoLogin(request, response)方法
  • autoLogin(request, response)方法,会检测当前浏览器所传送过来的cookie信息
  • 如果cookie信息没有过期,在cookie信息里面,找到key 为 "SPRING_SECURITY_REMEMBER_ME_COOKIE"  的value (spring security所设置)
  • 找到value之后,使用userService,重新检索用户信息,存放在org.springframework.security.core.userdetails.UserDetails事例对象中,   
  • 随后,把UserDetails,转化成Authentication,至此autoLogin(request, response)方法完成: Authentication rememberMeAuth = rememberMeServices.autoLogin(request, response);
  • 继续,doFilter()方法里面,会将rememberMeAuth 放入SecurityContext中去:   SecurityContextHolder.getContext().setAuthentication(rememberMeAuth);
  • 前台flex Gui 就是根据是否可以从SecurityContext中拿到Authentication( Authentication authentication = SecurityContextHolder.getContext().getAuthentication();) ,去判断用户是否已经登陆了。
  • 总结,“用户成功登陆之后。当用户直接关闭浏览器,再重新打开浏览器访问该web的时候”:rememberMeServices会根据cookie重新setup Authentication 到SecurityContext中。


那么session的问题来了:
  • “在用户成功登陆之后。当用户直接关闭浏览器,再重新打开浏览器访问该web的时候”:
  • sessionId是重新创建的,session也是新创建的,log:[BlazeDS]FlexSession created with id '81E8AFF21272ECFC84A29663D8EAAB03' for an Http-based client connection.
  • 这就意味着,在浏览器关闭之前的session再也找不到了,进而之前往session所放入的value也找不到了。。。
  • (注意!如果是浏览器刷新操作,一般情况,session id是不会变的)
  • 总结,“在用户成功登陆之后。当用户关闭浏览器,再重新打开浏览器访问该web的时候”,session会被新建,跟cookie无关,session里面是空的,没有任何曾经设置(曾经设置,是指,在用户登录成功时候往session set的value)过的properties。


整体看下来:
  • “在用户成功登陆之后。当用户直接关闭浏览器,再重新打开浏览器访问该web的时候”
  • Authentication 会被“复原”:因为rememberMeServices会autoLogin()
  • Session会被“新建”:因为session Id 被新建,服务器无法回到原来的session
  • 因此,此时所判定的用户已经登录的状态,但是session是空的,那么如果在程序中,想从session中取一些曾经设置过的properties时候,就会报错,取不到值



解决此问题的方案1:
  • 在用户登录成功的时候,将session id写入cookie
  • rememberMeServices调用autoLogin()的时候,读取session id,从服务器获得原始的session

解决此问题的方案2:
  • 不将session id写入 cookie
  • rememberMeServices调用autoLogin()的时候,其实已经创建了新的,空的session,
  • 此时,再往空session设置相关的properties

我个人感觉方案2比较好一点,因为:
  • “将session id写入cookie”,这个功能实现起来不难,可以在rememberMeServices.onLoginSuccess()方法里面实现。
  • 但是会引发几个问题!
    1. cookie是有过期时间的,session也有过期时间,其两者过期时间不相同怎么办?
    2. cookie在浏览器会被清除的,随之的session id也就永远消失了,原始的session再也找不到怎么办?
    3. java api里面并不提供通过session id获得session的方法。
      网上有方法,将session可以,以key-value方式存储在servletContext里面(http://www.andowson.com/posts/list/371.page),
      那么如果session 过期了,是不是还得写监听方法,将其移除?
    4. 总之方案1,会所引发的一些列问题,太麻烦了!!!

方案2的具体实现方法:
  • rememberMeServices的autoLogin()方法不允许被覆盖
  • autoLogin()内部会调用processAutoLoginCookie(),此方法允许被覆盖
  • 可以在自定义的rememberMeServices方法中覆盖processAutoLoginCookie(),在这里为空的session重新设置相关的properties,具体实现如下

package test;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.flex.samples.product.IProductDAO;import org.springframework.security.core.Authentication;import org.springframework.security.core.context.SecurityContextHolder;import org.springframework.security.core.userdetails.UserDetails;import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices;public class MyRememberMeServices extends TokenBasedRememberMeServices {private static final String CURRENT_NAME = "current_name";@Autowiredprivate UserDAO userDAO;@Overridepublic void onLoginSuccess(HttpServletRequest request,HttpServletResponse response,Authentication successfulAuthentication) {super.onLoginSuccess(request, response, successfulAuthentication);SecurityContextHolder.getContext().setAuthentication(successfulAuthentication);this.afterOnLoginSuccess(request, response, successfulAuthentication);}private void afterOnLoginSuccess(HttpServletRequest request,HttpServletResponse response,Authentication successfulAuthentication) {HttpSession session = request.getSession();System.out.println("login success-----------------session id = "+ session.getId());String userName = successfulAuthentication.getName();//登陆成功时,为新创建出来的空session设置properties, session.setAttribute(CURRENT_NAME, userDAO.findByUserName(userName)); }protected UserDetails processAutoLoginCookie(String[] cookieTokens,HttpServletRequest request, HttpServletResponse response) {UserDetails userDetails = super.processAutoLoginCookie(cookieTokens,request, response);this.afterProcessAutoLoginCookie(userDetails, request, response);return userDetails;}private void afterProcessAutoLoginCookie(UserDetails userDetails,HttpServletRequest request, HttpServletResponse response) {HttpSession session = request.getSession(); //之前没有session,</span>在这里会根据新的session id创建新的sessionSystem.out.println("auto login success-----------------session id = "+ session.getId());String userName = userDetails.getUsername();// 当用户已经登陆,直接关闭浏览器,再次又打开浏览器,访问该web应用时候,所走的是: “自动”登陆的流程// “自动”登陆成功时,为新创建出来的空session设置properties, session.setAttribute(CURRENT_NAME, userDAO.findByUserName(userName)); }}




在这里,我往session里面设置了数据库里面一整条的user记录,目的是让session里可以存储,user的id主键 
我们要知道,Authentication对象里面是没有id主键的!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!


security-config.xml配置文件没有什么变化:

<?xml version="1.0" encoding="UTF-8"?><beans:beans xmlns="http://www.springframework.org/schema/security"xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd                        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd"><http entry-point-ref="entryPoint"><anonymous enabled="false" /><form-login login-page="/login.jsp"authentication-success-handler-ref="simpleLoginSuccessHandler" /><remember-me key="testdrive" services-ref="rememberMeServices" /></http><beans:bean id="rememberMeServices" class="test.MyRememberMeServices"><beans:property name="key" value="testdrive" /><beans:property name="alwaysRemember" value="true" /></beans:bean><beans:bean id="entryPoint"class="org.springframework.flex.security3.FlexAuthenticationEntryPoint" /><beans:bean id="simpleLoginSuccessHandler" class="test.SimpleLoginSuccessHandler"><beans:property name="defaultTargetUrl" value="/secured/secured.html"></beans:property><beans:property name="forwardToDestination" value="false"></beans:property></beans:bean><authentication-manager><authentication-provider><user-service><user name="john" password="john" authorities="ROLE_USER" /><user name="admin" password="admin" authorities="ROLE_USER, ROLE_ADMIN" /><user name="guest" password="guest" authorities="ROLE_GUEST" /></user-service></authentication-provider></authentication-manager></beans:beans>







ps:在此测试过程中,为什么始终使用同一个浏览器?原因是cookie,不同浏览器存储的cookie文件位置不同,比如fireFox的cookie路径:

  • XP       C:\Documents and Settings\用户名\Application Data\Mozilla\Firefox\Profiles\xxxxxxxx.default\
  • Win7   C:\Users\用户名\AppData\Local\Mozilla\Firefox\Profiles
  • 但是普通方式察看,都看不见该文件夹!












0 0
原创粉丝点击