zoukankan      html  css  js  c++  java
  • spring security 使用自定义的AuthenticationFilter,提示Invalid remember-me cookie,自动登录失败的解决方法

    spring security 在使用自定义的AuthenticationFilter时,提示Invalid remember-me cookie,自动登录失败的解决方法

    后台日志报错提示

    Invalid remember-me cookie: Cookie token[2] contained signature '1706b276eae214cc837865d3c508cf01' but expected '84908c8a60e515d544d96550496f0daf'

    我自定义了一个LoginFilter,它继承于UsernamePasswordAuthenticationFilter,用于支持json格式登录。

    public class LoginFilter extends UsernamePasswordAuthenticationFilter {
        private static Logger log = LoggerFactory.getLogger(LoginFilter.class);
    
        @Override
        public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
    ///省略实现
        }
    }
    

    但自动登录时始终报错:请求时携带的名为remember-me的cookie和服务器后台计算出的remember-me不匹配。后来Debug了很长时间才发现, 服务端会实现化两个TokenRememberMeServices实例,每个实例的构造方法都会需要key ( 计算加密remember-me的cookie时用到的这个key(相当于盐) ) ,如果两个key不一致,计算加密后的 remember-me 签名当然也就不一样,也就是cookie不匹配,自动登录失败。

    //必须入一个key
    public TokenBasedRememberMeServices(String key, UserDetailsService userDetailsService) {
       super(key, userDetailsService);
    }
    

    在使用自定义的AuthenticationFilter时,要实现自动登录必须配置RememberMeServices,否则系统将使用默认使用NullRememberMeServices。NullRememberMeServices所有方法都是空的,没有实现setCookies操作,不会向客户端写入名为remember-me的cookies,也就无法实现自动登录。

    在自定义的过滤器 LoginFilter#setRememberMeServicesWebSecurityConfigurerAdapter#configure(HttpSecurity)中需要同时设置Key,并且这个两处的Key值要相同。我之前没有在configure(HttpSecurity)方法中设key, 这里若没有设值,系统将使用随机生成的UUID作为key。这个随机的UUID肯定与setRememberMeServices方法中手动设置的keyOfRememberMe不一致,所以最终导致remember-me签名校验失败,无法自动登录。

    @Configuration
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
        @Autowired
        private ApplicationContext ctx;
        //appllication.yaml 配置的remember-me key
        @Value("${spring.security.remember-me.key}")
        private String keyOfRememberMe;
        @Autowired
        DataSource dataSource;
    
        @Autowired
        UserService userService;
         @Bean
        public LoginFilter loginFilter() throws Exception {
            LoginFilter loginFilter = new LoginFilter();
            //.....省略其他配置
            loginFilter.setAuthenticationManager(authenticationManagerBean());
            //   使用自定义的LoginFiler后要重新设置RememberMeServices,
            loginFilter.setRememberMeServices(new TokenBasedRememberMeServices(keyOfRememberMe, userService));
            return loginFilter;
    
        }
        
        @Override
      protected void configure(HttpSecurity http) throws Exception {
        //使用自定义的过滤器LoginFilter替代默认的UsernamePasswordAuthenticationFilter
        http.addFilterAt(loginFilter(), UsernamePasswordAuthenticationFilter.class);
        http.authorizeRequests()
                //...省略其他配置
                .and()
                .rememberMe()
                .key(keyOfRememberMe) //非常重要,和上面的setRememberMeServices中的key保持一致
                .and()
                .csrf()
                .disable();
    
     }
    }
    

    补充:

    另外还可以在LoginFilter#setRememberMeServicesWebSecurityConfigurerAdapter#configure(HttpSecurity)中配置同一个RememberMeServices实例,这样就不现分别为这两个方法设置remember-me的key了。

    @Configuration
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
        @Autowired
        private ApplicationContext ctx;
        //appllication.yaml 配置的remember-me key
        @Value("${spring.security.remember-me.key}")
        private String keyOfRememberMe;
        @Autowired
        DataSource dataSource;
    
        @Autowired
        UserService userService;
        
        //配置一个RememberMeServices类型的Bean
         @Bean
        public RememberMeServices rememberMeServices() {
            return new TokenBasedRememberMeServices(keyOfRememberMe, userService);
        }
        
         @Bean
        public LoginFilter loginFilter() throws Exception {
            LoginFilter loginFilter = new LoginFilter();
            //.....省略其他配置
            loginFilter.setAuthenticationManager(authenticationManagerBean());
            //  使用配置好的RememberMeServices实例,
            loginFilter.setRememberMeServices(rememberMeServices());
            return loginFilter;
    
        }
        
        @Override
      protected void configure(HttpSecurity http) throws Exception {
        //使用自定义的过滤器LoginFilter替代默认的UsernamePasswordAuthenticationFilter
        http.addFilterAt(loginFilter(), UsernamePasswordAuthenticationFilter.class);
        http.authorizeRequests()
                //...省略其他配置
                .and()
                .rememberMe()
            	//.key(keyOfRememberMe)
                //设置一个RememberMeServices实例,就不用再调用方法.key(keyOfRememberMe) 
                .rememberMeServices(rememberMeServices())
                .and()
                .csrf()
                .disable();
    
     }
    }
    
  • 相关阅读:
    springboot之静态资源放行的方法(记录)
    FastDFS之图片上传
    FastDFS安装部署
    Docker安装MySQL5.7.25
    123qwe
    Spring3 MVC 注解(一)---注解基本配置及@controller和 @RequestMapping 常用解释
    jQuery可见性过滤选择器
    jQuery属性过滤选择器
    jQuery内容过滤选择器
    jQuery子元素过滤选择器
  • 原文地址:https://www.cnblogs.com/gocode/p/14814699.html
Copyright © 2011-2022 走看看