会话指得是浏览器和服务端通过session交互过程
当前系统中,同一个用户是否可以在多台设备登录,springsecurity默认没有限制,可以在多台设备登录,可以在springsecurity中配置管理
引入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).
会话失效我们该如何改变找个提示?
@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(); } }
@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(); Mapmap = 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(); } }
默认是被挤下线方式 可以设置后来者无法登录
@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(); } }
上面会话都是通过内存中的map集中管理,所以无法在分布式集群系统中共享,要在集群中使用,就要用spring-session集合redis实现session共享
引入依赖
org.springframework.boot spring-boot-starter-data-redis org.springframework.session spring-session-data-redis
系统配置文件配置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); } }