SpringSecurity添加记住我功能
in JavaDevelop with 0 comment

SpringSecurity添加记住我功能

in JavaDevelop with 0 comment

添加记住我功能

记住我功能的基本原理

在security中认证过滤链中的 org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter 过滤器来实现的

当没有其他的认证过滤器处理的时候,记住我这个过滤器就尝试工作

如何实现记住我的功能

给默认登录页面增加选项

<!--名称是固定的-->
               <input type="checkbox" value="true" name="remember-me">记住我

nam的名称是以下类中被定义

org.springframework.security.config.annotation.web.configurers.RememberMeConfigurer

private static final String DEFAULT_REMEMBER_ME_NAME = "remember-me";

配置:(补全的代码都是伪代码,只贴出来变化的部分)

@Configuration
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {

  // 数据源是需要在使用处配置数据源的信息
   @Autowired
   private DataSource dataSource;
   @Autowired
   private PersistentTokenRepository persistentTokenRepository;
   // 之前已经写好的 MyUserDetailsService
   @Autowired
   private UserDetailsService userDetailsService;

   @Bean
   public PersistentTokenRepository persistentTokenRepository() {
       // org.springframework.security.config.annotation.web.configurers.RememberMeConfigurer.tokenRepository
       JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
       jdbcTokenRepository.setDataSource(dataSource);
       // 该对象里面有定义创建表的语句
       // 可以设置让该类来创建表
       // 但是该功能只用使用一次,如果数据库已经存在表则会报错
//        jdbcTokenRepository.setCreateTableOnStartup(true);
       return jdbcTokenRepository;
   }

   @Override
   protected void configure(HttpSecurity http) throws Exception {
     http
         // 由源码得知,在最前面的是UsernamePasswordAuthenticationFilter
         .addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
         // 定义表单登录 - 身份认证的方式
         .formLogin()
         .loginPage("/authentication/require")
         .loginProcessingUrl("/authentication/form")
         .successHandler(myAuthenticationSuccessHandler)
         .failureHandler(myAuthenticationFailureHandler)
         .and()
         // 从这里开始配置记住我的功能
         .rememberMe()
         .tokenRepository(persistentTokenRepository)
         // 新增过期配置,单位秒,默认配置写的60秒
         .tokenValiditySeconds(securityProperties.getBrowser().getRememberMeSeconds())
         .tokenValiditySeconds(60 * 5)
         // userDetailsService 是必须的。不然就报错
         .userDetailsService(userDetailsService)

从数据库获取到记住我的token后,验证成功,则通过userDetailsService获取用户信息,然后在框架中写入认证信息,完成登录;

测试:

  1. 访问/user 肯定被拦截,无权限访问
  2. 访问标准登录页:/imocc-signIn.html 记住勾选记住我的选项
  3. 退出浏览器或则重启系统
  4. 直接访问 /user

在第4步的时候会明显的感觉到系统运行缓慢,这是因为需要从数据库获取信息

在登录成功的时候会往数据库插入一条数据

INSERT INTO `imooc-demo`.`persistent_logins` (`username`, `series`, `token`, `last_used`) VALUES ('admin', 'eBbVlKvXSseasbH6yVGozQ==', '72sHS9ifyza2yTN3h0Kq7A==', '2018-08-04 14:05:31');

登录一次则会插入一条;那什么时候被删除呢?

在源码中看到:

  1. 当携带的cookie和数据库中最后一次不匹配的时候,会删除所有与该用户相关的记录
  2. 当使用logout功能退出的时候

所以该地方可能会有一些小小的问题;但是可以自己去解决这个数据过多的情况;增加定时任务多长时间清理一次等方法

源码解析

登录在验证成功之后会调用该方法
org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter#successfulAuthentication

然后委托了 org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices#onLoginSuccess

protected void onLoginSuccess(HttpServletRequest request,
    HttpServletResponse response, Authentication successfulAuthentication) {
  String username = successfulAuthentication.getName();

  logger.debug("Creating new persistent login for user " + username);

  PersistentRememberMeToken persistentToken = new PersistentRememberMeToken(
      username, generateSeriesData(), generateTokenData(), new Date());
  try {
    tokenRepository.createNewToken(persistentToken);
    addCookie(persistentToken, request, response);
  }
  catch (Exception e) {
    logger.error("Failed to save persistent token ", e);
  }
}

携带cookie访问的时候会触发这个方法
org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices#processAutoLoginCookie

里面有验证过期等的逻辑