security 会话并发管理

时间:2022-10-09 13:01:03


一、简介

会话指得是浏览器和服务端通过session交互过程

二、会话并发管理

1、什么是会话并发

当前系统中,同一个用户是否可以在多台设备登录,springsecurity默认没有限制,可以在多台设备登录,可以在springsecurity中配置管理

2、代码

引入security不做任何配置 默认同一个账号是可以在多个浏览器登录访问系统


@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.csrf().disable()
.sessionManagement()//开启会话管理
.maximumSessions(1);//同一个账号只能在一个浏览器登录
}

/**
*找个bean可以不加,但是建议加上
* security提供一个map来集护当前http session记录 实现会话并发管理,当登录时候增加一条 ,退出时从集合移除一个
*/
@Bean
public HttpSessionEventPublisher httpSessionEventPublisher(){
return new HttpSessionEventPublisher();
}
}


当多个浏览器登录时候出现如下提示

This session has been expired (possibly due to multiple concurrent logins being attempted as the same user).

会话失效我们该如何改变找个提示?

3、会话被挤下线时处理

3.1、传统web开发

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.csrf().disable()
.sessionManagement()
.maximumSessions(1)
.expiredUrl("/login");//被挤下线时候跳转地址
}
@Bean
public HttpSessionEventPublisher httpSessionEventPublisher(){
return new HttpSessionEventPublisher();
}
}


3.2、前后端分离


@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.csrf().disable()
.sessionManagement()
.maximumSessions(1)
.expiredSessionStrategy(event -> {
HttpServletResponse response = event.getResponse();
Map<String,Object> map = new HashMap<>();
map.put("code",500);
map.put("msg","当前账号异地登录");
String result = new ObjectMapper().writeValueAsString(map);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().println(result);
response.flushBuffer();
});//参数是个函数式接口 直接用lambda处理
}
@Bean
public HttpSessionEventPublisher httpSessionEventPublisher(){
return new HttpSessionEventPublisher();
}
}


4、禁止再次登录

默认是被挤下线方式 可以设置后来者无法登录


@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.csrf().disable()
.sessionManagement()
.maximumSessions(1)
.expiredUrl("/login")
.maxSessionsPreventsLogin(true);//一旦登录 禁止再次登录
}
@Bean
public HttpSessionEventPublisher httpSessionEventPublisher(){
return new HttpSessionEventPublisher();
}
}


5、分布式会话共享

上面会话都是通过内存中的map集中管理,所以无法在分布式集群系统*享,要在集群中使用,就要用spring-session集合redis实现session共享

引入依赖


<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>


系统配置文件配置redis

spring.redis.port=6379
spring.redis.url=localhost

security配置

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

//注入session管理方案
@Autowired
private FindByIndexNameSessionRepository findByIndexNameSessionRepository;

@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.csrf().disable()
.sessionManagement()
.maximumSessions(1)
.expiredUrl("/login")
.sessionRegistry(sessionRegistry())//将session交给谁管理
.maxSessionsPreventsLogin(true);
}

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