Spring Boot Security持久化令牌7
1.持久化令牌
1.1 原理
持久化令牌就是在基本的自动登录功能基础上,又增加了新的校验参数,来提高系统的安全性,这一些都是由开发者在后台完成的,对于用户来说,登录体验和普通的自动登录体验是一样的。
在持久化令牌中,新增了两个经过 MD5 散列函数计算的校验参数,一个是 series,另一个是 token。其中,series 只有当用户在使用用户名/密码登录时,才会生成或者更新,而 token 只要有新的会话,就会重新生成,这样就可以避免一个用户同时在多端登录,就像手机 QQ ,一个手机上登录了,就会踢掉另外一个手机的登录,这样用户就会很容易发现账户是否泄漏(之前看到松哥交流群里有小伙伴在讨论如何禁止多端登录,其实就可以借鉴这里的思路)。
持久化令牌的具体处理类在 PersistentTokenBasedRememberMeServices 中,上篇文章我们讲到的自动化登录具体的处理类是在 TokenBasedRememberMeServices 中,它们有一个共同的父类:
而用来保存令牌的处理类则是 PersistentRememberMeToken,该类的定义也很简洁命令:
public class PersistentRememberMeToken {private final String username;private final String series;private final String tokenValue;private final Date date;//省略 getter
}
这里的 Date 表示上一次使用自动登录的时间。
1.2 代码演示
接下来,我通过代码来给大家演示一下持久化令牌的具体用法。
首先我们需要一张表来记录令牌信息,这张表我们可以完全自定义,也可以使用系统默认提供的 JDBC 来操作,如果使用默认的 JDBC,即 JdbcTokenRepositoryImpl,我们可以来分析一下该类的定义:
public class JdbcTokenRepositoryImpl extends JdbcDaoSupport implementsPersistentTokenRepository {public 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)";public static final String DEF_TOKEN_BY_SERIES_SQL = "select username,series,token,last_used from persistent_logins where series = ?";public static final String DEF_INSERT_TOKEN_SQL = "insert into persistent_logins (username, series, token, last_used) values(?,?,?,?)";public static final String DEF_UPDATE_TOKEN_SQL = "update persistent_logins set token = ?, last_used = ? where series = ?";public static final String DEF_REMOVE_USER_TOKENS_SQL = "delete from persistent_logins where username = ?";
}
根据这段 SQL 定义,我们就可以分析出来表的结构,松哥这里给出一段 SQL 脚本:
CREATE TABLE `persistent_logins` (`username` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,`series` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,`token` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,`last_used` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,PRIMARY KEY (`series`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
首先我们在数据库中准备好这张表。
既然要连接数据库,我们还需要准备 jdbc 和 mysql 依赖,如下:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId>
</dependency>
然后修改 application.properties ,配置数据库连接信息:
spring.datasource.url=jdbc:mysql:///oauth2?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=123
接下来,我们修改 SecurityConfig,如下:
@Autowired
DataSource dataSource;
@Bean
JdbcTokenRepositoryImpl jdbcTokenRepository() {JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();jdbcTokenRepository.setDataSource(dataSource);return jdbcTokenRepository;
}
@Override
protected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().anyRequest().authenticated().and().formLogin().and().rememberMe().key("javaboy").tokenRepository(jdbcTokenRepository()).and().csrf().disable();
}
提供一个 JdbcTokenRepositoryImpl 实例,并给其配置 DataSource 数据源,最后通过 tokenRepository 将 JdbcTokenRepositoryImpl 实例纳入配置中。
OK,做完这一切,我们就可以测试了。
1.3 测试
我们还是先去访问 /hello
接口,此时会自动跳转到登录页面,然后我们执行登录操作,记得勾选上“记住我”这个选项,登录成功后,我们可以重启服务器、然后关闭浏览器再打开,再去访问 /hello 接口,发现依然能够访问到,说明我们的持久化令牌配置已经生效。
查看 remember-me 的令牌,如下:
这个令牌经过解析之后,格式如下:
emhqATk3ZDBdR8862WP4Ig%3D%3D:ZAEv6EIWqA7CkGbYewCh8g%3D%3D
这其中,%3D 表示 =
,所以上面的字符实际上可以翻译成下面这样:
emhqATk3ZDBdR8862WP4Ig==:ZAEv6EIWqA7CkGbYewCh8g==
此时,查看数据库,我们发现之前的表中生成了一条记录:
数据库中的记录和我们看到的 remember-me 令牌解析后是一致的。
1.4逻辑
- 首先从前端传来的 cookie 中解析出 series 和 token。
- 根据 series 从数据库中查询出一个 PersistentRememberMeToken 实例。
- 如果查出来的 token 和前端传来的 token 不相同,说明账号可能被人盗用(别人用你的令牌登录之后,token 会变)。此时根据用户名移除相关的 token,相当于必须要重新输入用户名密码登录才能获取新的自动登录权限。
- 接下来校验 token 是否过期。
- 构造新的 PersistentRememberMeToken 对象,并且更新数据库中的 token(这就是我们文章开头说的,新的会话都会对应一个新的 token)。
- 将新的令牌重新添加到 cookie 中返回。
- 根据用户名查询用户信息,再走一波登录流程。
2.二次校验
相比于上篇文章,持久化令牌的方式其实已经安全很多了,但是依然存在用户身份被盗用的问题,这个问题实际上很难完美解决,我们能做的,只能是当发生用户身份被盗用这样的事情时,将损失降低到最小。
因此,我们来看下另一种方案,就是二次校验。
二次校验这块,实现起来要稍微复杂一点,我先来和大家说说思路。
为了让用户使用方便,我们开通了自动登录功能,但是自动登录功能又带来了安全风险,一个规避的办法就是如果用户使用了自动登录功能,我们可以只让他做一些常规的不敏感操作,例如数据浏览、查看,但是不允许他做任何修改、删除操作,如果用户点击了修改、删除按钮,我们可以跳转回登录页面,让用户重新输入密码确认身份,然后再允许他执行敏感操作。
这个功能在 Shiro 中有一个比较方便的过滤器可以配置,Spring Security 当然也一样,例如我现在提供三个访问接口:
@RestController
public class HelloController {@GetMapping("/hello")public String hello() {return "hello";}@GetMapping("/admin")public String admin() {return "admin";}@GetMapping("/rememberme")public String rememberme() {return "rememberme";}
}java
- 第一个 /hello 接口,只要认证后就可以访问,无论是通过用户名密码认证还是通过自动登录认证,只要认证了,就可以访问。
- 第二个 /admin 接口,必须要用户名密码认证之后才能访问,如果用户是通过自动登录认证的,则必须重新输入用户名密码才能访问该接口。
- 第三个 /rememberme 接口,必须是通过自动登录认证后才能访问,如果用户是通过用户名/密码认证的,则无法访问该接口。
好了,我们来看下接口的访问要怎么配置:
@Override
protected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/rememberme").rememberMe().antMatchers("/admin").fullyAuthenticated().anyRequest().authenticated().and().formLogin().and().rememberMe().key("javaboy").tokenRepository(jdbcTokenRepository())java.and().csrf().disable();
}
可以看到:
- /rememberme 接口是需要 rememberMe 才能访问。
- /admin 是需要 fullyAuthenticated,fullyAuthenticated 不同于 authenticated,fullyAuthenticated 不包含自动登录的形式,而 authenticated 包含自动登录的形式。
- 最后剩余的接口(/hello)都是 authenticated 就能访问。
OK,配置完成后,重启测试,测试过程我就不再赘述了。
Spring Boot Security持久化令牌7相关推荐
- Spring Boot Security - Remember Me示例
Spring Boot Security- Remember Me Example - Websparrowhttps://www.websparrow.org/spring/spring-boot- ...
- Spring Boot + Security + MyBatis + Thymeleaf + Activiti 快速开发平台项目
项目介绍 Spring Boot + Security + MyBatis + Thymeleaf + Activiti 快速开发平台 基于 Layui 的后台管理系统模板,扩展 Layui 原生 U ...
- spring boot security学习
spring boot security(一) 配置认证和授权 通过继承WebSecurityConfigurerAdapter,可以重写其认证和授权的逻辑. @Configuration @Enab ...
- boke练习: spring boot: security post数据时,要么关闭crst,要么添加隐藏域
spring boot: security post数据时,要么关闭crst,要么添加隐藏域 http.csrf().disable(); 或者: <input name="${_cs ...
- 《深入理解 Spring Cloud 与微服务构建》第十六章 Spring Boot Security 详解
<深入理解 Spring Cloud 与微服务构建>第十六章 Spring Boot Security 详解 文章目录 <深入理解 Spring Cloud 与微服务构建>第十 ...
- Spring Boot + Security + Thymeleaf + Activiti 快速开发平台项目
点击关注公众号,实用技术文章及时了解 项目介绍 Spring Boot + Security + MyBatis + Thymeleaf + Activiti 快速开发平台 基于 Layui 的后台管 ...
- Spring Boot + Security + MyBatis + Thymeleaf + Activiti 快速开发平台
前言 项目介绍 Spring Boot + Security + MyBatis + Thymeleaf + Activiti 快速开发平台 基于Layui的后台管理系统模板,扩展Layui原生UI样 ...
- Spring Boot Security + Vue 登录成功后重定向无 Access-Control-Allow-Origin 问题解决办法
需要明确: 已正确配置跨域资源共享(CORS)(不然也不可能看到登录成功了). 已正确配置了跨站请求伪造(CSRF)(登录时需要携带 _csrf.token). 全部使用 POST 提交的数据. 本文 ...
- SpringBootAdmin集成Turbine、使用Spring Boot Security添加安全验证
SpringBootAdmin集成Turbine Hystrix Dashboard是一个监控熔断器状况的组件,而Turbine是一个可以聚合多个HystrixDashboard的组件.在Spring ...
最新文章
- tomcat访问webapp下面工程的文件含有软连接
- python多重继承
- 计算机应用技术滨江计划,杭州高新区(滨江)列出干货满满的“计划表”
- 发那科pmc地址分配_一台全新的FANUC数控机床,请简述有挡块回参功能的实现步骤?包括PMC的I/O分配、具体参数设定、梯形图程序...
- 1-2:网络初识之了解OSI和TCP/IP及网络分层(物理层,数据链路层,网路层,传输层,应用层)
- mysql筛选向导后返回原表_Navcat关于导出向导的常见问题!
- target-action设计模式--主要为Button的方法重写
- Python UDP broadcast PermissionError: [Errno 13] Permission denied
- matlab画森林图,R语言meta分析(4)网状Meta 分析
- 局域网打印机怎么连接_苹果手机怎么连打印机?苹果系统怎么连接网络打印机?一看就会...
- 2021-02-15 大数据课程笔记 day26
- VS2022-更换背景壁纸
- 人工智能专家:总结AI在医疗的发展
- Discuz!论坛教程之如何禁止/允许用户编辑自己发布的帖子
- 犯罪心理学Seasons one
- 程序员别只顾着低头写代码,了解一下2020年网络流行语吧!
- ik php分词,ik中英文混合分词器
- matlab 假设建议,matlab 假设检验
- 基于JDBC的mysql数据库连接
- 部署Node.js项目(CentOS)
热门文章
- 指针和引用的区别以及引用与指针基础
- Vue系列之vue生命周期详解
- 服务器维护王者荣耀8月4日,王者荣耀8月4日更新阿古朵上线正式服,谁说萌妹子不喜欢玩坦克?...
- 2021年P气瓶充装考试总结及P气瓶充装操作证考试
- 为table标签内的tbody添加滚动条
- 一篇实用的Latex的入门教程
- android华为手机开启蓝牙耳机,华为手机如何连接蓝牙耳机? 华为手机连接蓝牙耳机方法教程介绍!...
- StrictHostKeyChecking
- 中国运动健身人群分析报告出炉,看看哪些人在偷偷变瘦
- PPT基础(十九)布尔运算