加载中...
加载中...
SpringSecurity记住我(两种方式)

SpringSecurity记住我(两种方式) 原创

SpringSecurity记住我,分为两种方式

1、基于简单加密token的方法
2、持久化记住我两种实现:基于内存的;基于数据库的


记住我功能基本原理


流程:
1、 当用户发起认证请求,会被UsernamePasswordAuthenticationFilter拦截,然后身份认证;
2、 认证成功后,在AbstracAuthenticationProcessingFilter中有个RememberMeServices接口。该接口默认实现类是NullRememberMeServices,使用数据库实现记住我,配置后会调用另一个实现抽象类AbstractRememberMeServices

对于RememberMeService负责针对每一用户生成一个Token,然后将token写入到浏览器的Cookie里面,同时会使用TokenRepository将这个token写入数据库中。将Token写入数据库时候,同时会把用户认证成功的用户名一并写入数据库(此时用户名和token是一一对应的)。

当用户下次请求的时候会经过过滤器链中的RemeberMeAuthenticationFilter(这个过滤器作用就是读取cookie中token)然后交给RemeberMeService,RemeberMeService通过TokenRepository到数据库去查询这个Token数据库里面有没有记录。如果有记录就去除用户名,取出用户名之后,就会去调用UserDetailsService,获取用户信息,然后把获取的当前用户信息放到SecurityContext里面。这样用户就直接登录上了。
对于RemeberMeAuthenticationFilter在我们的过滤器链中绿色过滤器中,他是在倒数第二个位置。前面是其他的认证,其他的认证都没法认证用户信息的时候RemeberMeAuthenticationFilter尝试去做认证。


基于简单加密token的方法

简单加密token的方法思想

摘抄自:https://www.iteye.com/blog/elim-2163997#_Toc405748626
当用户选择了记住我成功登录后,Spring Security将会生成一个cookie发送给客户端浏览器。cookie值由如下方式组成:
base64(username+":"+expirationTime+":"+md5Hex(username+":"+expirationTime+":"+password+":"+key))
 username:登录的用户名。
 password:登录的密码。
 expirationTime:token失效的日期和时间,以毫秒表示。
 key:用来防止修改token的一个key。
这样用来实现Remember-Me功能的token只能在指定的时间内有效,且必须保证token中所包含的username、password和key没有被改变才行。
需要注意的是,这样做其实是存在安全隐患的,那就是在用户获取到实现记住我功能的token后,任何用户都可以在该token过期之前通过该token进行自动登录。如果用户发现自己的token被盗用了,那么他可以通过改变自己的登录密码来立即使其所有的记住我token失效。如果希望我们的应用能够更安全一点,可以使用接下来要介绍的持久化token方式,或者不使用Remember-Me功能,因为Remember-Me功能总是有点不安全的。


记住我代码具体实现(简单加密)

