SpringSecurity RememberMe功能

一、前言

如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功能)