美文网首页
SpringSecurity RememberMe功能

SpringSecurity RememberMe功能

作者: 茧铭 | 来源:发表于2021-01-06 16:22 被阅读0次

    一、前言

    如SpringSecurity在用户名密码登录的示例所示:
    UsernamePasswordAuthenticationFilter的父类Filter中的 doFilter() 方法,调用用户认证的方法认证用户成功后,会调用一个名为successfulAuthentication 的方法,它内部有几大过程;

    • 1、将认证成功的信息存入SecurityContextHolder中。
    • 2、如果rememberMeServices功能开启了,处理rememberMe的逻辑。
    • 3、调用successHandler成功处理器。
      其中第二点就是今天要说的 "记住我" 的功能。它的实现逻辑如下:
    rememberMe.png

    二、流程梳理

    1、第一次赋值流程(假设已经开启了rememberMe认证流程)

    PersistentTokenBasedRememberMeServices类中,先是生成一个PersistentRememberMeToken类型的 token,并通过tokenReposority.createNewToken(token)方法存储这个token,最后将token信息存入cookie中返回前端。


    这里的tokenReposority是需要我们自己配置的,SpringSecurity提前提供好了两个可供使用的类
    InMemoryTokenRepositoryImpl 
    顾名思义,将token存储在内存中,特点是快,但是消耗内存,用户量少的话可以使用。内部原理也很简单,就是将不同的token存储在一个HashMap里面。
    
    @Bean
    public PersistentTokenRepository persistentTokenRepository(){
        InMemoryTokenRepositoryImpl tokenRepository = new InMemoryTokenRepositoryImpl ();
        return tokenRepository;
    }
    

    JdbcTokenRepositoryImpl
    这个是将token存储在数据库中的选择,下面setCreateTableOnStartup选中的ture会自动在数据库中创建一个表来存储数据(第二次启动项目记得改为false,因为表第一次启动已经创建了,第二次还是true会报错)
    
    @Bean
    public PersistentTokenRepository persistentTokenRepository(){
        JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
        tokenRepository.setCreateTableOnStartup(true);  
        tokenRepository.setDataSource(dataSource);
        return tokenRepository;
    }
    

    其实,我们还可以自定义这个TokenRepository,只需要去实现上述说的两个类的接口PersistentTokenRepository即可。

    public interface PersistentTokenRepository {
        void createNewToken(PersistentRememberMeToken token);
        void updateToken(String series, String tokenValue, Date lastUsed);
        PersistentRememberMeToken getTokenForSeries(String seriesId);
        void removeUserTokens(String username);
    }
    

    2、过滤流程

    第一次我登录了之后,因为会话的关系,我们可以访问一些资源。但是当我关闭页面,在会话消失后,我们的访问一个后台资源的话,按照以往的逻辑,应该是访问不到且会跳转到登录页面。但是如果我们之前的会话有RememberMe的话,cookie中带有一个名为remember-me的信息,在通过前面几个过滤器之后,到了名为RememberMeAuthenticationFilter的过滤器中的时候,它会从request中拿取cookie信息,并尝试通过token去获取用户信息,成功获取到之后会通过UserDetailService认证,认证通过即可放行。

    上面的aotuLogin()方法如下

    最后,附上开启记住我功能的配置

       // tokenRepository配置
        @Bean
        public PersistentTokenRepository persistentTokenRepository(){
            JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
            tokenRepository.setCreateTableOnStartup(false);
            tokenRepository.setDataSource(dataSource);
            return tokenRepository;
        }
        // 默认的密码加密
        @Bean
        public PasswordEncoder passwordEncoder(){
            return new BCryptPasswordEncoder();
        }
        // 这个自定义即可
        @Bean
        public UserDetailsService demoUser() {
            return (username) -> new User(username, passwordEncoder().encode("123456"),
                    AuthorityUtils.commaSeparatedStringToAuthorityList("admin,ROLE_USER"));
        }
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
                http.formLogin()
                        .loginProcessingUrl("/testToLogin")
                        .loginPage("/needLogin")
                        .successHandler((request, response, authentication) -> {
                            PrintWriter writer = response.getWriter();
                            writer.print("强啊,成了得嘛");
                        }).failureHandler((request, response, exception) -> {
                            response.setContentType("application/json;charset=UTF-8");
                            response.getWriter().write(om.writeValueAsString("login failure"));
                        })
                    .and()
                        .rememberMe()            // -----------就是这里了
                        .alwaysRemember(true)      // 最近发现新版本要多配置一个这个,否则也没有开启
                        .tokenValiditySeconds(3600)
                        .tokenRepository(persistentTokenRepository())
                        .userDetailsService(demoUser())
                    .and()
                    .authorizeRequests()
                    .antMatchers("/skip", "/needLogin").permitAll()
                    .anyRequest()
                    .authenticated()
                    .and()
                    .csrf().disable();
        }
    

    相关文章

      网友评论

          本文标题:SpringSecurity RememberMe功能

          本文链接:https://www.haomeiwen.com/subject/ysonoktx.html