SpringSecurity之会话管理介绍

本文详细介绍了Spring Security中的会话管理机制,包括如何限制同一用户的会话并发数量,处理会话并发冲突,以及在传统Web开发和前后端分离场景下的不同处理策略。此外,还探讨了在集群环境中如何利用spring-session和Redis实现会话共享,确保多节点下的会话一致性。
摘要由CSDN通过智能技术生成

1.简介

当浏览器调用登录接口登录成功后,服务端会和浏览器之间建立一个会话(Session)浏览器在每次发送请求时都会携带一个SessionId,服务端则根据这个SessionId来判断用户身份。当浏览器关闭后,服务端的Session并不会自动销毁,需要开发者手动在服务端调用Session销毁方法,或者等Session过期时间到了自动销毁。在Spring Security中,与HttpSession相关的功能由SessionManagermentFilter和SessionAuthenticationStrategy接口来处理,SessionManagermentFilter过滤器将Session相关操作委托给SessionAuthenticationStrategy接口去完成。

2.会话并发管理

2.1.简介

会话并发管理就是指在当前系统中,同一个用户可以同时创建多少个会话,如果一台设备对应一个会话,那么也可以简单理解为同一个用户可以同时在多少台设备上进行登录。默认情况下,同一用户在多少台设备上登录并没有限制,不过开发者可以在Spring Security中对此进行配置。

2.2.会话并发管理
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
	//...
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .and()
                .csrf()
                .disable()
                .sessionManagement() //开启会话管理
                .maximumSessions(1);  //允许会话最大并发只能一个客户端
    }
    @Bean
    public HttpSessionEventPublisher httpSessionEventPublisher(){
        return new HttpSessionEventPublisher();
    }
}

  1. sessionManagement()用来开启会话管理、maximumSessions指定会话的并发数为1.
  2. HttpSessionEventPublisher提供一个个HttpSessionEventPublisher实例。Spring Security中通过一个Map集合来维护当前的HttpSession记录,进而实现会话的并发管理。当用户登录成功时,就向集合中添加一条HttpSession记录;当会话销毁时,就从结合中移除一条HttpSession记录。HttpSessionEventPublisher实现了HttpSessionListener接口,可以监听到HttpSession的创建和销毁时间,并将HttpSession的创建/销毁事件发布出去,这样,当由HttpSession销毁时,Spring Security就可以感知到该事件了。
2.3.测试会话管理

配置完成后,启动项目,使用谷歌浏览器进行登录,完成登录操作,之后使用火狐浏览器进行登录,完成登录操作,当第二个浏览器登录成功后,再回到第一个浏览器,刷新页面,结果发现无法正常访问。

3.传统Web开发处理

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .and()
                .csrf()
                .disable()
                .sessionManagement() //开启会话管理
                .maximumSessions(1)  //允许会话最大并发只能一个客户端
                .expiredUrl("/login"); //当用户被挤下线之后跳转路径
    }

4.前后端分离开发处理

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .and()
                .csrf()
                .disable()
                .sessionManagement() //开启会话管理
                .maximumSessions(1)  //允许会话最大并发只能一个客户端
//                .expiredUrl("/login"); //当用户被挤下线之后跳转路径   传统Web开发
                .expiredSessionStrategy(event -> {      //前后端分离架构处理方案
                    HttpServletResponse response = event.getResponse();
                    Map<String,Object> result = new HashMap<>();
                    result.put("status",500);
                    result.put("msg","当前会话已经失效,请重新登录");
                    String s = new ObjectMapper().writeValueAsString(result);
                    response.setContentType("application/json;charset=UTF-8");
                    response.getWriter().println(s);
                    response.flushBuffer();
                });
    }

5.禁止再次登录

默认的效果是一种“被挤下线”的效果,后面登录的用户会把前面登录的用户“挤下线”。还有一种是禁止后来者登录,即一旦当前用户登陆成功,后来者无法再次使用相同的用户登录,直到当前用户主动注销登录,配置如下:

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .and()
                .csrf()
                .disable()
                .sessionManagement() //开启会话管理
                .maximumSessions(1)  //允许会话最大并发只能一个客户端
//                .expiredUrl("/login"); //当用户被挤下线之后跳转路径   传统Web开发
                .expiredSessionStrategy(event -> {      //前后端分离架构处理方案
                    HttpServletResponse response = event.getResponse();
                    Map<String,Object> result = new HashMap<>();
                    result.put("status",500);
                    result.put("msg","当前会话已经失效,请重新登录");
                    String s = new ObjectMapper().writeValueAsString(result);
                    response.setContentType("application/json;charset=UTF-8");
                    response.getWriter().println(s);
                    response.flushBuffer();
                })
                .maxSessionsPreventsLogin(true);  //一旦登录禁止再次登录
    }

6.会话共享

前面所讲的会话管理都是单机上的会话管理,如果当前是集群环境,前面所讲的会话管理方案就会失效。此时可以利用spring-session结合redis实现session共享。

6.1.实战

引入依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.session</groupId>
            <artifactId>spring-session-data-redis</artifactId>
        </dependency>

编写配置

spring.redis.host=localhost
spring.redis.port=6379
spring.redis.password=123456

配置springsecurity

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private final FindByIndexNameSessionRepository sessionRepository;

    @Autowired
    public SecurityConfig(FindByIndexNameSessionRepository sessionRepository) {
        this.sessionRepository = sessionRepository;
    }


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .and()
                .csrf()
                .disable()
                .sessionManagement() //开启会话管理
                .maximumSessions(1)  //允许会话最大并发只能一个客户端
//                .expiredUrl("/login"); //当用户被挤下线之后跳转路径   传统Web开发
                .expiredSessionStrategy(event -> {      //前后端分离架构处理方案
                    HttpServletResponse response = event.getResponse();
                    Map<String,Object> result = new HashMap<>();
                    result.put("status",500);
                    result.put("msg","当前会话已经失效,请重新登录");
                    String s = new ObjectMapper().writeValueAsString(result);
                    response.setContentType("application/json;charset=UTF-8");
                    response.getWriter().println(s);
                    response.flushBuffer();
                })
                .sessionRegistry(sessionRegistry())
                .maxSessionsPreventsLogin(true);//一旦登录禁止再次登录
    }

    //创建 session 同步到redis 中方案
   @Bean
   public SpringSessionBackedSessionRegistry sessionRegistry(){
        return new SpringSessionBackedSessionRegistry(sessionRepository);
   }

}

在这里插入图片描述

在这里插入图片描述

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值