spring security使用自定义的的AuthenticationFilter时,要限制session个数,禁止多端同时登录

在WebSecurityConfigurerAdapter#configure(HttpSecurity)方法中配置session管理没有效果,因为我们使用了自己的AuthenticationFilter,只能手动给LoginFilter配置SessionAuthenticationStrategy。

@Configuration
public class CustomFilterSecurityConfig extends WebSecurityConfigurerAdapter {

    //sessesion失效所需要的监听器,spring security默认配置过个bean,我们只需要自动注入即可
    @Autowired
    DelegatingApplicationListener delegatingApplicationListener;
    //省略其他......
       @Bean
    public LoginFilter loginFilter() throws Exception {
                LoginFilter loginFilter = new LoginFilter();
		//省略其他......
        //自定义filter要手动配置SessionAuthenticationStrategy
        loginFilter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy());
        return loginFilter;
    }
       @Bean
    public SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        
        SessionRegistryImpl sessionRegistry = new SessionRegistryImpl();
        ConcurrentSessionControlAuthenticationStrategy concurrentSessionControlStrategy =
                new ConcurrentSessionControlAuthenticationStrategy(sessionRegistry);
        delegatingApplicationListener.addListener(new GenericApplicationListenerAdapter(sessionRegistry));
        concurrentSessionControlStrategy.setMaximumSessions(1);
        //除非先在以前的客户端登出,否则不能在新的客户端登录
        concurrentSessionControlStrategy.setExceptionIfMaximumExceeded(true);
        //要注意这3个实例在集合中的顺序
        CompositeSessionAuthenticationStrategy delegateStrategies = new CompositeSessionAuthenticationStrategy(
                Arrays.asList(concurrentSessionControlStrategy,
                        new ChangeSessionIdAuthenticationStrategy(),
                        new RegisterSessionAuthenticationStrategy(sessionRegistry)
                ));
        return delegateStrategies;
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //替代默认的UsernamePasswordAuthenticationFilter
        http.addFilterAt(loginFilter(), UsernamePasswordAuthenticationFilter.class);
        http.authorizeRequests()
               //这里的配置session管理没用,因为我们使用了自己的AuthenticationFilter
                .sessionManagement()
                .maximumSessions(1)
                .maxSessionsPreventsLogin(true)
        ;

    }
}
    

SessionAuthenticationStrategy 是一个接口,我们不能只使用ConcurrentSessionControlAuthenticationStrategy做session管理,而应该使用CompositeSessionAuthenticationStrategy实例。CompositeSessionAuthenticationStrategyConcurrentSessionControlAuthenticationStrategyChangeSessionIdAuthenticationStrategyRegisterSessionAuthenticationStrategy组合起来,使用了典型的委托组合设计模式(或代理模式),CompositeSessionAuthenticationStrategy在onAuthentication方法中循环调用这三个实例它们自己的onAuthentication.

这里的RegisterSessionAuthenticationStrategy做session注册的,它比较重要,若没有它就无法检测登录的session次数。

我的SessionAuthenticationStrategy配置也是参考配置类方法SessionManagementConfigurer#getSessionAuthenticationStrategy来实现的。可以看出阅读源码是多么重要吧!

private SessionAuthenticationStrategy getSessionAuthenticationStrategy(H http) {
   if (this.sessionAuthenticationStrategy != null) {
      return this.sessionAuthenticationStrategy;
   }
   List<SessionAuthenticationStrategy> delegateStrategies = this.sessionAuthenticationStrategies;
   SessionAuthenticationStrategy defaultSessionAuthenticationStrategy;
   if (this.providedSessionAuthenticationStrategy == null) {
      // If the user did not provide a SessionAuthenticationStrategy
      // then default to sessionFixationAuthenticationStrategy 
       //执行这个分支defaultSessionAuthenticationStrategy是一个ChangeSessionIdAuthenticationStrategy实例
      defaultSessionAuthenticationStrategy = postProcess(this.sessionFixationAuthenticationStrategy);
   }
   else {
      defaultSessionAuthenticationStrategy = this.providedSessionAuthenticationStrategy;
   }
   if (isConcurrentSessionControlEnabled()) {
       //配置ConcurrentSessionControlAuthenticationStrategy
      SessionRegistry sessionRegistry = getSessionRegistry(http);
      ConcurrentSessionControlAuthenticationStrategy concurrentSessionControlStrategy = new ConcurrentSessionControlAuthenticationStrategy(
            sessionRegistry);
      concurrentSessionControlStrategy.setMaximumSessions(this.maximumSessions);
      concurrentSessionControlStrategy.setExceptionIfMaximumExceeded(this.maxSessionsPreventsLogin);
      concurrentSessionControlStrategy = postProcess(concurrentSessionControlStrategy);
 //配置RegisterSessionAuthenticationStrategy
      RegisterSessionAuthenticationStrategy registerSessionStrategy = new RegisterSessionAuthenticationStrategy(
            sessionRegistry);
      registerSessionStrategy = postProcess(registerSessionStrategy);

      delegateStrategies.addAll(Arrays.asList(concurrentSessionControlStrategy,
            defaultSessionAuthenticationStrategy, registerSessionStrategy));
   }
   else {
      delegateStrategies.add(defaultSessionAuthenticationStrategy);
   }
    //将上面3个SessionAuthenticationStrategy传入CompositeSessionAuthenticationStrategy的构造方法
   this.sessionAuthenticationStrategy = postProcess(
         new CompositeSessionAuthenticationStrategy(delegateStrategies));
   return this.sessionAuthenticationStrategy;
}
posted @ 2021-05-26 18:56  蜀中孤鹰  阅读(776)  评论(0编辑  收藏  举报