JWT入门简介

JWT入门简介

官网:https://jwt.io/
文档:https://jwt.io/introduction/

目录

什么是JWT

JWT是“JSON Web Token”的英文缩写,是一种开放的工业标准方法(RFC 7519),用于在网络应用环境中安全地传递声明信息。虽然JWT的名称中包含一个单词“JSON”,但是JWT本身并不是JSON格式的(组成JWT的各个部分是JSON格式的)。实际上,JWT由三段信息构成,将这三段信息文本用.链接一起就构成了JWT字符串:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaXNzIjoiaXNzMCIsIm5hbWUiOiJ6aGFuZ3NhbiIsImFkbWluIjp0cnVlfQ.eNKsQ89xab7Za5P9uywqPvAiYZIHK1dwS0h8rRW9sVM

第一部分为头部(Header),第二部分为载荷(Payload),第三部分为签名(Signature)。

头部(Header)

JWT的头部承载两部分信息:

声明类型,值为JWT

声明加密的算法,可以使用不同的签名算法,如:HS256,HS384,HS512等等,不同的实现库所能支持的算法也尽不相同

完整的头部就像下面这样的JSON:

{ "typ": "JWT", "alg": "HS256" }

然后将头部进行Base64编码就构成了第一部分:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9。

载荷(Payload)

载荷就是存放声明信息的地方(通常可以将登录的用户信息存放在这里),包含2个部分:

公共声明

私有声明

公共声明中可以包含如下信息(建议但不强制使用):

iss: jwt签发者

sub: jwt所面向的用户

aud: 接收jwt的一方

exp: jwt的过期时间,这个过期时间必须要大于签发时间

nbf: 定义在什么时间之前,该jwt都是不可用的

iat: jwt的签发时间

jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击

私有声明中可以声明一些与业务相关的信息,但是一般不建议存放敏感信息,因为Base64编码值是可以解码的,意味着该部分信息可以归类为明文信息,存放敏感信息不安全。

一个Payload示例如下:

{ "iss": "iss0" "sub": "1234567890", "name": "zhangsan", "admin": true }

显然,在上述定义的Payload中,name和admin都属于自定义声明信息。然后将其进行Base64编码,得到JWT的第二部分:eyJzdWIiOiIxMjM0NTY3ODkwIiwiaXNzIjoiaXNzMCIsIm5hbWUiOiJ6aGFuZ3NhbiIsImFkbWluIjp0cnVlfQ。

签名(Signature)

JWT的第三部分是一个签名信息,这个部分需要Base64编码后的Header和Base64编码后的Payload使用.连接组成字符串,然后通过Header中声明的加密方式进行加盐secret组合加密并将加密结果进行Bas464编码,就是构成了JWT的第三部分:eNKsQ89xab7Za5P9uywqPvAiYZIHK1dwS0h8rRW9sVM。如下为计算签名值的完整示例:

public static void main(String[] args) throws InvalidKeyException { // 计算得到Base64编码后的Header值 String header = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9"; // 计算得到Base64编码后的Payload值 String payload = "eyJzdWIiOiIxMjM0NTY3ODkwIiwiaXNzIjoiaXNzMCIsIm5hbWUiOiJ6aGFuZ3NhbiIsImFkbWluIjp0cnVlfQ"; String secret = "secret"; String encodeStr = header + "." + payload; // 对Base64编码后的Header和Base64编码后的payload进行HMAC256加盐加密,得到JWT的第三部分签名信息 String signature = HMACSHA256(encodeStr.getBytes(), secret.getBytes()); System.out.println(signature); } // 使用HMAC256加密 public static String HMACSHA256(byte[] data, byte[] key) throws InvalidKeyException { try { SecretKeySpec signingKey = new SecretKeySpec(key, "HmacSHA256"); Mac mac = Mac.getInstance("HmacSHA256"); mac.init(signingKey); return new BASE64Encoder().encode(mac.doFinal(data)); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } return null; }

注意: secret是保存在服务器端的,JWT的签发生成也是在服务器端的,secret用于进行JWT的签发和验证。所以,它是服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。

JWT使用场景

JWT主要解决的是在网络中安全地传递用户信息,因此可应用在如下场景:
1.在REST接口中保存用户信息,实现API接口的访问授权。
用户登录之后将信息(如:user_id)编码到JWT字符串中传递给客户端,这样服务端就不用再保存登录用户信息了,便于服务端分布式扩容。另外,还可以直接使用JWT的公共声明实现访问控制(如通过exp声明实现访问失效,jti声明实现一次性token等等)。
2.分布式站点的单点登录(SSO)。
实现原理是将JWT字符串作为响应Cookie的一部分返回给浏览器客户端,这样JWT就可以在相同主域的多个站点之后传递,从而实现分布式站点的单点登录。注意,在这里必须使用HttpOnly属性来防止Cookie被JavaScript读取,从而避免XSS攻击。

如何传递JWT

理论上,在基于HTTP协议的应用中可以有如下几种传递方式:

在HTTP消息头中传递,如:Authorization: \'Bearer \' + header.body.signature

在Cookie中传递,如:Set-Cookie: jwt=header.body.signature; HttpOnly;domain=.lenovo.com

在消息体中传递:jwt=header.body.signature,但通常不应该这么做

JWT应用实践 手动签发JWT

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zzppyg.html