JWT令牌 JWT概述和简单使用
1. JWT介绍
在介绍JWT之前先看一下传统校验令牌的方法,如下图:
问题:
传统授权方法的问题是用户每次请求资源服务,资源服务都需要携带令牌访问认证服务去校验令牌的合法性,并根据令牌获取用户的相关信息,性能低下。
解决:
使用JWT的思路是,用户认证通过会得到一个JWT令牌,JWT令牌中已经包括了用户相关的信息,客户端只需要携带JWT访问资源服务,资源服务根据事先约定的算法自行完成令牌校验,无需每次都请求认证服务完成授权。
JWT令牌授权过程如下图:
什么是JWT?
JSON Web Token(JWT)是一个开放的行业标准(RFC 7519),它定义了一种简介的、自包含的协议格式,用于在通信双方传递json对象,传递的信息经过数字签名可以被验证和信任。JWT可以使用HMAC算法或使用RSA的公 钥/私钥对来签名,防止被篡改。
JWT令牌的优点:
- jwt基于json,非常方便解析。
- 可以在令牌中自定义丰富的内容,易扩展。
- 通过非对称加密算法及数字签名技术,JWT防止篡改,安全性高。
- 资源服务使用JWT可不依赖认证服务即可完成授权。
缺点:
- JWT令牌较长,占存储空间比较大。
2.JWT的使用场景主要包括:
认证授权
这是比较常见的使用场景,只要用户登录过一次系统,之后的请求都会包含签名出来的token,通过token也可以用来实现单点登录。
因为它的开销很小,并且能够在不同的域中轻松使用。
交换信息
JSON Web令牌是在各方之间安全传输信息的好方法。因为JWT可以签名 - 例如,使用公钥/私钥对 - 您可以确定发件人是他们所说的人。此外,由于使用标头和有效负载计算签名,您还可以验证内容是否未被篡改。
3.无状态登录
目前我们创建了若干个微服务,假如我们在用户中心做了登陆,接下来要去商品详情页添加商品,此时就要跳转到购物车微服务,但是购物车微服务和用户微服务不是同一台tomcat,之前判断登录状态是通过tomcat session ,通过保存用户信息到tomcat session 中,那现在我们有两台不同的tomcat,所以现在跳转之后没有登录状态了,这显然是不合理的,因为用户信息无法共享,所以之前所学的登录在分布式系统中是没法使用的,那我们就来解决这个问题——无状态登录。
3.1 有状态登录
有状态服务,即服务端需要记录每次会话的客户端信息,从而识别客户端身份,根据用户身份进行请求的处理,典型的设计如tomcat中的session。
例如登录:用户登录后,我们把登录者的信息保存在服务端session中,并且给用户一个cookie值,记录对应的session。然后下次请求,用户携带cookie值来,我们就能识别到对应session,从而找到用户的信息。
缺点
- 服务端保存大量数据,增加服务端压力
- 服务端保存用户状态,无法进行水平扩展
- 客户端请求依赖服务端,多次请求必须访问同一台服务器
#####3.2 无状态登陆
微服务集群中的每个服务,对外提供的都是Rest风格的接口。而Rest风格的一个最重要的规范就是:服务的无状态性,即:
- 服务端不保存任何客户端请求者信息
- 客户端的每次请求必须具备自描述信息,通过这些信息识别客户端身份(并不是用户说自己是谁就是谁,自描述信息会在服务端生成)
带来的好处是什么呢?
- 客户端请求不依赖服务端的信息,任何多次请求不需要必须访问到同一台服务
- 服务端的集群和状态对客户端透明
- 服务端可以任意的迁移和伸缩
- 减小服务端存储压力
3.3 如何实现无状态
无状态登录的流程:
- 当客户端第一次请求服务时,服务端对用户进行信息认证(登录)
- 认证通过,将用户信息进行加密形成token,返回给客户端,作为登录凭证
- 以后每次请求,客户端都携带认证的token
- 服务的对token进行解密,判断是否有效。
流程图:
整个登录过程中,最关键的点是什么?
token的安全性
token是识别客户端身份的唯一标示,如果加密不够严密,被人伪造那就完蛋了。
采用何种方式加密才是安全可靠的呢?
我们将采用JWT + RSA非对称加密与JWT + HS256(安全程度没前者高,推荐前者)
4.JWT 的组成结构
实际的 jwt 产生的token,大概长得如下的格式
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.
eyJzdWIiOiJ0ZXN0IiwiYXVkIjpbImFwcCIsIndlYiJdLCJkYXRhIjoie1wiYWdlXCI6MjQsXCJzZXhcIjoxLFwidXNlcklkXCI6MSxcInVzZXJuYW1lXCI6XCLluIXlpKflj5RcIn0iLCJpc3MiOiJyc3R5cm8iLCJleHAiOjE1NTUzOTg5NjIsImlhdCI6MTU1NTM5ODg0Mn0.
N1PbxQ1okvw-q8TWiSX0uMx1z6QL6IEUBtOfcwP7-Mg
它是一个很长的字符串,中间用点(.)分隔成三个部分。
- Header(头部)
- Payload(负载)
- Signature(签名)
1、Header(头部)
标头通常由两部分组成:令牌的类型,即JWT,以及正在使用的签名算法,例如HMAC SHA256或RSA。
例如:
1 | { |
然后,这个JSON被编码为Base64Url,形成JWT的第一部分。
2、Payload(负载)
令牌的第二部分是有效负载,其中包含声明。声明是关于实体(通常是用户)和其他数据的声明
这个部分一般就是我们需要存贮的数据都放在这里。
JWT 规定了7个官方字段,供选用。
- iss (issuer):发布者
- sub (subject):主题
- iat (Issued At):生成签名的时间
- exp (expiration time):签名过期时间
- aud (audience):观众,相当于接受者
- nbf (Not Before):生效时间
- jti (JWT ID):编号
当然,也可以自定义字段,例如下面的** data** 字段
1 | { |
然后经过Base64Url编码,形成JSON Web令牌的第二部分
3、Signature(签名)
Signature 部分是对前两部分的签名,防止数据篡改。
首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名
1 | HMACSHA256( |
算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用”点”(.)分隔,就可以返回给用户。
4、Base64URL
前面提到,Header 和 Payload 串型化的算法是 Base64URL。这个算法跟 Base64 算法基本类似,但有一些小的不同。
JWT 作为一个令牌token,有些场合可能会放到 URL(比如 api.example.com/?token=xxx)。Base64 有三个字符+、/和=,在 URL 里面有特殊含义,所以要被替换掉:=被省略、+替换成-,/替换成_ 。这就是 Base64URL 算法
4.1JWT交互流程
流程图:
步骤翻译:
- 用户登录
- 服务的认证,通过后根据secret生成token
- 将生成的token返回给用户
- 用户每次请求携带token
- 服务端利用公钥解读jwt签名,判断签名有效后,从Payload中获取用户信息
- 处理请求,返回响应结果
因为JWT签发的token中已经包含了用户的身份信息,并且每次请求都会携带,这样服务端就无需保存用户信息,甚至无需去数据库查询,完全符合了Rest的无状态规范。
5.JWT 的加密方式
JWT签名算法中,一般有两个选择,一个采用HS256,另外一个就是采用RS256。
签名实际上是一个加密的过程,生成一段标识(也是JWT的一部分)作为接收方验证信息是否被篡改的依据。
RS256 (采用SHA-256 的 RSA 签名) 是一种非对称算法, 它使用公共/私钥对: 标识提供方采用私钥生成签名, JWT 的使用方获取公钥以验证签名。由于公钥 (与私钥相比) 不需要保护, 因此大多数标识提供方使其易于使用方获取和使用 (通常通过一个元数据URL)。
另一方面, HS256 (带有 SHA-256 的 HMAC 是一种对称算法, 双方之间仅共享一个 密钥。由于使用相同的密钥生成签名和验证签名, 因此必须注意确保密钥不被泄密。
在开发应用的时候启用JWT,使用RS256更加安全,你可以控制谁能使用什么类型的密钥。另外,如果你无法控制客户端,无法做到密钥的完全保密,RS256会是个更佳的选择,JWT的使用方只需要知道公钥。
由于公钥通常可以从元数据URL节点获得,因此可以对客户端进行进行编程以自动检索公钥。如果采用这种方式,从服务器上直接下载公钥信息,可以有效的减少配置信息
6.JWT 代码实现(HS256 方式)
JSON Web Tokens官网 Libraries里有各种语言的推荐包
https://github.com/jwtk/jjwt JWT官网里面star最多的,所以用了
6.1导入依赖
1 | <dependency> |
6.2生成Token(采用HS256)
1 |
|
6.3 解析token
1 |
|
控制台结果
1 | {jti=1231231312, iat=1584070892, sub=JWT令牌测试, iss=zhangxin, name=自定义信息} |
7.基于私钥生成jwt令牌(RSA方式)
Spring Security 提供对JWT的支持,本节我们使用Spring Security 提供的JwtHelper来创建JWT令牌,校验JWT令牌等操作。
JWT令牌生成采用非对称加密算法
1、生成密钥证书
下边命令生成密钥证书,采用RSA 算法每个证书包含公钥和私钥
1 | keytool -genkeypair -alias xckey -keyalg RSA -keypass xuecheng -keystore xc.keystore -storepass xuechengkeystore |
- Keytool 是一个java提供的证书管理工具
- alias:密钥的别名
- keyalg:使用的hash算法
- keypass:密钥的访问密码
- keystore:密钥库文件名,xc.keystore保存了生成的证书
- storepass:密钥库的访问密码
Keytool 是一个java提供的证书管理工具
查询证书信息:
1 | keytool -list -keystore xc.keystore |
删除别名
1 | keytool -delete -alias xckey -keystore xc.keystore |
2、导出公钥
openssl是一个加解密工具包,这里使用openssl来导出公钥信息。
安装 openssl:http://slproweb.com/products/Win32OpenSSL.html
安装资料目录下的Win64OpenSSL-1_1_0g.exe
配置openssl的path环境变量,本教程配置在D:\OpenSSL-Win64\bin,cmd进入xc.keystore文件所在目录执行如下命令:
1 | keytool -list -rfc --keystore xc.keystore | openssl x509 -inform pem -pubkey |
下边这一段就是公钥内容:
1 | -----BEGIN PUBLIC KEY----- |
将上边的公钥拷贝到文本文件中,合并为一行!!!
导入Pom依赖
1 | <dependency> |
7.2生成jwt令牌
1 |
|
7.3解析jwt令牌
1 |
|
运行结果
1 | {"roles":"程序员","sex":"男","name":"zhangxin","id":"123"} |
8.RSA加密
8.1 加密技术
加密技术是对信息进行编码和解码的技术,编码是把原来可读信息(又称明文)译成代码形式(又称密文),其逆过程就是解码(解密),加密技术的要点是加密算法,加密算法可以分为三类:
- 对称加密,如AES
- 基本原理:将明文分成N个组,然后使用密钥对各个组进行加密,形成各自的密文,最后把所有的分组密文进行合并,形成最终的密文。
- 优势:算法公开、计算量小、加密速度快、加密效率高
- 缺陷:双方都使用同样密钥,安全性得不到保证
- 非对称加密,如RSA
- 基本原理:同时生成两把密钥:私钥和公钥,私钥隐秘保存,公钥可以下发给信任客户端
- 私钥加密,持有私钥或公钥才可以解密
- 公钥加密,持有私钥才可解密
- 优点:安全,难以破解
- 缺点:算法比较耗时
- 不可逆加密,如MD5,SHA
- 基本原理:加密过程中不需要使用密钥,输入明文后由系统直接经过加密算法处理成密文,这种加密后的数据是无法被解密的,无法根据密文推算出明文。
8.2 没有RSA加密3
注:我们把Zuul加进来,因为Zuul是整个后台微服务的入口,所有的请求都要先经过Zuul才能到达特定的微服务,所以我们关于鉴权的流程没有必要放在每一个微服务做一次,这就太复杂了,我们放在入口Zuul的位置,只需要做一次。
- 用户请求登录
- Zuul将请求转发到授权中心,请求授权
- 授权中心校验完成,颁发JWT凭证
- 客户端请求其它功能,携带JWT
- Zuul将JWT交给授权中心校验,通过后放行
- 用户请求到达微服务
- 微服务将JWT交给鉴权中心,鉴权同时解析用户信息
- 鉴权中心返回用户数据给微服务
- 微服务处理请求,返回响应
发现什么问题了?
每次鉴权都需要访问鉴权中心,系统间的网络请求频率过高,效率略差,鉴权中心的压力较大。
8.3 RSA加密
- 我们首先利用RSA生成公钥和私钥。私钥保存在授权中心,公钥保存在Zuul和各个微服务
- 用户请求登录
- 授权中心校验,通过后用私钥对JWT进行签名加密
- 返回JWT给用户
- 用户携带JWT访问
- Zuul直接通过公钥解密JWT,进行验证,验证通过则放行
- 请求到达微服务,微服务直接用公钥解析JWT,获取用户信息,无需访问授权中心
9. JWT 的特点
互联网服务中的用户认证方式 有两种:
SessionId
比如,用户在浏览器中用户名密码登录成功之后,浏览器每次发请求都会自己带上sessionid 发送给服务器。服务器通过sessionID 得到用户信息
TOKEN
token,一般在手机app 端比较常用的方式,那就是用户登录成功之后,服务器返回一个 授权的token 字符串,app 每次请求也要带着这个token 转到服务器,服务器通过 token 得到用户信息
如上两种方式都很类似,换汤不换药。它们的相同点就是:认证信息都保存在后端服务器
而JWT 和 它们的区别就是:认证信息保存在了客户端,减轻服务端的内存压力。
9.1特点
- JWT是无状态的,特别适用于分布式站点的单点登录(SSO)场景
- JWT 不仅可以用于认证,也可以用于交换信息。有效使用 JWT,可以降低服务器查询数据库的次数。
- 因为有签名,所以JWT可以防止被篡改
- 由于JWT的payload是使用base64编码的,并没有加密,因此JWT中不能存储敏感数据。而session的信息是存在服务端的,相对来说更安全。
- 由于是无状态使用JWT,所有的数据都被放到JWT里,如果还要进行一些数据交换,那载荷会很大,经过编码之后导致JWT非常长
- JWT是一次性的。想修改里面的内容,就必须签发一个新的JWT
- 一旦签发一个JWT,在到期之前就会始终有效,无法中途废弃。
RSA公钥和私钥是什么?
首先来说,RSA是一种非对称加密算法,它是由三位数学家(Rivest、Shamir、Adleman)设计出来的。非对称加密是相对于对称加密而言的。对称加密算法是指加密解密使用的是同一个秘钥,而非对称加密是由两个密钥(公钥、私钥)来进行加密解密的,由此可见非对称加密安全性更高。
公钥顾名思义就是公开的密钥会发放给多个持有人,而私钥是私有密码往往只有一个持有人。
公私钥特性
公钥与私钥是成对出现的;
私钥文件中包含了公钥数据,所以可以基于私钥导出公钥;
密钥越长,越难破解,所以2048位密钥比1024位密钥要更安全;
公钥和私钥都是密钥,被公开的那个就是公钥,没有被公开的那个就是私钥。
公钥和私钥都可用于加密和解密
公钥和私钥都可以用于加解密操作,用公钥加密的数据只能由对应的私钥解密,反之亦然。虽说两者都可用于加密,但是不同场景使用不同的密钥来加密,规则如下:
1、私钥用于签名、公钥用于验签
签名和加密作用不同,签名并不是为了保密,而是为了保证这个签名是由特定的某个人签名的,而不是被其它人伪造的签名,所以私钥的私有性就适合用在签名用途上。
私钥签名后,只能由对应的公钥解密,公钥又是公开的(很多人可持有),所以这些人拿着公钥来解密,解密成功后就能判断出是持有私钥的人做的签名,验证了身份合法性。
2、公钥用于加密、私钥用于解密,这才能起到加密作用
因为公钥是公开的,很多人可以持有公钥。若用私钥加密,那所有持有公钥的人都可以进行解密,这是不安全的!
若用公钥加密,那只能由私钥解密,而私钥是私有不公开的,只能由特定的私钥持有人解密,保证的数据的安全性。










