SpringSecurity 下 Session 使用

在之前的几章里面,我们分别做了快速入门、自定义表单登录、自定义手机登录。他们有一个共同点,就是目前我们与客户端之间的交互都依赖于 Session。那么本章我们就带大家来了解一下 SpringSecurity 下 Session 的使用与设置。

Session 是什么?

Session 中文意思为会议,在计算机中,尤其在网络应用中称为会话控制

Session直接翻译成中文比较困难,一般都译成时域。在计算机专业术语中,Session是指一个终端用户与交互系统进行通信的时间间隔,通常指从注册进入系统到注销退出系统之间所经过的时间。以及如果需要的话,可能还有一定的操作空间。

百度百科

为什么要使用 Session?

Http 协议本身是无状态的,多次连接之间的状态无法共享,服务端就判断是否是同一个用户操作的就比较麻烦。

而 Session 就是用来保存状态的,允许通过将对象存储在 Web 服务器内存中,在整个用户会话过程中进行共享。

Session 原理

浏览器第一次访问服务器时,服务端会生成 Session,然后同时为该 Session 生成一个唯一的 SessionId,然后将 SessionId 与 Session 以键值对的形式存储到内存中(Java 默认)。将 SessionId 以 Cookie 的方式返回给浏览器。浏览器下一次访问的时候就携带 SessionId 来访问,这样服务端通过查询就可以取出 Session 达到共享了。

在 SpringSecurity 里面的使用

单机 Session
  • Session 超时设置
    • 设置 Session 超时时间,在系统配置文件(application.properties/yml) 中配置

      server:
        session:
          timeout: 600 #单位为 Second,设置的时间低于 1 分钟,按 1 分钟处理
      
    • 设置 Session 超时/失效后继续访问处理,可以设置跳转页面,也可以设置跳转接口。前后端分离一般返回 Json,不分离可以引导页面跳转。两者都要兼容,可以跳转到自定义接口判断实际请求,进行处理。

      // 设置 Session 失效后访问跳转的页面
      http.sessionManagement().invalidSessionUrl("/session/invalid");
      
  • Session 并发控制
    • Session 最大数设置,限制系统中用户数量

      http.sessionManagement().maximumSessions(1)// 设置同时在线的最大 Session 数
      
    • 设置 Session 数量达到最大值时,是否阻止后面用户登录

      // 设置当 Session 数量达到最大数量时,阻止后面的登录
      http.sessionManagement().maxSessionsPreventsLogin(true);
      
    • 自定义设置 Session 数量达到最大时后续登录的处理策略,默认实现会将前面用户的 Session 置为失效

      // 设置 Session 并发登录时处理策略
      http.sessionManagement().expiredSessionStrategy(new DefaultExpiredSessionStrategy());
      
      /**
       * @author: hblolj
       * @Date: 2019/3/16 9:13
       * @Description: Session 并发登录时的处理策略
       * @Version:
       **/
      public class DefaultExpiredSessionStrategy implements SessionInformationExpiredStrategy{
      
          @Override
          public void onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException, ServletException {
              event.getResponse().setCharacterEncoding("UTF-8");
              event.getResponse().getWriter().write("并发登录!");
          }
      }
      
集群 Session(单机 Session 下的设置在集群下一样有效)

在单机 Session 下基本上只要处理好上面的问题就可以了,但是随着系统越来越大,单体应用可能满足不了我们的需求。如果我们对应用进行了集群,那么用户在每个应用上都会产生一个 Session,并且这些 Session 还不是共享的,就达不到 Session 使用的初衷了。

为了让 Session 可以共享,一般我们就会改变 Session 的存储方式,Session 默认存储在内存中,多个主机间就不方便共享。主流处理方案有:

  • 保存在数据库
  • 保存在 Redis 中
  • 利用 Servlet 容器来进行 Session 同步
  • 或者使用 Nginx 使用用户 ip 进行哈希处理,在负载均衡时,让用户每次访问同一个主机避免多 Session 问题

我们这里主要还是讨论 SpringSecurity 下集群 Session 的处理方案,其他扩展信息,大家感兴趣可以自行查询,或者留言讨论。

我们这里示例使用 Redis 作为 Session 存储方式,通过下列配置 Session 就会默认存储在 Redis 中了。关于 SpringBoot 下 Redis 的使用,不了解的可以查询资料使用,上手还是比较简单的。

  • 使用 Redis 作为 Session 的存储,通过配置文件指定存储方式。

    spring:
      session:
        store-type: redis #设置将 Session 存储在 Redis 中
      redis:
        host: redis 所在的主机
        port: 默认是 6379
        database: 选择使用的 database
        password: your self password
        pool:
          max-wait: -1
          max-idle: 1
          max-active: 8
          min-idle: 0
    
  • 添加 Redis 依赖

    <!-- SpringBoot 版本 1.5.19.RELEASE-->
    <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    
  • 添加 Spring-Session 依赖

    <!-- SpringBoot 版本 1.5.19.RELEASE-->
    <dependency>
       <groupId>org.springframework.session</groupId>
       <artifactId>spring-session</artifactId>
    </dependency>
    
  • 除了 Redis,SpringBoot 还支持一下存储方式,大家可以自行扩展使用

    public enum StoreType {
        REDIS,
        MONGO,
        JDBC,
        HAZELCAST,
        HASH_MAP,
        NONE;
    
        private StoreType() {
        }
    }
    
退出登录

