springsecurity remember-me 功能
本文基于spring-security-web-4.1.2.RELEASE。
要实现rememberMe,有两种方案。
1.基于简单加密token的方法
首先需要在配置文件中加入<remember-me />,然后在登录页表单中加入复选框即可。
<input type="checkbox" name="remember-me" value="true" checked="checked"/>两周之内不必登陆
分析:
这种方式实现方式是在当用户选择了记住我成功登录后,Spring Security会判断request中是否包含remember-me参数,判断逻辑在spring-security-web包的
org.springframework.security.web.authentication.rememberme.AbstractRememberMeServices中,如下:
String paramValue = request.getParameter(parameter);
if (paramValue != null) {
if (paramValue.equalsIgnoreCase("true") || paramValue.equalsIgnoreCase("on")
|| paramValue.equalsIgnoreCase("yes") || paramValue.equals("1")) {
return true;
}
}
然后服务器将会生成一个token放入cookie(该cookie默认名称remember-me)中。token值由如下方式组成:
base64(username+":"+expirationTime+":"+md5Hex(username+":"+expirationTime+":"+password+":"+key))
- username:登录的用户名。
- password:登录的密码。
- expirationTime:token失效的日期和时间,以毫秒表示。
- key:用来防止修改token的一个key。
生成cookie的逻辑在org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices中。源码如下:
String username = retrieveUserName(successfulAuthentication);
String password = retrievePassword(successfulAuthentication); // If unable to find a username and password, just abort as
// TokenBasedRememberMeServices is
// unable to construct a valid token in this case.
if (!StringUtils.hasLength(username)) {
logger.debug("Unable to retrieve username");
return;
} if (!StringUtils.hasLength(password)) {
UserDetails user = getUserDetailsService().loadUserByUsername(username);
password = user.getPassword(); if (!StringUtils.hasLength(password)) {
logger.debug("Unable to obtain password for user: " + username);
return;
}
} int tokenLifetime = calculateLoginLifetime(request, successfulAuthentication);
long expiryTime = System.currentTimeMillis();
// SEC-949
expiryTime += 1000L * (tokenLifetime < 0 ? TWO_WEEKS_S : tokenLifetime);
//生成加密后的token
String signatureValue = makeTokenSignature(expiryTime, username, password); setCookie(new String[] { username, Long.toString(expiryTime), signatureValue },
tokenLifetime, request, response);
以后客户端再次访问受限资源时,spring-security解码名为remember-me的cookie,获得有效期限和username,判断后自动在系统中认证,从而实现免登录。
这样做是存在安全隐患的,那就是在用户获取到实现记住我功能的cookie后,任何用户都可以在该cookie过期之前通过该cookie进行自动登录,即是说无法防范cookie被盗用后带来的风险。
如果希望我们的应用能够更安全一点,可以使用接下来要介绍的持久化token方式,或者不使用Remember-Me功能,因为Remember-Me功能总是有点不安全的。
2.基于persistent(持久化)token的方法
最简单的实现方式如下:
a.配置文件:
<!-- token有两种持久化方案,一种基于内存(InMemoryTokenRepositoryImpl),一种基于数据库(JdbcTokenRepositoryImpl)-->
<!-- 这里我们选择基于数据库的方式,需要注入dataSource(请自行配置DataSource)--> <remember-me data-source-ref="dataSource"/>
b.数据库中插入表 persistent_logins,插入表的语句是 org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl 提供的,其中还定义了固定的sql语句来查询token。请自行阅读源码。
create table persistent_logins (username varchar(64) not null default '', series varchar(64) primary key, token varchar(64) not null , last_used timestamp not null)
c.在登录页表单中加入复选框
<input type="checkbox" name="remember-me" value="true" checked="checked"/>两周之内不必登陆
这样就完成了持久化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有被盗用的可能性。
另外,可以通过复写 类org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl 并配置<remember-me token-repository-ref=''/>来应用自定义的数据库表,相信看过源码的同学们都做得到。
springsecurity remember-me 功能的更多相关文章
- 解决SpringSecurity阻止ajax的POST和PUT请求,导致403Forbidden的问题
前言: 最近在整合springboot+springsecurity,在PUT请求的时候出现了403的问题,这里记录一下解决的过程 到Spring的官网去查查SpringSecurity的参考手册看看 ...
- 如何使用Spring Securiry实现前后端分离项目的登录功能
如果不是前后端分离项目,使用SpringSecurity做登录功能会很省心,只要简单的几项配置,便可以轻松完成登录成功失败的处理,当访问需要认证的页面时,可以自动重定向到登录页面.但是前后端分离的项目 ...
- SpringSecurity 初始化流程源码
SpringSecurity 初始化流程源码 本篇主要讲解 SpringSecurity初始化流程的源码部分,包括核心的 springSecurityFilterChain 是如何创建的,以及在介绍哪 ...
- 从零学习SpringSecurity
一.简介 SpringSecurity是一个功能强大且高度可定制的身份验证和访问控制框架,和spring项目整合更加方便. 二.核心功能 认证(Authentication):指的是验证某个用户能否访 ...
- web应用安全框架选型:Spring Security与Apache Shiro
一. SpringSecurity 框架简介 官网:https://projects.spring.io/spring-security/ 源代码: https://github.com/spring ...
- Apache Shiro简单介绍
1. 概念 Apache Shiro 是一个开源安全框架,提供身份验证.授权.密码学和会话管理.Shiro 框架具有直观.易用等特性,同时也能提供健壮的安全性,虽然它的功能不如 SpringSecur ...
- springboot security 安全
spring security几个概念 “认证”(Authentication) 是建立一个他声明的主体的过程(一个“主体”一般是指用户,设备或一些可以在你的应用程序中执行动作的其他系统) . “授权 ...
- 【SpringCloud微服务实战】搭建企业级应用开发框架(一):架构说明
SpringCloud分布式应用微服务系统架构图: SpringCloud分布式应用微服务系统组件列表: 微服务框架组件:Spring Boot2 + SpringCloud Hoxton.SR8 + ...
- SpringSecurity之记住我功能的实现
Spring security记住我基本原理: 登录的时候,请求发送给过滤器UsernamePasswordAuthenticationFilter,当该过滤器认证成功后,会调用RememberMeS ...
随机推荐
- RAR和ZIP:压缩大战真相 (挺赞值得了解)
前言--王者归来? 等待足足两年之久,压缩霸主WinZip终于在万众期待下发布了9.0正式版.全世界自然一片沸腾,在世界各大知名下载网站中,WinZip9.0再次带起下载狂潮.然而此时国内并没有王者回 ...
- php addslashes和stripslashes函数
addslashes — 使用反斜线引用字符串 stripslashes — 反引用一个引用字符串 Example #1 一个 addslashes() 例子 <?php$str = &qu ...
- 解决zabbix使用中文是出现乱码的问题
解决zabbix使用中文是出现乱码的问题 ①:上传windows的simhei.ttf字体到zabbix服务器的/usr/share/zabbix/fonts/目录下 ②:编辑/usr/sh ...
- 【BioCode】根据seq与位点信息截取窗口
代码说明 sequence24371.txt 以上为所有氨基酸的编号,序列,与位点标记.根据标记为“1”的位点,截取窗口:如下(实验结果): 图示为一个窗口为12的蛋白质片段 2N+1=25: 实现代 ...
- POSt 提交参数 实体 和字符串
//1.后台接受是 字符串形式 [HttpPost] public int SendTaxiAudioByWX(string userid, string suid, string indexno, ...
- shell 一些符号的使用
给你个全的,你在Linux环境下多试下就明白了:$0 这个程式的执行名字$n 这个程式的第n个参数值,n=1..9$* 这个程式的所有参数,此选项参数可超过9个.$# 这个程式的参数个数$$ 这个程式 ...
- C#和Java在多态情况下对成员访问的比较
本文简单比较一下两种语言在里氏替换原则下,父类引用变量访问成员时的访问结果: 如果有两个类,如Person与Student,后者继承了前者,而且子类与父类有重名成员,当Person p = new S ...
- idea导出包含main函数的jar
1.首先打开File->project stucture->Artifacts 2.按照下图方式: 3.选择面main函数的所在的类,选择MAINFEST.MF问的生成路径 这里一定选择 ...
- 【bzoj2300】[HAOI2011]防线修建 离线+STL-set维护凸包
题目描述 给你(0,0).(n,0).(x,y)和另外m个点,除(0,0)(n,0)外每个点横坐标都大于0小于n,纵坐标都大于0. 输入 第一行,三个整数n,x,y分别表示河边城市和首都是(0,0), ...
- 学习Python最好的方法就是实践和教程并行,以下有一些资源和教程,还有一些学习思维导图:
1.Python 的 14 张思维导图下载地址: https://woaielf.github.io/2017/06/13/python3-all/ 2.Python基础教程|菜鸟教程: http:/ ...