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逻辑

  1. 首先从前端传来的 cookie 中解析出 series 和 token。
  2. 根据 series 从数据库中查询出一个 PersistentRememberMeToken 实例。
  3. 如果查出来的 token 和前端传来的 token 不相同,说明账号可能被人盗用(别人用你的令牌登录之后,token 会变)。此时根据用户名移除相关的 token,相当于必须要重新输入用户名密码登录才能获取新的自动登录权限。
  4. 接下来校验 token 是否过期。
  5. 构造新的 PersistentRememberMeToken 对象,并且更新数据库中的 token(这就是我们文章开头说的,新的会话都会对应一个新的 token)。
  6. 将新的令牌重新添加到 cookie 中返回。
  7. 根据用户名查询用户信息,再走一波登录流程。

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
  1. 第一个 /hello 接口,只要认证后就可以访问,无论是通过用户名密码认证还是通过自动登录认证,只要认证了,就可以访问。
  2. 第二个 /admin 接口,必须要用户名密码认证之后才能访问,如果用户是通过自动登录认证的,则必须重新输入用户名密码才能访问该接口。
  3. 第三个 /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();
}

可以看到:

  1. /rememberme 接口是需要 rememberMe 才能访问。
  2. /admin 是需要 fullyAuthenticated,fullyAuthenticated 不同于 authenticated,fullyAuthenticated 不包含自动登录的形式,而 authenticated 包含自动登录的形式。
  3. 最后剩余的接口(/hello)都是 authenticated 就能访问。

OK,配置完成后,重启测试,测试过程我就不再赘述了。

Spring Boot Security持久化令牌7相关推荐

  1. Spring Boot Security - Remember Me示例

    Spring Boot Security- Remember Me Example - Websparrowhttps://www.websparrow.org/spring/spring-boot- ...

  2. Spring Boot + Security + MyBatis + Thymeleaf + Activiti 快速开发平台项目

    项目介绍 Spring Boot + Security + MyBatis + Thymeleaf + Activiti 快速开发平台 基于 Layui 的后台管理系统模板,扩展 Layui 原生 U ...

  3. spring boot security学习

    spring boot security(一) 配置认证和授权 通过继承WebSecurityConfigurerAdapter,可以重写其认证和授权的逻辑. @Configuration @Enab ...

  4. boke练习: spring boot: security post数据时,要么关闭crst,要么添加隐藏域

    spring boot: security post数据时,要么关闭crst,要么添加隐藏域 http.csrf().disable(); 或者: <input name="${_cs ...

  5. 《深入理解 Spring Cloud 与微服务构建》第十六章 Spring Boot Security 详解

    <深入理解 Spring Cloud 与微服务构建>第十六章 Spring Boot Security 详解 文章目录 <深入理解 Spring Cloud 与微服务构建>第十 ...

  6. Spring Boot + Security + Thymeleaf + Activiti 快速开发平台项目

    点击关注公众号,实用技术文章及时了解 项目介绍 Spring Boot + Security + MyBatis + Thymeleaf + Activiti 快速开发平台 基于 Layui 的后台管 ...

  7. Spring Boot + Security + MyBatis + Thymeleaf + Activiti 快速开发平台

    前言 项目介绍 Spring Boot + Security + MyBatis + Thymeleaf + Activiti 快速开发平台 基于Layui的后台管理系统模板,扩展Layui原生UI样 ...

  8. Spring Boot Security + Vue 登录成功后重定向无 Access-Control-Allow-Origin 问题解决办法

    需要明确: 已正确配置跨域资源共享(CORS)(不然也不可能看到登录成功了). 已正确配置了跨站请求伪造(CSRF)(登录时需要携带 _csrf.token). 全部使用 POST 提交的数据. 本文 ...

  9. SpringBootAdmin集成Turbine、使用Spring Boot Security添加安全验证

    SpringBootAdmin集成Turbine Hystrix Dashboard是一个监控熔断器状况的组件,而Turbine是一个可以聚合多个HystrixDashboard的组件.在Spring ...

最新文章

  1. tomcat访问webapp下面工程的文件含有软连接
  2. python多重继承
  3. 计算机应用技术滨江计划,杭州高新区(滨江)列出干货满满的“计划表”
  4. 发那科pmc地址分配_一台全新的FANUC数控机床,请简述有挡块回参功能的实现步骤?包括PMC的I/O分配、具体参数设定、梯形图程序...
  5. 1-2:网络初识之了解OSI和TCP/IP及网络分层(物理层,数据链路层,网路层,传输层,应用层)
  6. mysql筛选向导后返回原表_Navcat关于导出向导的常见问题!
  7. target-action设计模式--主要为Button的方法重写
  8. Python UDP broadcast PermissionError: [Errno 13] Permission denied
  9. matlab画森林图,R语言meta分析(4)网状Meta 分析
  10. 局域网打印机怎么连接_苹果手机怎么连打印机?苹果系统怎么连接网络打印机?一看就会...
  11. 2021-02-15 大数据课程笔记 day26
  12. VS2022-更换背景壁纸
  13. 人工智能专家:总结AI在医疗的发展
  14. Discuz!论坛教程之如何禁止/允许用户编辑自己发布的帖子
  15. 犯罪心理学Seasons one
  16. 程序员别只顾着低头写代码,了解一下2020年网络流行语吧!
  17. ik php分词,ik中英文混合分词器
  18. matlab 假设建议,matlab 假设检验
  19. 基于JDBC的mysql数据库连接
  20. 部署Node.js项目(CentOS)

热门文章

  1. 指针和引用的区别以及引用与指针基础
  2. Vue系列之vue生命周期详解
  3. 服务器维护王者荣耀8月4日,王者荣耀8月4日更新阿古朵上线正式服,谁说萌妹子不喜欢玩坦克?...
  4. 2021年P气瓶充装考试总结及P气瓶充装操作证考试
  5. 为table标签内的tbody添加滚动条
  6. 一篇实用的Latex的入门教程
  7. android华为手机开启蓝牙耳机,华为手机如何连接蓝牙耳机? 华为手机连接蓝牙耳机方法教程介绍!...
  8. StrictHostKeyChecking
  9. 中国运动健身人群分析报告出炉,看看哪些人在偷偷变瘦
  10. PPT基础(十九)布尔运算