首先還是吐槽,百度了兩天,嘗試了十多種方案,各種重寫,無效。。。。。最後,還是在官網找到解決方案。
哎。。。已經很多次了。官網。官網。官網。。以後要多看官網。
還是描述下需求吧。很簡單,相同的用戶登錄時,只能保留最後一個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);
}