既然 Session 中共享用户多次请求的状态,那么如果我们要用户登出,就只要清空 Session 即可。那么在 SpringSecurity 下是怎样设置的呢?

  • 如何退出

    • 请求登出地址,默认为 /logout

    • 自定义登出地址

      http.logout().logoutUrl("/quit");// 默认是 logout
      
  • 退出处理原理 (访问退出地址,退出处理逻辑 SpringSecurity 有默认实现)

    • 使当前 Session 失效

      (HttpSession)session.invalidate();
      
    • 清除与当前用户先关的 remember-me 记录

      if (authentication != null) {
          this.tokenRepository.removeUserTokens(authentication.getName());
      }
      
    • 清空当前的 SecurityContext

      if (this.clearAuthentication) {
          SecurityContext context = SecurityContextHolder.getContext();
          context.setAuthentication((Authentication)null);
      }
      
      SecurityContextHolder.clearContext();
      
  • 与退出相关的配置

    • 自定义退出成功跳转页面或处理策略,同时设置只有 logoutSuccessHandler 会生效

      http
      	.logout()
          .logoutSuccessUrl("http://www.baidu.com") // 设置退出成功跳转的页面
          .logoutSuccessHandler(new DefaultLogoutSuccessHandler()) // 设置退出成功的处理策略
      
      /**
       * @author: hblolj
       * @Date: 2019/3/16 10:28
       * @Description:
       * @Version:
       **/
      @Slf4j
      public class DefaultLogoutSuccessHandler implements LogoutSuccessHandler{
      
          @Override
          public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
      
              log.info("Logout Success!");
          }
      }
      
    • 设置退出时删除客户端 Cookie

      http.logout().deleteCookies("JSESSIONID") // 退出时删除客户端指定的 Cookie
      

OK,到了这里,基本上在 SpringSecurity 里面 Session 的常规设置与使用都介绍完了。大家如果有什么问题,欢迎在下面的留言区留言讨论。

实际上,在现在的开发中,单体 Web 应用可能还是以 Session 为主,但是如果要同时支持 App 的话, Session 就不是那么好用了,因为 Session 依赖 Cookie,虽然 App 访问时可以模拟浏览器设置 Cookie,但是契合度还是不如 Web,所以对于 App 或者集群环境下,现在主流使用基于 OAuth2 的 Token 作为验证。是不是以为我们下一章要讲 SpringSecurity 下 OAuth2 协议的集成与 Token 使用呢?哈哈,下一章我们计划带大家集成一下 SpringSecurity 下第三方登录,比如 QQ 快速登录、微信快速登录等(也依赖于 OAuth 协议)。至于集成 Token 认证还要往后几章哦!

To Be Continue!

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Security和Spring Session的结合可以提供更强大的安全性和会话管理功能。Spring Security提供了身份验证和授权的功能,而Spring Session提供了跨多个请求的会话管理。 在结合使用时,可以使用Spring Session的`SessionRepositoryFilter`将会话存储在Redis或数据库等外部存储中,并在Spring Security中使用`SessionManagementConfigurer`配置会话管理。以下是一个简单的示例: ``` @Configuration @EnableWebSecurity @EnableRedisHttpSession public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private RedisConnectionFactory redisConnectionFactory; @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/public/**").permitAll() .anyRequest().authenticated() .and() .formLogin().loginPage("/login").permitAll() .and() .logout().permitAll(); } @Bean public RedisOperationsSessionRepository sessionRepository() { return new RedisOperationsSessionRepository(redisConnectionFactory); } @Bean public SessionManagementConfigurer<HttpSecurity> sessionManagementConfigurer() { return new SessionManagementConfigurer<HttpSecurity>() { @Override public void configure(HttpSecurity http) throws Exception { http.sessionManagement() .sessionAuthenticationStrategy(sessionAuthenticationStrategy()) .maximumSessions(1) .maxSessionsPreventsLogin(true) .sessionRegistry(sessionRegistry()); } }; } @Bean public SessionAuthenticationStrategy sessionAuthenticationStrategy() { return new ConcurrentSessionControlAuthenticationStrategy(sessionRegistry()); } @Bean public SessionRegistry sessionRegistry() { return new SpringSessionBackedSessionRegistry<>(new RedisOperationsSessionRepository(redisConnectionFactory)); } } ``` 在上面的配置中,`@EnableRedisHttpSession`注解启用了Spring Session,并使用`RedisOperationsSessionRepository`将会话存储在Redis中。`SessionManagementConfigurer`配置了会话管理,包括最大并发会话数和会话注册表。 需要注意的是,Spring Session默认使用一个名为`SESSION`的Cookie来跟踪会话。如果需要自定义Cookie名称和其他会话属性,可以使用`@EnableRedisHttpSession`的`cookieName`和`redisNamespace`属性进行配置。 在使用Spring Security和Spring Session结合时,还需要确保在各个请求中正确地暴露会话信息。可以使用Spring Session的`SessionRepositoryFilter`来完成这个任务,例如: ``` @Configuration public class WebConfig implements WebMvcConfigurer { @Autowired private RedisConnectionFactory redisConnectionFactory; @Bean public FilterRegistrationBean<SessionRepositoryFilter> sessionRepositoryFilterRegistration() { FilterRegistrationBean<SessionRepositoryFilter> registration = new FilterRegistrationBean<>(); registration.setFilter(new SessionRepositoryFilter(sessionRepository())); registration.addUrlPatterns("/*"); registration.setOrder(Ordered.HIGHEST_PRECEDENCE); return registration; } @Bean public RedisOperationsSessionRepository sessionRepository() { return new RedisOperationsSessionRepository(redisConnectionFactory); } } ``` 在上面的配置中,`SessionRepositoryFilter`将会话信息暴露在所有请求中。需要注意的是,`SessionRepositoryFilter`应该注册为具有最高优先级的过滤器,以确保会话数据在其他过滤器之前暴露。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值