我们在登录的时候经常会有remember me的按钮,其实Spring Security也给我们提供了记住我的功能。
我们根据上一篇的架构继续我们的开发。
我们这里的remember-me的功能是直接将token存储到数据库中的,还有一种方法是将token存储到内存中,这里讲不再讲述。
简单的说,remember me其实就是我们在下次登录的时候可以不需要密码,直接访问即可,注意,这里不同于登录时设置过期时间,就是说一直登录的时候,强迫用户重新登录。这里我就在这里尝试了很多遍,为什么我设置remember-me的过期时间是10s,但是我登录成功后,等待了20s之后,还是可以继续访问。这其实是一个误区,我已经登录成功了,remember-me即使过期表示的是下一次登录的时候需要输入用户和密码,这里并不控制当前登录的强制退出。
在之前的remember-me按钮中添加remember-me按钮
<div>
记住我:<input type="checkbox" name="remember-me"/>
div>
其实这里我们可以不自动进行新增,如果我们不进行新增的话需要在声明PersistentTokenRepository的bean的时候添加
tokenRepository.setCreateTableOnStartup(true);
这个的意思是token表不存在的话,我们可以自动在数据库中创建该表,如果数据库中已经存在token表,那么就不能写,否则程序报错。
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;
这里的字段和表名是固定的。
(1)注入DataSource
@Autowired
private DataSource dataSource;
(2)新增PersistentTokenRepository的bean用来处理rememer me操作
/**
* 用来做remember me的处理
*/
@Bean
public PersistentTokenRepository persistentTokenRepository(){
JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
// 如果token表不存在,使用下面语句可以初始化该表;若存在,请注释掉这条语句,否则会报错。
// tokenRepository.setCreateTableOnStartup(true);
tokenRepository.setDataSource(dataSource);
return tokenRepository;
}
如果我们需要spring Security自动帮我们创建token表就是在这里进行添加。
上面代码的作用就是添加数据源,datasource,这里的DataSource我们没有进行显示配置,所以用的是spring boot给我们默认提供的datasource。
/**
* 定制登陆行为
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/login")
.defaultSuccessUrl("/sayHello").permitAll()
.and()
.logout().permitAll()
// 自动登录的校验
.and().rememberMe()
.tokenRepository(persistentTokenRepository())
// 声明有效时间,单位是s
.tokenValiditySeconds(60)
// 进行校验的service
.userDetailsService(userDetailsService);
// 关闭CSRF
http.csrf().disable();
}
这里我们添加了remember()功能,向其中注入了我们自己的persistentTokenRepository类,并且规定了他的过期时间,并注入我们自己实现的校验类CustomUserDetailsService。
(1)登陆成功后,在remember过期之前关闭浏览器,重新访问受限制的url,可以进行访问,因为remember me未超时
(2)登录成功后,在remember过期后关闭浏览器,重新访问受限制的url,自动跳转到登录页面,因为remember me超时
(3)登录成功后观察浏览器的cookie
(4)登录成功后,观察数据库的token表
(5)当时间过期之后,浏览器的cookie自动删除,但是数据库的token表不删除该记录
发现已经没有key是remember-me的cookie,但是数据库中该记录一直存在
(6)当logout后,正常退出后,数据库将该token记录删除。
我们这里使用数据库的持久化进行remember操作,注意这里的校验是:在客户端的Cookie中,保存一个无意义的加密串,该加密串和用户名密码都没有关系,然后在数据库中保存该加密串-用户信息的对应关系,自动登录的时候,用cookie中的加密串,到数据库中验证,如果通过自动登录才算通过。
当浏览器发起登录请求的时候,会先进入UsernamePasswordAuthenticationFilter过滤器,当认证通过后进过RememberService里面,其中的TokenRepository·,他会产生一个token,首先将token写入到浏览器cookie中,然后将token,认证成功的用户名写入到数据库中。
当浏览器下次请求时,会经过 RememberMeAuthenticationFilter,它会读取 Cookie 中的 token,交给 RememberMeService 从数据库中查询记录。如果存在记录,会读取用户名并去调用 UserDetailsService,获取用户信息,并将用户信息放入Spring Security 中,实现自动登陆。
RememberMeAuthenticationFilter 是在整个过滤链的位置比较后,所以这种登录方式是在所有传统的登录方式都无法登录的时候才会使用的自动登录。