zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    WPF程序设计 :第四章 按钮与其他控件(Buttons and Other Controls)
    C#参考 : 枚举类型
    C#3.0 新特性学习笔记(3):匿名类型
    F#语言2008年9月CTP版已经更新
    C#3.0 新特性学习笔记(1): 对象集合初始化器
    WPF程序设计基础:属性系统
    C#3.0 新特性学习笔记(2):var 隐式声明变量
    MSSql行列转换的Sql语法 详解与实例
    WPF程序设计 :第一章 应用程序和窗口(The Application and the Window)
    WPF程序设计 :第二章 基本画刷(Basic Brushes)
  • 原文地址:https://www.cnblogs.com/gocode/p/14814711.html
Copyright © 2011-2022 走看看