SpringSecurity实现Remember-Me实践

本文涉及的产品
密钥管理服务KMS,1000个密钥,100个凭据,1个月
简介: SpringSecurity实现Remember-Me实践

【1】基于会话技术的实现

也就是基于Cookie的实现,用户信息通过某种规则进行加密然后生成一个字符串设置为cookie。


① 登录页面

这里name="remember-me"表示“记住我”的复选框,默认key是remember-me

<form action="/user/login"  method="post">
    <input type="text" name="username" />
    <input type="text" name="password" />
    <input type="checkbox" name="remember-me" />
    <input type="submit" />
</form>

② 配置类开启记住我功能

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserDetailsService userDetailsService;
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(password());
    }
    @Bean
    PasswordEncoder password() {
        return new BCryptPasswordEncoder();
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.exceptionHandling().accessDeniedPage("/unauth.html");
        http.formLogin()
                .loginPage("/login.html") //登录页面
                .loginProcessingUrl("/user/login") // 处理登录的请求
                .defaultSuccessUrl("/test/index",true)// 登录成功后跳转路径
                .permitAll();
        http.authorizeRequests()
                .antMatchers("/static/**","/images/**","/css/**","/js/**")//可以直接放行
                .permitAll()
                .antMatchers("/findAll").hasAuthority("admin") // 用户访问findAll 必须有 admin 权限
                .antMatchers("/find").hasAnyAuthority("admin","sale") // 用户访问 find ,拥有admin或者sale之一即可
                .antMatchers("/sale/**").hasRole("sale") // 需要用户具有sale角色
                .antMatchers("/product/**").hasAnyRole("admin","product") //用户具有admin或者product角色之一即可
                .anyRequest().authenticated();//其他请求都需要认证
        http.csrf().disable(); 
    //开启记住我
        http.rememberMe();
    }
}


如下所示,提交表单时请求参数会带上remember-me: on

提交表单后响应头会设置cookie-remember-me,默认是14天有效期。其值加密规则如下所示:

   username + ":" + expiryTime + ":"
      + Md5Hex(username + ":" + expiryTime + ":" + password + ":" + key)

从这个规则也可以看出,如果用户修改了密码那么记住我功能将会自动失效。

得到signatureValue规则如下所示(Md5Hex(username + ":" + expiryTime + ":" + password + ":" + key)):


得到最终cookie值源码如下:

protected String encodeCookie(String[] cookieTokens) {
  StringBuilder sb = new StringBuilder();
  for(int i = 0; i < cookieTokens.length; ++i) {
      try {
          sb.append(URLEncoder.encode(cookieTokens[i], StandardCharsets.UTF_8.toString()));
      } catch (UnsupportedEncodingException var5) {
          this.logger.error(var5.getMessage(), var5);
      }
      if (i < cookieTokens.length - 1) {
          sb.append(":");
      }
  }
  String value = sb.toString();
  sb = new StringBuilder(new String(Base64.getEncoder().encode(value.getBytes())));
  while(sb.charAt(sb.length() - 1) == '=') {
      sb.deleteCharAt(sb.length() - 1);
  }
  return sb.toString();
}


.

此时关闭浏览器再重新打开,可直接访问受保护的请求(请求头的Cookie会带上remember-me)。


其本质就是根据某种规则生成一个加密串(串中有用户名和密码)设置为cookie,再次请求时带上该cookie。在autoLogin方法中会解密该cookie,然后根据解密中的用户信息与数据库的数据进行对比来实现自动登录。


原理部分可以查阅一下RememberMeConfigurer、TokenBasedRememberMeServices与AbstractRememberMeServices三个类。

AbstractRememberMeServices关于自动登录的处理逻辑如下:


这种方式无疑是不安全的,将用户信息通过某种加密规则生成字符串保存到浏览器是有几率被逆向的。故而spring官方推荐使用基于数据库的实现如PersistentTokenBasedRememberMeServices。

【2】基于数据库的实现

AbstractRememberMeServices的另外一个实现是PersistentTokenBasedRememberMeServices,也就是持久化token实现。其核心支撑PersistentTokenRepository又有两个实现:基于内存的InMemoryTokenRepositoryImpl和基于数据库的JdbcTokenRepositoryImpl。前者是通过维护了private final Map<String, PersistentRememberMeToken> seriesTokens = new HashMap();这样一个map来实现,后者则依赖于数据库表。


在其源码中可以看到,默认表名与列都已定义persistent_logins (username, series, token, last_used) ,并提供了创建表的策略(如果createTableOnStartup为true)。

如下所示,注入PersistentTokenRepository 。

@Autowired 
private DataSource dataSource; 
@Bean 
public PersistentTokenRepository persistentTokenRepository(){ 
  JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl(); 
  // 赋值数据源 
  jdbcTokenRepository.setDataSource(dataSource); 
  // 自动创建表,第一次执行会创建,以后要执行就要删除掉! 
  jdbcTokenRepository.setCreateTableOnStartup(true); 
  return jdbcTokenRepository; 
} 


开启记住我功能并设置tokenRepository。

@Autowired 
private PersistentTokenRepository tokenRepository;
// 开启记住我功能 
http.rememberMe() 
  .tokenRepository(tokenRepository) 
  .userDetailsService(usersService);
CREATE TABLE `persistent_logins` (
  `username` varchar(64) NOT NULL,
  `series` varchar(64) NOT NULL,
  `token` varchar(64) NOT NULL,
  `last_used` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`series`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

每次自动登录后,会根据series重新生成token并更新:

其原理示意图如下所示:


目录
相关文章
|
安全 Java 数据库
SpringSecurity 入门
Spring Security是Spring采用 `AOP`思想,基于 `servlet过滤器`实现的安全框架。它提供了完善的**认证机制**和**方法级的授权功能**。是一款非常优秀的权限管理框架。
89 0
|
1月前
|
安全 Java 应用服务中间件
认识SpringSecurity
认识SpringSecurity
26 0
认识SpringSecurity
|
2月前
|
JSON 安全 Java
SpringSecurity专题
SpringSecurity专题
|
安全 Java 数据库
SpringSecurity入门
SpringSecurity入门
105 0
|
7月前
【SpringSecurity 】SpringSecurity 自定义登录页面
【SpringSecurity 】SpringSecurity 自定义登录页面
118 0
|
安全 前端开发 Java
SpringSecurity
SpringSecurity
157 0
|
安全 Java 数据安全/隐私保护
SpringSecurity的记住我
记住我现在的情况,我们只要登录之后,关闭浏览器,再登录,就会让我们重新登录,但是很多网站的情况,就是有一个记住密码的功能,这个该如何实现呢?很简单
SpringSecurity的记住我
|
存储 安全 Java
Remember功能基本实现|学习笔记
快速学习Remember功能基本实现
Remember功能基本实现|学习笔记
|
Java Apache Spring
Apache Shiro In Easy Steps With Spring Boot(二)-Authenticator,Authorizer,Subject
Apache Shiro In Easy Steps With Spring Boot(二)-Authenticator,Authorizer,Subject
Apache Shiro In Easy Steps With Spring Boot(二)-Authenticator,Authorizer,Subject
下一篇
DataWorks