http.rememberMe().rememberMeParameter("remember-me") //接收页面传过来的参数默认就是remember-me,这里可以指定更换
.rememberMeCookieName(keyName) //设置cookie的名称
.tokenValiditySeconds(1209600)
.key(
.key(key) //用于加密   

前端登录加

<input name="remember-me" type="checkbox" value="true"/>记住我




基于持久化token的方法

持久化token的方法思想

摘抄自:https://www.iteye.com/blog/elim-2163997#_Toc405748626
持久化token的方法跟简单加密token的方法在实现Remember-Me功能上大体相同,都是在用户选择了“记住我”成功登录后,将生成的token存入cookie中并发送到客户端浏览器,待到下次用户访问系统时,系统将直接从客户端cookie中读取token进行认证。

不同的是基于简单加密token的方法,一旦用户登录成功后,生成的token将在客户端保存一段时间,如果用户不点击退出登录,或者不修改密码,那么在cookie失效之前,他都可以使用该token进行登录,哪怕该token被别人盗用了,用户与盗用者都同样可以进行登录。而基于持久化token的方法采用这样的实现逻辑:

(1)用户选择了“记住我”成功登录后,将会把username、随机产生的序列号、生成的token存入一个数据库表中,同时将它们的组合生成一个cookie发送给客户端浏览器。

(2)当下一次没有登录的用户访问系统时,首先检查cookie,如果对应cookie中包含的username、序列号和token与数据库中保存的一致,则表示其通过验证,系统将重新生成一个新的token替换数据库中对应组合的旧token,序列号保持不变,同时删除旧的cookie,重新生成包含新生成的token,就的序列号和username的cookie发送给客户端。

(3)如果检查cookie时,cookie中包含的username和序列号跟数据库中保存的匹配,但是token不匹配。这种情况极有可能是因为你的cookie被人盗用了,由于盗用者使用你原本通过认证的cookie进行登录了导致旧的token失效,而产生了新的token。这个时候Spring Security就可以发现cookie被盗用的情况,它将删除数据库中与当前用户相关的所有token记录,这样盗用者使用原有的cookie将不能再登录,同时提醒用户其帐号有被盗用的可能性。

(4)如果对应cookie不存在,或者包含的username和序列号与数据库中保存的不一致,那么将会引导用户到登录页面。

从以上逻辑我们可以看出持久化token的方法比简单加密token的方法更安全,因为一旦你的cookie被人盗用了,你只要再利用原有的cookie试图自动登录一次,原有的token将失效导致盗用者不能再使用原来盗用的cookie进行登录了,同时用户可以发现自己的cookie有被盗用的可能性。

但因为cookie被盗用后盗用者还可以在用户下一次登录前顺利的进行登录,所以如果你的应用对安全性要求比较高就不要使用Remember-Me功能了。

使用持久化token方法时需要我们的数据库中拥有如下表及其表结构。

复制收展Javapublic static final String CREATE_TABLE_SQL = "create table persistent_logins (username varchar(64) not null, series varchar(64) primary key, token varchar(64) not null, last_used timestamp not null)";
  • 1

=================================

基于简单加密token的方法,当我们重启服务器后Remember Me将失效。这是因为用户的session已经丢失了。
Spring Security提供了Remember Me Toke持久化技术来解决这个问题。RememberMeServices默认实现类是TokenBasedRememberMeServices,该类将Remember Me Token存储到内存,当重启服务后,内存数据将丢失,也是无法验证用户的有效Token。
PersistentTokenBasedRememberMeServices是RememberMeServices的另一个实现,其通过PersistentTokenRepository将Remember Me Token存储到数据库。当用户再次登入,通过对比cookie和数据库就可以完成认证过程。

记住我代码具体实现(数据库)

复制收展Java/**
* 可持久化的cookie token服务
*/
@Bean
public PersistentTokenRepository persistentTokenRepository() {
JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
//InMemoryTokenRepositoryImpl tokenRepository = new InMemoryTokenRepositoryImpl();
//配置数据源,因为要在数据库中创建表保存token信息
tokenRepository.setDataSource(dataSource);
//启动的时候是否创建该表 CREATE_TABLE_SQL,这个表格是保存用户登录信息的。
//1、我们直接拷贝出sql语句,然后在数据库执行;
//2、我们不拷贝sql语句,在JdbcTokenRepositoryImpl自己执行,此时我们开启:
// 如果token表不存在,使用下面语句可以初始化 persistent_logins表;若存在,请注释掉这条语句,否则会报错。
//tokenRepository.setCreateTableOnStartup(true);
return tokenRepository;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

注意:如果token表不存在,使用下面语句可以初始化 persistent_logins表;若存在,请注释掉这条语句,否则会报错。 //tokenRepository.setCreateTableOnStartup(true);

复制收展Java@Resource(name="smsUserDetailsService")
private UserDetailsService smsUserDetailsService;

@Resource
private DataSource dataSource;

@Override
public void configure(HttpSecurity http) throws Exception {

//记住我的配置,
//问题:AbstractAuthenticationProcessingFilter中loginSuccess()调用的是NullRememberMeServices(默认实现,且方法都是空的)中loginSuccess(),走不到抽象类 AbstractRememberMeServices
// rememberMe需要的配置包含TokenRepository对象以及token过期时间
http.rememberMe().tokenRepository(persistentTokenRepository())
.tokenValiditySeconds(60 * 60 * 24)
.userDetailsService(smsUserDetailsService);

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

前端登录

<input name="remember-me" type="checkbox" value="true"/>记住我

这样就配置好了,配置挺简单。


记住我代码具体实现(内存)

基于内存的和基于数据库方式差不多,区别是

PersistentTokenRepository的实现类持久化方式不同,默认使用InMemoryTokenRepositoryImpl

 

public class InMemoryTokenRepositoryImpl implements PersistentTokenRepository {}

public class JdbcTokenRepositoryImpl extends JdbcDaoSupport implements PersistentTokenRepository {}
复制收展Javapackage org.springframework.security.web.authentication.rememberme;

import java.util.Date;

public interface PersistentTokenRepository {
void createNewToken(PersistentRememberMeToken var1);

void updateToken(String var1, String var2, Date var3);

PersistentRememberMeToken getTokenForSeries(String var1);

void removeUserTokens(String var1);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

看一看源码请查看

http://www.leixingke.com/article/detail/BBhhQ9ke  

没有更多推荐了 [去首页]
image
文章
357
原创
284
转载
73
翻译
0
访问量
199056
喜欢
47
粉丝
6
码龄
5年
资源
0

文章目录

加载中...
0
0