Spring Security实现禁止用户重复登陆的配置原理

这篇文章主要介绍了Spring Security实现禁止用户重复登陆的配置原理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

系统使用了Spring Security做权限管理,现在对于系统的用户,需要改动配置,实现无法多地登陆。

一、SpringMVC项目,配置如下:

首先在修改Security相关的XML,我这里是spring-security.xml,修改UsernamePasswordAuthenticationFilter相关Bean的构造配置

加入

<property name="sessionAuthenticationStrategy" ref="sas" />

新增sas的Bean及其相关配置

<bean id="sas" class="org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy">
    <constructor-arg>
      <list>
        <bean class="org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy">
          <constructor-arg ref="sessionRegistry"/>
          <!-- 这里是配置session数量,此处为1,表示同一个用户同时只会有一个session在线 -->
          <property name="maximumSessions" value="1" />
          <property name="exceptionIfMaximumExceeded" value="false" />
        </bean>
        <bean class="org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy">
        </bean>
        <bean class="org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy">
          <constructor-arg ref="sessionRegistry"/>
        </bean>
      </list>
    </constructor-arg>
  </bean>

  <bean id="sessionRegistry"
        class="org.springframework.security.core.session.SessionRegistryImpl" />

加入ConcurrentSessionFilter相关Bean配置

<bean id="concurrencyFilter"
        class="org.springframework.security.web.session.ConcurrentSessionFilter">
    <constructor-arg name="sessionRegistry" ref="sessionRegistry" />
    <constructor-arg name="sessionInformationExpiredStrategy" ref="redirectSessionInformationExpiredStrategy" />
  </bean>

  <bean id="redirectSessionInformationExpiredStrategy"
        class="org.springframework.security.web.session.SimpleRedirectSessionInformationExpiredStrategy">
    <constructor-arg name="invalidSessionUrl" value="/login.html" />
  </bean>

二、SpringBoot项目

三、Bean配置说明

  • SessionAuthenticationStrategy:该接口中存在onAuthentication方法用于对新登录用户进行session相关的校验。
  • 查看UsernamePasswordAuthenticationFilter及其父类代码,可以发现在doFilter中存在sessionStrategy.onAuthentication(authResult, request, response);方法
  • 但UsernamePasswordAuthenticationFilter中的sessionStrategy对象默认为NullAuthenticatedSessionStrategy,即不对session进行相关验证。
  • 如本文配置,建立id为sas的CompositeSessionAuthenticationStrategy的Bean对象。
  • CompositeSessionAuthenticationStrategy可以理解为一个托管类,托管所有实现SessionAuthenticationStrategy接口的对象,用来批量托管执行onAuthentication函数
  • 这里CompositeSessionAuthenticationStrategy中注入了三个对象,关注ConcurrentSessionControlAuthenticationStrategy,它实现了对于session并发的控制
  • UsernamePasswordAuthenticationFilter的Bean中注入新配置的sas,用于替换原本的NullAuthenticatedSessionStrategy
  • ConcurrentSessionFilter的Bean用来验证session是否失效,并通过SimpleRedirectSessionInformationExpiredStrategy将失败访问进行跳转。

四、代码流程说明(这里模拟用户现在A处登录,随后用户在B处登录,之后A处再进行操作时会返回失败,提示重新登录)

1、用户在A处登录,UsernamePasswordAuthenticationFilter调用sessionStrategy.onAuthentication进行session验证

2、ConcurrentSessionControlAuthenticationStrategy中的onAuthentication开始进行session验证,服务器中保存了登录后的session

/**
   * In addition to the steps from the superclass, the sessionRegistry will be updated
   * with the new session information.
   */
  public void onAuthentication(Authentication authentication,
      HttpServletRequest request, HttpServletResponse response) {

    //根据所登录的用户信息,查询相对应的现存session列表
    final List<SessionInformation> sessions = sessionRegistry.getAllSessions(
        authentication.getPrincipal(), false);

    int sessionCount = sessions.size();
    //获取session并发数量,对于XML中的maximumSessions
    int allowedSessions = getMaximumSessionsForThisUser(authentication);

    //判断现有session列表数量和并发控制数间的关系
    //如果是首次登录,根据xml配置,这里应该是0<1,程序将会继续向下执行,
    //最终执行到SessionRegistryImpl的registerNewSession进行新session的保存
    if (sessionCount < allowedSessions) {
      // They haven't got too many login sessions running at present
      return;
    }

    if (allowedSessions == -1) {
      // We permit unlimited logins
      return;
    }

    if (sessionCount == allowedSessions) {
      //获取本次http请求的session
      HttpSession session = request.getSession(false);

      if (session != null) {
        // Only permit it though if this request is associated with one of the
        // already registered sessions
        for (SessionInformation si : sessions) {
          //循环已保存的session列表,判断本次http请求session是否已经保存
          if (si.getSessionId().equals(session.getId())) {
            //本次http请求是有效请求,返回执行下一个filter
            return;
          }
        }
      }
      // If the session is null, a new one will be created by the parent class,
      // exceeding the allowed number
    }

    //本次http请求为新请求,进入具体判断
    allowableSessionsExceeded(sessions, allowedSessions, sessionRegistry);
  }
