SpringBootSecurity学习(13)前后端分离版之JWT
JWT 使用
前面简单介绍了把默认的页面登录改为前后端分离的接口异步登录的方法,可以帮我们实现基本的前后端分离登录功能。但是这种基本的登录和前面的页面登录还有一个一样的地方,就是使用session和cookie来维护登录状态,这种方法的问题在于,扩展性不好。单机当然没有问题,如果是服务器集群,或者是跨域的服务导向架构,就要求 session 数据共享,每台服务器都能够读取 session。
一种解决方案是 session 数据持久化,写入redis或别的持久层。各种服务收到请求后,都向持久层请求数据。这种方案的优点是架构清晰,缺点是工程量比较大。另外,持久层万一挂了,就会单点失败。
另一种方案是服务器索性不保存 session 数据了,所有数据都保存在客户端,每次请求都发回服务器。JWT 就是这种方案的一个代表。关于JWT的理论知识,建议参考 阮一峰 大神写的教程 :JSON Web Token 入门教程,这是我认为可能是写的最清晰的一个,下面的jwt的实现也是根据此教程来实现。
具体的理论知识可以参考教程,这里简单说下流程,用户登录成功后,在header中返回用户一个token信息,这个信息里面包含了加密的用户信息和数字签名,最重要的还有过期时间,客户端接到后,每次访问接口header中都带着这个token,服务端验证成功后就表示处于登录状态,过期后再从新获取即可。
具体的token内容包含了头部(加密信息),载体(用户信息),签名(签名两个部分的前面)三大块,三大块之间用英文句号(也就是 ".")连接起来,组成一个完整的token信息
流程设计
根据前面的理论知识,我们来设计一下如何使用jwt。首先我们使用jwt,就可以不再使用session和cookie,所以第一步就是:
- 在security配置文件中配置session为无状态。
然后考虑构建jwt消息体,有三个部分,第一个部分就是头部,内容是加密类型:

上面代码中,alg属性表示签名的算法(algorithm),默认是 HMAC SHA256(写成 HS256);typ属性表示这个令牌(token)的类型(type),JWT 令牌统一写为JWT,最后,将上面的 JSON 对象使用 Base64URL 算法转成字符串,作为第一部分。所以第二步就是:
- 在security配置文件中配置session为无状态。
- 确定header信息格式
下一步确定第二部分,消息载体(Payload),这也是一个json对象,用来存放实际需要传递的数据。JWT 规定了7个官方字段,供选用:

当然除了这些还可以加一些其它内容,比如用户信息,这个 JSON 对象也要使用 Base64URL 算法转成字符串,所以第三步和第四步就是:
- 在security配置文件中配置session为无状态。
- 确定header信息格式
- 确定消息体
- 使用 HMAC SHA256 算法 对header和消息体进行签名作为第三部分
现在token的消息基本组合完成了,用户登录成功和客户端访问接口,都要把token放在header里面,名字是 Authorization 。所以最后一步就是,客户端正常访问非登录等接口时,验证token的合法性,所以,总体设计流程如下:
- 在security配置文件中配置session为无状态。
- 确定header信息格式
- 确定消息体
- 使用 HMAC SHA256 算法 对header和消息体进行签名作为第三部分
- 添加过滤器,验证token合法性
修改配置类
上面的流程设计完了,下面我们按照流程修改项目,首先修改security配置类:

配置完后,启动项目,访问登录,登录成功后可以看到,没有任何cookie保存下来。
定义JWT工具类
首先来定义几个常量:

然后定义Base64URL 算法编码和解码方法:

然后定义HmacSHA256 加密算法和获取签名的方法:

最后来设计一个简单验证token的方法:

这样jwt工具类就设计好了,目前这几个方法足够操作token内容。
定义JWT消息对象
下面来定义jwt的内容,其实内容很简单,就三个部分,因此,定义三个字段即可:

来看一下构造方法,

这个构造方法很便捷,使用它创建对象以后,jwt的三个部分基本都完成了,header部分和payload部分都编码了,签名也完成了,因此下面重写toString方法直接可以生成token:

