`
aokunsang
  • 浏览: 816097 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

SpringSecurity3.1.2控制一个账户同时只能登录一次

阅读更多

网上看了很多资料,发现多多少少都有一些不足(至少我在使用的时候没成功),后来经过探索研究,得到解决方案。

 

具体SpringSecurity3怎么配置参考SpringSecurity3.1实践,这里只讲如何配置可以控制一个账户同时只能登录一次的配置实现。

 

网上很多配置是这样的,在<http>标签中加入concurrency-control配置,设置max-sessions=1。

<session-management invalid-session-url="/timeout">
  <concurrency-control max-sessions="1" error-if-maximum-exceeded="true"/>
</session-management>

 但是经测试一直没成功,经过一番查询原来是UsernamePasswordAuthenticationFilter中的一个默认属性sessionStrategy导致的。

首先我们在spring-security.xml中添加<debug/>,可以看到经过的过滤器如下:

Security filter chain: [
  ConcurrentSessionFilter --- (主要)并发控制过滤器,当配置<concurrency-control />时,会自动注册
  SecurityContextPersistenceFilter  --- 主要是持久化SecurityContext实例,也就是SpringSecurity的上下文。也就是SecurityContextHolder.getContext()这个东西,可以得到Authentication。
  LogoutFilter   ---  注销过滤器
  PmcUsernamePasswordAuthenticationFilter  --- (主要)登录过滤器,也就是配置文件中的<form-login/>配置、(本文主要问题就在这里)
  RequestCacheAwareFilter                 ---- 主要作用为:用户登录成功后,恢复被打断的请求(这些请求是保存在Cache中的)。这里被打断的请求只有出现AuthenticationException、AccessDeniedException两类异常时的请求。
  SecurityContextHolderAwareRequestFilter  ---- 不太了解
  AnonymousAuthenticationFilter        ------  匿名登录过滤器
  SessionManagementFilter              ----  session管理过滤器
  ExceptionTranslationFilter         ---- 异常处理过滤器(该过滤器只过滤下面俩拦截器)[处理的异常都是继承RuntimeException,并且它只处理AuthenticationException(认证异常)和AccessDeniedException(访问拒绝异常)]
  PmcFilterSecurityInterceptor       ------  自定义拦截器
  FilterSecurityInterceptor
]

 那么先来看UsernamePasswordAuthenticationFilter,它实际是执行其父类AbstractAuthenticationProcessingFilter的doFilter()方法,看部分源码:

try {
	    //子类继承该方法进行认证后返回Authentication
            authResult = attemptAuthentication(request, response);
            if (authResult == null) {
                // return immediately as subclass has indicated that it hasn't completed authentication
                return;
            }
	    //判断session是否过期、把当前用户放入session(这句是关键)
            sessionStrategy.onAuthentication(authResult, request, response);
        } catch(InternalAuthenticationServiceException failed) {
            logger.error("An internal error occurred while trying to authenticate the user.", failed);
            unsuccessfulAuthentication(request, response, failed);
            return;
        }

 看sessionStrategy的默认值是什么?

private SessionAuthenticationStrategy sessionStrategy = new NullAuthenticatedSessionStrategy();

 然后我们查询NullAuthenticatedSessionStrategy类的onAuthentication()方法竟然为空方法[问题就出现在这里]。那么为了解决这个问题,我们需要向UsernamePasswordAuthenticationFilter中注入类ConcurrentSessionControlStrategy。

这里需要说明下,注入的属性name名称为sessionAuthenticationStrategy,因为它的setter方法这么写的:

public void setSessionAuthenticationStrategy(SessionAuthenticationStrategy sessionStrategy) {
   this.sessionStrategy = sessionStrategy;
}

 这样,我们的配置文件需要这样配置(只贴出部分代码)[具体参考SpringSecurity3.1实践那篇博客]:

  <http entry-point-ref="loginAuthenticationEntryPoint">
  	<logout 
		delete-cookies="JSESSIONID"
  		logout-success-url="/" 
  		invalidate-session="true"/>
	
	<access-denied-handler error-page="/common/view/accessDenied.jsp"/>
		
	  	<session-management invalid-session-url="/timeout" session-authentication-strategy-ref="sas"/>
  	
  	<custom-filter ref="pmcLoginFilter" position="FORM_LOGIN_FILTER"/>
  	<custom-filter ref="concurrencyFilter" position="CONCURRENT_SESSION_FILTER"/>
  	<custom-filter ref="pmcSecurityFilter" before="FILTER_SECURITY_INTERCEPTOR"/>
  </http>


  <!-- 登录过滤器(相当于<form-login/>) -->
  <beans:bean id="pmcLoginFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
  	<beans:property name="authenticationManager" ref="authManager"></beans:property>
  	<beans:property name="authenticationFailureHandler" ref="failureHandler"></beans:property>
  	<beans:property name="authenticationSuccessHandler" ref="successHandler"></beans:property>
  	<beans:property name="sessionAuthenticationStrategy" ref="sas"></beans:property>
  </beans:bean>

  <!-- ConcurrentSessionFilter过滤器配置(主要设置账户session过期路径) -->
  <beans:bean id="concurrencyFilter" class="org.springframework.security.web.session.ConcurrentSessionFilter">
  	<beans:property name="expiredUrl" value="/timeout"></beans:property>
  	<beans:property name="sessionRegistry" ref="sessionRegistry"></beans:property>
  </beans:bean>

<!-- 未验证用户的登录入口 -->
  <beans:bean id="loginAuthenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
  	<beans:constructor-arg name="loginFormUrl" value="/"></beans:constructor-arg>
  </beans:bean>

  <!-- 注入到UsernamePasswordAuthenticationFilter中,否则默认使用的是NullAuthenticatedSessionStrategy,则获取不到登录用户数
  error-if-maximum-exceeded:若当前maximumSessions为1,当设置为true表示同一账户登录会抛出SessionAuthenticationException异常,异常信息为:Maximum sessions of {0} for this principal exceeded;
  												                 当设置为false时,不会报错,则会让同一账户最先认证的session过期。
     具体参考:ConcurrentSessionControlStrategy:onAuthentication()
  -->
  <beans:bean id="sas" class="org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy">
  	<beans:property name="maximumSessions" value="1"></beans:property>
  	<beans:property name="exceptionIfMaximumExceeded" value="true"></beans:property>
  	<beans:constructor-arg name="sessionRegistry" ref="sessionRegistry"></beans:constructor-arg>
  </beans:bean>
  <beans:bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl"></beans:bean>

 这样设置后,即可实现一个账户同时只能登录一次,但是有个小瑕疵。

如:A用户用账号admin登录系统,B用户在别处也用账号admin登录系统,这时会出现两种情况:

  1、设置exceptionIfMaximumExceeded=true,会报异常:SessionAuthenticationException("Maximum sessions of {0} for this principal exceeded");

  2、设置exceptionIfMaximumExceeded=false,那么B用户会把A用户挤掉,A用户再点击页面,则会跳转到

ConcurrentSessionFilter的expiredUrl路径。

     最理想的解决办法是第一种,但是不能让其报异常,在登录失败的handler中扑捉该异常,跳转到登录页面提示<该账号已经登录>。

     但是这里还会有点问题,当用户关闭浏览器或者直接关机等非正常退出时候将登录不进去。目前我的解决方案是:比对IP地址,判断如果是本机用户可以多次登录系统,然后使第一次登录的账号无效。大致思路关注2点:

1、把ConcurrentSessionControlStrategy源码copy一份,更改部分业务逻辑(主要拿客户端ip做文章);

2、重载SessionRegistryImpl,让其调用registerNewSession()方法时候保存ip地址。

详细的解决方案以及配置文件参考附件。

分享到:
评论
7 楼 syxfqw7 2016-08-05  
完全正确,补充一下

<beans:bean id="pmcLoginFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter"> 
    <beans:property name="authenticationManager" ref="zhfwAuthenticationManager"></beans:property> 
    <!--beans:property name="authenticationFailureHandler" ref="failureHandler"></beans:property> 
    <beans:property name="authenticationSuccessHandler" ref="successHandler"></beans:property--> 
    <beans:property name="sessionAuthenticationStrategy" ref="sas"></beans:property> 
</beans:bean>
可以这么写,handler不用可以注释掉
6 楼 MrwenQ 2014-09-12  
好的 谢谢
5 楼 aokunsang 2014-09-11  
MrwenQ 写道
楼主你好,我想问下 你这个同个IP多次登录是怎么做的呢?

主要做2点:
1、把ConcurrentSessionControlStrategy源码copy一份,更改部分业务逻辑(主要拿客户端ip做文章);
2、重载SessionRegistryImpl,让其调用registerNewSession()方法时候保存ip地址。
我把更改过的三个类和配置文件上传上去,你参考下吧。
4 楼 MrwenQ 2014-09-11  
楼主你好,我想问下 你这个同个IP多次登录是怎么做的呢?
3 楼 aokunsang 2014-08-15  
c.hle2008 写道
楼主,我按你弄的成功了,不过我的<beans:property name="exceptionIfMaximumExceeded" value="true"/>是这个配置,这样确实实现了只能一个用户登录,通过以下配置,也能正常退出到指定的页面,<logout logout-url="/user/logout.do" logout-success-url="/login.jsp" invalidate-session="true"/>,但是,如果再登录时,就登录不上了,提示“当前用户已经登录”,如何解决。

你是不是没有配置退出的地址?退出按钮的连接地址配置为:/user/logout.do应该就能安全退出,下次还能登陆。   如果是强制关闭页面的话,就有点棘手了。
2 楼 c.hle2008 2014-08-14  
楼主,我按你弄的成功了,不过我的<beans:property name="exceptionIfMaximumExceeded" value="true"/>是这个配置,这样确实实现了只能一个用户登录,通过以下配置,也能正常退出到指定的页面,<logout logout-url="/user/logout.do" logout-success-url="/login.jsp" invalidate-session="true"/>,但是,如果再登录时,就登录不上了,提示“当前用户已经登录”,如何解决。
1 楼 423418271 2014-01-17  
各种错误啊

相关推荐

    spring-3.1.2包含jar包文件

    Spring 3.1.2 版本是该框架的一个重要里程碑,它包含了对先前版本的改进和一些新特性。在这个版本中,Spring 提供了更加完善的依赖注入(DI)和面向切面编程(AOP)支持,同时增强了对Java Persistence API (JPA) 和...

    spring3.1.2

    Spring 3.1.2 是该框架的一个特定版本,它在 Spring 3.x 系列中带来了诸多改进和新特性,旨在提升开发效率和应用性能。 首先,Spring 3.1 引入了核心容器的增强,特别是依赖注入(Dependency Injection,DI)机制的...

    spring-framework-3.1.2.RELEASE jar包

    Spring 3.1.2.RELEASE是该框架的一个重要版本,虽然它已经相对较老,但仍然在某些项目中被广泛使用,尤其是那些无法立即升级到最新版本的系统。这个版本的发布在当时引入了许多改进和新特性,旨在提升性能、简化开发...

    springsecurity所有jar包

    Spring Security 是一个强大的和高度可定制的身份验证和访问控制框架,用于Java应用程序。它为Web应用和企业级应用提供安全解决方案,包括用户认证、权限控制、会话管理等多个方面。在Spring Security 3.1.2版本中,...

    spring-security-oauth2与spring-security-web 3.1.2 源码

    4. **CSRF Protection**: 为了防止跨站请求伪造(Cross-Site Request Forgery),`spring-security-web`包含了内置的CSRF防护机制,它要求每个修改状态的请求携带一个CSRF令牌。 接下来,`spring-security-oauth2`...

    spring3.1.2源码包

    总结,Spring 3.1.2源码的学习是一次深入理解Java企业级开发的宝贵旅程,通过细致的研究,开发者可以更高效地利用Spring框架,提升软件开发的质量和效率。同时,对源码的探索也是提升自身编程技能的重要途径,有助于...

    Spring3.1.2Jar包

    Spring3.1.2版本是该框架的一个稳定版本,提供了许多改进和新功能,旨在提高开发效率和应用程序的可维护性。下面将详细讨论Spring3.1.2中的关键知识点。 1. **依赖注入(Dependency Injection, DI)**:Spring的核心...

    spring-framework-3.1.2.RELEASE.zip

    总结,Spring Framework 3.1.2是一个强大且成熟的Java开发框架,其丰富的功能和灵活的设计理念,使得它在各种规模的项目中都有广泛的应用。通过理解和掌握这些知识点,开发者可以更好地利用Spring提升软件开发的质量...

    spring3.1.2jar包及包详解

    首先,Spring 3.1.2是一个稳定版本,它包含了众多改进和优化,以提升性能和兼容性。这个版本主要关注于增强核心功能,如AOP(面向切面编程)和IoC(控制反转),同时也对数据访问层进行了增强,比如JDBC和ORM集成。 ...

    Spring_framework_3.1.2_API CHM格式

    Spring_framework_3.1.2_API.CHM格式,带索引和全文搜索等方便携带和查询。 从之前发布其他chm文件下载用户的反映看,有不少朋友反映下载后打开无法显示,这一般不是chm文件的问题,这里统一说明一下解决办法: ...

    spring-security-web源码所需jar包

    7. **spring-aop-3.1.2.RELEASE.jar**:Spring的面向切面编程模块,Spring Security利用AOP实现对方法和类的细粒度安全控制。 8. **spring-security-web-3.1.2.RELEASE.jar**:我们的主角,包含了处理Web安全的类和...

    spring-security

    Spring Security 是一个强大且高度可配置的Java安全框架,主要用于保护基于Spring的应用程序。在Spring Security 3.1.2版本中,该框架提供了一套完整的解决方案,涵盖了从身份验证、授权到访问控制等多方面的安全...

    spring 3.1.2+struts 2.3.2 + mybatis 3.1.1框架搭建

    本教程将详细解析如何使用Spring 3.1.2、Struts 2.3.2和MyBatis 3.1.1这三个流行框架搭建一个完整的开发环境。这三大框架分别负责不同层面的任务:Spring作为整体的应用管理容器,Struts作为MVC(模型-视图-控制器)...

    spring3.1.2 API chm英文版

    Spring 3.1.2是该框架的一个稳定版本,提供了许多改进和新特性。本API文档以CHM(Microsoft Compiled HTML Help)格式提供,这种格式允许用户离线浏览,方便开发者在没有互联网连接的情况下查阅。 Spring框架的核心...

    spring-3.1.2、cxf-3.1.3、mybaties、mysql 整合实现webservice需要的完整jar文件

    整合这些技术能创建一个高效、灵活的Web服务系统,其中Spring负责业务逻辑和控制流程,CXF处理Web服务通信,MyBatis处理数据库操作,而MySQL作为可靠的后台数据库存储数据。这样的架构使得开发和维护更加容易,并且...

    Spring3.1 security +Spring MVC+hibernate集成

    NULL 博文链接:https://xly1981.iteye.com/blog/1897539

    springMVC+Mybatis3.1+spring3.1.2包含事务,注解,拦截器

    springMVC+Mybatis3.1+spring3.1.2(包含事务详解,代码诠释,含数据库文件) 展示了增、删、改、查、注解、拦截器、spring事务配置(亲测成功),sql文 件!!赶紧来下载给好评!!! web project 完全可以跑起来!...

Global site tag (gtag.js) - Google Analytics