/**
   * Allows subclasses to customise behaviour when too many sessions are detected.
   *
   * @param sessions either <code>null</code> or all unexpired sessions associated with
   * the principal
   * @param allowableSessions the number of concurrent sessions the user is allowed to
   * have
   * @param registry an instance of the <code>SessionRegistry</code> for subclass use
   *
   */
  protected void allowableSessionsExceeded(List<SessionInformation> sessions,
      int allowableSessions, SessionRegistry registry)
      throws SessionAuthenticationException {
    //根据exceptionIfMaximumExceeded判断是否要将新http请求拒绝
    //exceptionIfMaximumExceeded也可以在XML中配置
    if (exceptionIfMaximumExceeded || (sessions == null)) {
      throw new SessionAuthenticationException(messages.getMessage(
          "ConcurrentSessionControlAuthenticationStrategy.exceededAllowed",
          new Object[] { Integer.valueOf(allowableSessions) },
          "Maximum sessions of {0} for this principal exceeded"));
    }

    // Determine least recently used session, and mark it for invalidation
    SessionInformation leastRecentlyUsed = null;

    //若不拒绝新请求,遍历现存seesion列表
    for (SessionInformation session : sessions) {
      //获取上一次/已存的session信息
      if ((leastRecentlyUsed == null)
          || session.getLastRequest()
              .before(leastRecentlyUsed.getLastRequest())) {
        leastRecentlyUsed = session;
      }
    }

    //将上次session信息写为无效(欺骗)
    leastRecentlyUsed.expireNow();
  }

3、用户在B处登录,再次通过ConcurrentSessionControlAuthenticationStrategy的检查,将A处登录的session置于无效状态,并在session列表中添加本次session

4、用户在A处尝试进行其他操作,ConcurrentSessionFilter进行Session相关的验证,发现A处用户已经失效,提示重新登录

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
      throws IOException, ServletException {
    HttpServletRequest request = (HttpServletRequest) req;
    HttpServletResponse response = (HttpServletResponse) res;

  //获取本次http请求的session
    HttpSession session = request.getSession(false);

    if (session != null) {
      //从本地session关系表中取出本次http访问的具体session信息
      SessionInformation info = sessionRegistry.getSessionInformation(session
          .getId());
      //如果存在信息,则继续执行
      if (info != null) {
        //判断session是否已经失效(这一步在本文4.2中被执行)
        if (info.isExpired()) {
          // Expired - abort processing
          if (logger.isDebugEnabled()) {
            logger.debug("Requested session ID "
                + request.getRequestedSessionId() + " has expired.");
          }
          //执行登出操作
          doLogout(request, response);

          //从XML配置中的redirectSessionInformationExpiredStrategy获取URL重定向信息,页面跳转到登录页面
          this.sessionInformationExpiredStrategy.onExpiredSessionDetected(new SessionInformationExpiredEvent(info, request, response));
          return;
        }
        else {
          // Non-expired - update last request date/time
          sessionRegistry.refreshLastRequest(info.getSessionId());
        }
      }
    }

    chain.doFilter(request, response);
  }