从这里可以看出,token整体默认是不加密,但也是可以加密的。生成原始 Token 以后,可以用密钥再加密一次。因此不要把密码等重要信息放入token。
修改登录成功处理器
用户登录成功后,不再把session发给用户,而是把jwt发送给用户,因此修改登录成功处理器如下:

注意上面手动把用户的密码信息设置为null。这里为了方便,直接使用fastjson组合对象。
修改实体类
带着token访问接口的时候,需要把token转回登录用户对象,因此我们的用户实体类和token中带的字段名字一致,来修改一下,先看角色实体类:

再看用户实体类:

可以看到,基本的原则就是修改的名字和父类的必要字段名字一致就行,这也是建议的字段名字。
编写token验证过滤器
我们把security的session改为无状态后,虽然不再传递session,但是security的过滤器并没有失效,因此造成的效果就是登录成功后,访问接口显示未登录。现在我们使用token就要在登录前加一个验证token的过滤器,验证通过后直接把信息放到SecurityContextHolder中。这样每次登录靠验证token来判断是否登录,不再靠session。来看这个过滤器:

这个过滤器很简单,继承了 GenericFilterBean 类,直接获取token,判断token不为空,验证token,并从token的payload中取出用户信息,放入SecurityContextHolder中,验证失败或者token过期直接返回token错误。逻辑很简单。
最后在security类中,把这个过滤器配置到前面:

这样我们自定义的jwt流程就完成了。可以在postman中测试一下,首先是登录:

登录成功后,可以看到header中放着token的信息,然后使用token放入另一个接口的header中访问接口,可以看到访问成功:

