spring security3.0控制多个用户账号同时登录和管理员踢出用户

来源:互联网 发布:淘宝宝贝如何优化 编辑:程序博客网 时间:2024/05/15 10:45

    声明一下,这篇文章不是基于acegi  spring security2.0写的,  我发现很多文章都是基于老版本写的,  并不适用最新版。

下面跟大家分享一下在spring security3.0里如何正宗的做法达到控制多个账号请求的经验。

 

步骤1

[xhtml] view
plaincopy
  1. <http auto-config="true" >  
  2.   
  3. <!--session-fixation-protection="none" 防止伪造sessionid攻击. 用户登录成功后会销毁用户当前的session.创建新的session,并把用户信息复制到新session中.-->  
  4.     <session-management invalid-session-url="/common/login.jsp?invalid-session=true" >  
  5.      <concurrency-control error-if-maximum-exceeded="true" max-sessions="1" />  
  6.  
      </
    session>
  7.   
  8. </http>  

下面只贴出关键部分, 为了不影响阅读。

 

注意: 不需要配置 SessionRegistry 等bean( 假设你其他地方不用到的话, 如果用到需要在

<concurrency-control session-registry-ref="sessionRegistry" error-if-maximum-exceeded="true" max-sessions="1" />

加上一个属性

 

在做某个管理员踢出一个账号的时候, SessionRegistry 这个bean是需要用到的。 写法如下:

[java] view
plaincopy
  1. @RequestMapping(value = "logout.html")  
  2. public String logout(String sessionId, String sessionRegistryId, String name, HttpServletRequest request, ModelMap model){    
  3.     List<Object> userList=sessionRegistry.getAllPrincipals();  
  4.     for(int i=0; i<userList.size(); i++){  
  5.         User userTemp=(User) userList.get(i);      
  6.         if(userTemp.getName().equals(name)){          
  7.             List<SessionInformation> sessionInformationList = sessionRegistry.getAllSessions(userTemp, false);  
  8.             if (sessionInformationList!=null) {   
  9.                 for (int j=0; j<sessionInformationList.size(); j++) {  
  10.                     sessionInformationList.get(j).expireNow();  
  11.                     sessionRegistry.removeSessionInformation(sessionInformationList.get(j).getSessionId());  
  12.                     String remark=userTemp.getName()+"被管理员"+SecurityHolder.getUsername()+"踢出";  
  13.                     loginLogService.logoutLog(userTemp, sessionId, remark);     //记录注销日志和减少在线用户1个  
  14.                     logger.info(userTemp.getId()+"  "+userTemp.getName()+"用户会话销毁," + remark);  
  15.                 }  
  16.             }  
  17.         }  
  18.     }  
  19.     return "auth/onlineUser/onlineUserList.html";  
  20. }  

 

 

有时候按文档和网上配置出来是很华丽, 可事实有时候就是没有如期运行。

我打开火狐  360浏览器, 还是等两个账号同时登录。

无奈之下把源码下下载剖析(常干的事儿, 喜欢捣腾这些东西)

 

判断重复的类是ConcurrentSessionControlStrategy.java下的

checkAuthenticationAllowed这个函数的

[java] view
plaincopy
  1. final List<SessionInformation> sessions = sessionRegistry.getAllSessions(authentication.getPrincipal(), false);  
  2.   
  3. int sessionCount = sessions.size();  

最重要的一句话是:

sessionInformationList.get(j).expireNow();
这句强制T出了用户, (设置为过期)

如果想彻底删除, 加上

sessionRegistry.removeSessionInformation(sessionInformationList.get(j).getSessionId());

即可,

这样使用getAllPrincipals  则获取不到被T出的用户了,  其实原理不是直接删除User对象, 只结束了它的sessionId,

因为这个User可能不止对应着1个sessionId

 

 

我发现, 无论我怎么配置,  sessionCount老是烦人的 0。    即使我手动配置了ConcurrentSessionControlStrategy这个bean也没用(默认会自己调的)

 

无奈中想自己写一个自定义的计数器控制, 但细想它这东西不至于这个小问题都出这么大的漏洞吧?

 

现在的问题是:

如何让 int sessionCount = sessions.size();  这句在第二个账号登陆的时候不为0。

 

于是我进入了sessionRegistry.getAllSessions(authentication.getPrincipal(), false);  这个函数。

 

也就是SessionRegistryImpl.java

 

    public List<SessionInformation> getAllSessions(Object principal, boolean includeExpiredSessions) {
        final Set<String> sessionsUsedByPrincipal = principals.get(principal);

 

这个函数是通过在一个HashMap里拿到key value的。

 

而principals的声明这样写。

 

private final Map<Object,Set<String>> principals = Collections.synchronizedMap(new HashMap<Object,Set<String>>());

 

 

现在的问题变为了:

如何让两个principal  ,  也就是User, 也就是

public UserDetails loadUserByUsername(String username)

 

两次登陆的时候返回的是同一个对象。

 

那么如何做到两次在不同浏览器登陆的时候返回的是同一个User?

 

答案是java的基础, equal   hashcode方法重写。

 

在User对象里添加以下方法:

 

[java] view
plaincopy
  1. public boolean equals(Object object) {  
  2.     if (object instanceof BaseObject) {  
  3.         if (this.id.equals(((BaseObject) object).getId()))  
  4.             return true;  
  5.     }  
  6.     return false;  
  7. }  
  8.   
  9.    public int hashCode(){       
  10.        return this.id.hashCode();       
  11.    }     

 

 涉及这方面的基础请参考   http://blog.csdn.net/willielee/archive/2010/08/11/5804463.aspx

 

我就不浪费CSDN的硬盘空间了, 不过还是得贴出最后一句话:

HashMap的key判断key是否相等也是从hashcode和equals是否相等判断

 

 从上面可以看出, 我们之前登陆的用户是存在    Map<Object,Set<String>> principals  的。

这个存储结构是,  一个User  对应多个Set集合的sessionId。

所以要判断用户是否已存在登陆的了, 当然要重写这2个方法。

文章出处   http://blog.csdn.net/sinlff/article/details/5892531

返回
  
0 0
原创粉丝点击