一、简介
会话指得是浏览器和服务端通过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);
}
}