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

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

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


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

  • 当,用户没有点击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";
	@Autowired
	private UserDAO userDAO;

	@Override
	public 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,在这里会根据新的session id创建新的session
		System.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配置文件没有什么变化:




	
		
		
		

	

	
		
		
	

	

	
		
		
	

	
		
			
				
				
				
			
		
	








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
  • 但是普通方式察看,都看不见该文件夹!












你可能感兴趣的:(flex)