有兴趣的可以debug跟踪一下流程。
JWT的几个特点
(1)JWT 默认是不加密,但也是可以加密的。生成原始 Token 以后,可以用密钥再加密一次。
(2)JWT 不加密的情况下,不能将秘密数据写入 JWT。
(3)JWT 不仅可以用于认证,也可以用于交换信息。有效使用 JWT,可以降低服务器查询数据库的次数。
(4)JWT 的最大缺点是,由于服务器不保存 session 状态,因此无法在使用过程中废止某个 token,或者更改 token 的权限。也就是说,一旦 JWT 签发了,在到期之前就会始终有效,除非服务器部署额外的逻辑。
(5)JWT 本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT 的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证。
(6)为了减少盗用,JWT 不应该使用 HTTP 协议明码传输,要使用 HTTPS 协议传输。
代码地址:https://gitee.com/blueses/spring-boot-security 14
SpringBootSecurity学习(13)前后端分离版之JWT的更多相关文章
- Jeecg-Boot前后端分离版
Jeecg-Boot前后端分离版http://www.jeecg.org/forum.php?gid=229 Jeecg-Boot 快速开发平台http://boot.jeecg.org/user/l ...
- 前后端分离,简单JWT登录详解
前后端分离,简单JWT登录详解 目录 前后端分离,简单JWT登录详解 JWT登录流程 1. 用户认证处理 2. 前端登录 3. 前端请求处理 4. 后端请求处理 5. 前端页面跳转处理 6. 退出登录 ...
- SpringBootSecurity学习(12)前后端分离版之简单登录
前后端分离 前面讨论了springboot下security很多常用的功能,其它的功能建议参考官方文档学习.网页版登录的形式现在已经不是最流行的了,最流行的是前后端分离的登录方式,前端单独成为一个项目 ...
- 从壹开始前后端分离[.NetCore] 37 ║JWT完美实现权限与接口的动态分配
缘起 本文已经有了对应的管理后台,地址:https://github.com/anjoy8/Blog.Admin 哈喽大家好呀!又过去一周啦,这些天小伙伴们有没有学习呀,已经有一周没有更新文章了,不过 ...
- 前后端分离java、jwt项目进行CORS跨域、解决非简单请求跨域问题、兼容性问题
情况描述: 最近在部署一个前后端分离的项目出现了跨域问题*, 项目使用jwt进行鉴权,需要前端请求发起携带TOKEN的请求*,请求所带的token无法成功发送给后端, 使用跨域后出现了兼容性问题:Ch ...
- 前后端分离下用jwt做用户认证
0 前后端分离下的用户信息认证 前端使用Vue+axios,后端使用SpringBoot+SpringSecurity. 为了解决http无状态的问题,我采用jwt(json web token)保存 ...
- SpringBootSecurity学习(15)前后端分离版之 OAuth2.0简单示例
OAuth2.0 OAuth 引入了一个授权层,用来分离两种不同的角色:客户端和资源所有者.客户端来申请资源,资源所有者同意以后,资源服务器可以向客户端颁发令牌.客户端通过令牌,去请求数据.也就是说, ...
- SpringBootSecurity学习(14)前后端分离版之 OAuth2.0介绍
登录总结 前面基本介绍了security的常规用法,同时介绍了JWT和它的一个简单实现,基本上开发中遇到的登录问题都能解决了,即使在分布式开发,或者微服务开发中实现登录也基本没有问题了.securit ...
- SpringBootSecurity学习(17)前后端分离版之 OAuth2.0 数据库(JDBC)存储客户端
自动批准授权码 前面我们授权的流程中,第一步获取授权码的时候,都会经历一个授权是否同意页面: 这个流程就像第三方登录成功后,提问是否允许获取昵称和头像信息的页面一样,这个过程其实是可以自动同意的,需要 ...
随机推荐
- JSON转换方法解析
JSON.parse() 与 JSON.stringify() 的区别 JSON.parse() :是从一个字符串中解析出 json 对象 JSON.stringify():是从一个对象中解析出字符串 ...
- Reactive(2) 响应式流与制奶厂业务
目录 再谈响应式 为什么Web后端开发的,对 Reactive 没有感觉 Java 9 支持的 Reactive Stream 范例 小结 扩展阅读 再谈响应式 在前一篇文章从Reactive编程到& ...
- sed一些常用命令
[转] http://blog.chinaunix.net/uid-20754793-id-177657.html 下面是我学习sed时参照参考书总结的一些常用sed命令,基本上每条语句都进行了调试1 ...
- 【Offer】[57-2] 【和为S的连续正数序列】
题目描述 思路分析 测试用例 Java代码 代码链接 题目描述 输入一个正数s,打印出所有和为s的连续正数序列(至少含有两个数).例如,输入15,由于1+2+3+4+5=4+5+6=7+8=15,所以 ...
- 【Offer】[18-1] 【在O(1)时间内删除链表节点】
题目描述 思路分析 测试用例 代码链接 题目描述 给定单向链表的头指针和一个结点指针,定义一个函数在O(1)时间删除该结点. 思路分析 一般我们删除单链表中的节点是需要遍历链表,找到要删除节点的前一个 ...
- Zabbix4.2对IIS监控摸索记录
Zabbix是很强大,但是相关的细节技术文档貌似很少,摸索之路就显得异常难. 度娘搜了下,关于Zabbix对IIS的监控资料确实有,确实也讲如何操作了,但是细细按照对方的要求操作下,总是缺数据,no ...
- 【LeetCode】DFS 总结
DFS(深度优先搜索) 常用来解决可达性的问题. 两个要点: 栈:用栈来保存当前节点信息,当遍历新节点返回时能够继续遍历当前节点.可以使用递归栈. 标记:和 BFS 一样同样需要对已经遍历过的节点进行 ...
- 【LeetCode】5# 最长回文子串
题目描述 给定一个字符串 s,找到 s 中最长的回文子串.你可以假设 s 的最大长度为 1000. 示例 1: 输入: "babad" 输出: "bab" 注意 ...
- Jenkins流水线(pipeline)实战之:从部署到体验
关于Jenkins流水线(pipeline) Jenkins 流水线 (pipeline) 是一套插件,让Jenkins可以实现持续交付管道的落地和实施. 关于blueocean Blue Ocean ...
- kafka对消费者分配分区规则(Java源码)
在上一篇 kafka topic消息分配partition规则(Java源码) 我们对生产者产生的消息分配partition规则进行了分析,那么本章我们来看看消费者是怎么样分配partition的. ...