首先还是吐槽,百度了两天,尝试了十多种方案,各种重写,无效。。。。。最后,还是在官网找到解决方案。
哎。。。已经很多次了。官网。官网。官网。。以后要多看官网。
还是描述下需求吧。很简单,相同的用户登录时,只能保留最后一个session。防止多处登录。也不算是单点。
在开始贴代码之前,各位看官一定要注意条件:
springboot+spring session redis+spring security ,使用java config方式。
可能不使用spring session redis 或者 spring security的情况,不会出现我的问题,因为不涉及redis session的控制。
一般情况下,如果使用spring自带的内存管理方式,非常简单就可以实现(没有尝试,看百度应该问题不大)
springsecurity配置中
@Override
protected void configure(HttpSecurity http) throws Exception {
....
http.sessionManagement().maximumSessions(1).maxSessionsPreventsLogin(false).expiredUrl("/login?expired");
}
而使用了spring session redis,则在spring security对登陆session控制的默认实现类SessionRegistryImpl中判断session时,无法获取正确的获取session。特别是重启应用后,基于内存的SessionRegistryImpl就会丢失session,而redis中的还存在。登陆成功的用户还能操作,而SessionRegistryImpl中,则没有任何session了。
所以,解决方案是,在springsecurity配置中,注册spring session redis 的sessionregistry。代码如下(20191024修改):
@Configuration
@Import({ SessionRedisConfig.class })
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true) // 开启security注解
public class WebSecurityConfig<S extends Session> extends WebSecurityConfigurerAdapter {
@Autowired
private FindByIndexNameSessionRepository<S> sessionRepository;
@Override
protected void configure(HttpSecurity http) throws Exception {
// 允许所有用户访问"/"和"/register"
http.authorizeRequests().antMatchers("/", "/public/**","/static/**", "/login").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
// 其他地址的访问均需验证权限
.anyRequest().authenticated()// .hasRole("USER")
.and().formLogin()
// 指定登录页是"/login"
.loginPage("/login")
// .failureUrl("/login?error=true")
.defaultSuccessUrl("/main").permitAll()
.and().headers().frameOptions().disable().and().logout()
.logoutUrl("/logout").logoutSuccessUrl("/").permitAll();
http.exceptionHandling().accessDeniedPage("/403")
.and().csrf().disable();
//not create new session
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER);
//ori session user
http.sessionManagement().sessionFixation().none();
http.sessionManagement().maximumSessions(1).maxSessionsPreventsLogin(false).expiredUrl("/login")
.sessionRegistry(sessionRegistry());
//http.addFilterBefore(myFilterSecurityInterceptor, FilterSecurityInterceptor.class);
}
@Bean
SpringSessionBackedSessionRegistry<S> sessionRegistry() {
return new SpringSessionBackedSessionRegistry<S>(sessionRepository);
}