5、A处用户只能再次登录,这时B处用户session将会失效重登,如此循环

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Ajax登陆使用Spring Security缓存跳转到登陆前的链接

    Spring Security缓存的应用之登陆后跳转到登录前源地址 什么意思? 用户访问网站,打开了一个链接:(origin url)起源链接 请求发送给服务器,服务器判断用户请求了受保护的资源. 由于用户没有登录,服务器重定向到登录页面:/login 填写表单,点击登录 浏览器将用户名密码以表单形式发送给服务器 服务器验证用户名密码.成功,进入到下一步.否则要求用户重新认证(第三步) 服务器对用户拥有的权限(角色)判定.有权限,重定向到origin url; 权限不足,返回状态码403( "禁

  • SpringBoot+Spring Security+JWT实现RESTful Api权限控制的方法

    摘要:用spring-boot开发RESTful API非常的方便,在生产环境中,对发布的API增加授权保护是非常必要的.现在我们来看如何利用JWT技术为API增加授权保护,保证只有获得授权的用户才能够访问API. 一:开发一个简单的API 在IDEA开发工具中新建一个maven工程,添加对应的依赖如下: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-b

  • 基于Spring Security的Oauth2授权实现方法

    前言 经过一段时间的学习Oauth2,在网上也借鉴学习了一些大牛的经验,推荐在学习的过程中多看几遍阮一峰的<理解OAuth 2.0>,经过对Oauth2的多种方式的实现,个人推荐Spring Security和Oauth2的实现是相对优雅的,理由如下: 1.相对于直接实现Oauth2,减少了很多代码量,也就减少的查找问题的成本. 2.通过调整配置文件,灵活配置Oauth相关配置. 3.通过结合路由组件(如zuul),更好的实现微服务权限控制扩展. Oauth2概述 oauth2根据使用场景不同

  • 浅谈Spring Security 对于静态资源的拦截与放行

    初始创建Spring Boot项目,使用thymeleaf作为模板引擎,利用Spring Security进行验证管理,根据官方例子试验成功(官方的Spring Security示例). 然后准备整合页面直接将html甩到templates目录下,静态资源甩到static目录下. 简单的测试页面,发现会报错如下: Refused to apply style from 'http://localhost:8080/login' because its MIME type ('text/html'

  • 使用Spring Security OAuth2实现单点登录

    1.概述 在本教程中,我们将讨论如何使用Spring Security OAuth和Spring Boot实现SSO - 单点登录. 我们将使用三个单独的应用程序: •授权服务器 - 这是中央身份验证机制 •两个客户端应用程序:使用SSO的应用程序 非常简单地说,当用户试图访问客户端应用程序中的安全页面时,他们将被重定向到首先通过身份验证服务器进行身份验证. 我们将使用OAuth2中的授权代码授权类型来驱动身份验证委派. 2.客户端应用程序 让我们从客户端应用程序开始;当然,我们将使用Sprin

  • spring security CSRF防护的示例代码

    CSRF是指跨站请求伪造(Cross-site request forgery),是web常见的攻击之一. 从Spring Security 4.0开始,默认情况下会启用CSRF保护,以防止CSRF攻击应用程序,Spring Security CSRF会针对PATCH,POST,PUT和DELETE方法进行防护. 我这边是spring boot项目,在启用了@EnableWebSecurity注解后,csrf保护就自动生效了. 所以在默认配置下,即便已经登录了,页面中发起PATCH,POST,P

  • SpringBoot2.0 整合 SpringSecurity 框架实现用户权限安全管理方法

    一.Security简介 1.基础概念 Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架.它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring的IOC,DI,AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为安全控制编写大量重复代码的工作. 2.核心API解读 1).SecurityContextHolder 最基本的对象,保存着当前会话用户认证,权限,鉴权等核心数据.Secu

  • Spring security实现登陆和权限角色控制

     随笔简介 1.spring版本:4.3.2.RELEASE+spring security 版本:4.1.2.RELEASE(其它不做说明) 2.所展示内容全部用注解配置 3.springmvc已经配置好,不作说明 4.会涉及到springmvc,spel,el的东西,不熟悉的同学可以先去看一下这方面内容,特别是springmvc 首先想一下,登陆需要什么,最简单的情况下,用户名,密码,然后比对数据库,如果吻合就跳转到个人页面,否则回到登陆页面,并且提示用户名密码错误.这个过程中应该还带有权限

  • Spring Security实现禁止用户重复登陆的配置原理

    这篇文章主要介绍了Spring Security实现禁止用户重复登陆的配置原理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 系统使用了Spring Security做权限管理,现在对于系统的用户,需要改动配置,实现无法多地登陆. 一.SpringMVC项目,配置如下: 首先在修改Security相关的XML,我这里是spring-security.xml,修改UsernamePasswordAuthenticationFilter相关Bean

  • spring security在分布式项目下的配置方法(案例详解)

    分布式项目和传统项目的区别就是,分布式项目有多个服务,每一个服务仅仅只实现一套系统中一个或几个功能,所有的服务组合在一起才能实现系统的完整功能.这会产生一个问题,多个服务之间session不能共享,你在其中一个服务中登录了,登录信息保存在这个服务的session中,别的服务不知道啊,所以你访问别的服务还得在重新登录一次,对用户十分不友好.为了解决这个问题,于是就产生了单点登录: **jwt单点登录:**就是用户在登录服务登录成功后,登录服务会产生向前端响应一个token(令牌),以后用户再访问系

  • SpringBoot + Spring Security 基本使用及个性化登录配置详解

    Spring Security 基本介绍 这里就不对Spring Security进行过多的介绍了,具体的可以参考官方文档 我就只说下SpringSecurity核心功能: 认证(你是谁) 授权(你能干什么) 攻击防护(防止伪造身份) 基本环境搭建 这里我们以SpringBoot作为项目的基本框架,我这里使用的是maven的方式来进行的包管理,所以这里先给出集成Spring Security的方式 <dependencies> ... <dependency> <groupI

  • 如何基于spring security实现在线用户统计

    1.修改web.xml,需要在web.xml描述文件中配置中使得o.s.s.web.session.HttpSessionEventPublisher生效,这样servelt容器将会通知Spring Security session生命周期的事件(通过HttpSessionEventPublisher) <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </lis

  • 如何使用Spring Security手动验证用户的方法示例

    1.概述 在这篇快速文章中,我们将 重点介绍如何以编程方式在Spring Security和Spring MVC中设置经过身份验证的用户 . 2. Spring Security 简而言之, Spring Security在ThreadLocal中保存每个经过身份验证的用户的主要信息 - 保存的是Authentication对象 . 为了构造和设置此Authentication对象, 通常我们需要使用Spring Security在标准身份验证上构建对象的相 同方法. 要让我们手动触发身份验证,

  • spring Security的自定义用户认证过程详解

    首先我需要在xml文件中声明.我要进行自定义用户的认证类,也就是我要自己从数据库中进行查询 <http pattern="/*.html" security="none"/> <http pattern="/css/**" security="none"/> <http pattern="/img/**" security="none"/> <h

  • Spring Boot 中嵌入式 Servlet 容器自动配置原理解析

    目录 1.参照 Spring Boot 自动配置包里面的web模块 2.EmbeddedServletContainerFactory(嵌入式Servlet容器工厂) 3.EmbeddedServletContainer(嵌入式的Servlet容器) 4.以TomcatEmbeddedServletContainerFactory为例 5.嵌入式容器的配置修改生效原理 1.参照 Spring Boot 自动配置包里面的web模块 EmbeddedServletContainerAutoConfi

  • Spring Security 自动踢掉前一个登录用户的实现代码

    登录成功后,自动踢掉前一个登录用户,松哥第一次见到这个功能,就是在扣扣里边见到的,当时觉得挺好玩的. 自己做开发后,也遇到过一模一样的需求,正好最近的 Spring Security 系列正在连载,就结合 Spring Security 来和大家聊一聊这个功能如何实现. 本文是本系列的第十三篇,阅读前面文章有助于更好的理解本文: 挖一个大坑,Spring Security 开搞! 松哥手把手带你入门 Spring Security,别再问密码怎么解密了 手把手教你定制 Spring Securi

  • Spring Security登录表单配置示例详解

    目录 Spring Security登录表单配置 1.引入pom依赖 2.bootstrap.yml添加配置 3.创建login.html 4.创建配置类 5.配置细节 6.登陆成功 7.登陆失败 8.注销登录 Spring Security登录表单配置 1.引入pom依赖 ​ 创建一个Spring Boot工程,引入Web和Spring Security依赖: <?xml version="1.0" encoding="UTF-8"?> <pro

  • 详解Spring Security的Web应用和指纹登录实践

    前言 Java 开发人员在解决 Web 应用安全相关的问题时,通常会采用两个非常流行的安全框架,Shiro 和 Spring Security.Shiro 配置简单,上手快,满足一般应用的安全需求,但是功能相对单一.Spring Security 安全粒度细,与 Spring Framework 无缝集成,满足绝大多数企业级应用的安全需求,但是配置复杂,学习曲线陡峭. Spring Security 相对 Shiro 功能强大,并且 Spring Framework,Spring Boot,Sp

随机推荐