之前几篇博客记录了一下SpringSecurity
一些基础知识,也在整合Security
那篇博客中记录了remember-me的使用方法和一些基本原理。这一篇就来记录一个和remember-me有关的一个操作,采用持久化token的方式来实现remember-me
本次记录是在原有代码上改动(实现好普通remember-me)功能的代码
小前言
以前我们实现remember-me
功能时,是直接在配置类相应方法中加入这一行代码:
http.rememberMe();
之后就会在登录页面自动生成这一个单选框。
当然如果我们需要自定义登录页的话,只需要在前端页面添加一个radio
的单选框,name为remember-me,value为true即可
问题剖析
上述的实现原理大致是用户勾选该单选框之后,Security会为我们在cookie中保存一个键值对,键为remember-me,值为系统生成的token
。这个token
是系统根据用户名和密码自动生成的,同时会设置上过期时间,只要我们在过期时间前访问页面,我们都会带上这个cookie,而系统会根据token
的值判断该用户是否已经登录。
这是有一个问题,我们每次请求时,系统都会给我们带回这个cookie信息,我们请求别的页面时,会自动带回给系统。但是如果有人恶意抓取了这个cookie信息,获得了token
那么他就可以伪造用户用户去登录了,这样就有隐患。而且还有一种隐患,token
是根据用户名和密码加密生成的,万一加密的渠道被破解了,那就等于变相把用户名和密码告诉别人了,所以我们需要采取另外一种方式来实现remember-me功能。
正文
这时我们采用持久化token
的方式来存储token。
在客户端的cookie中,仅保存一个无意义的加密串(与用户名、密码等敏感数据无关),然后在数据库中保存该加密串-用户信息的对应关系,自动登录时,用cookie中的加密串,到db中验证,如果通过,自动登录才算通过。
具体怎么做呢?
首先需要在对应数据库中创建一个表,这个表的表名和字段都是写死的,不要改动。估计可能是底层sql语句就是固定写好的。
CREATE TABLE persistent_logins (
username varchar(64) not null,
series varchar(64) not null,
token varchar(64) not null,
last_used timestamp not null,
PRIMARY KEY (series)
);
创建好表之后,在配置类中注入一个数据库连接池类:
@Autowired
private DataSource dataSource;
这里因为要持久化token,引入一个类jdbcTokenRepository
,这个类实现了一个接口persistentTokenRepository
,我们点进去这个接口:
public interface PersistentTokenRepository {
void createNewToken(PersistentRememberMeToken var1);
void updateToken(String var1, String var2, Date var3);
PersistentRememberMeToken getTokenForSeries(String var1);
void removeUserTokens(String var1);
}
看方法名字,不难发现这个接口实际上是对token进行增删改查的。平时默认Security中会自动注入一个该接口的实现类InMemoryTokenRepositoryImpl
,这个类存储token是在内存中的。就是上文介绍的实现remember-me的方法,这个时候我们需要往ioc容器中注入另外一个接口实现类JdbcTokenRepositoryImpl
,这个作用就是将token存储在数据库中。
在配置类中注入组件:
//注入持久化token的组件
@Bean
public PersistentTokenRepository persistentTokenRepository() {
JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
jdbcTokenRepository.setDataSource(dataSource);
return jdbcTokenRepository;
最后一步,指定使用的token存储器(我这么叫的.):
http.rememberMe().tokenRepository(persistentTokenRepository()).tokenValiditySeconds(1209600);
第二个方法tokenRepository(persistentTokenRepository())
就是指定token存储器,tokenValiditySeconds(1209600)
是设定过期时间。
我们登录测试一下
去数据库刚刚创建好的表中,可以看到里面已经有数据了。
至此,我们的token就存储在数据库中,这样安全性也得到了提高。