JWT

发布时间 2023-08-29 14:36:11作者: 啄木鸟伍迪

什么是JWT:

JWT (JSON Web Token) 是目前最流行的跨域认证解决方案,是一种基于 Token 的认证授权机制。 从 JWT 的全称可以看出,JWT 本身也是 Token,一种规范化之后的 JSON 结构的 Token。

JWT 自身包含了身份验证所需要的所有信息,因此,我们的服务器不需要存储 Session 信息。这显然增加了系统的可用性和伸缩性,大大减轻了服务端的压力。

可以看出,JWT 更符合设计 RESTful API 时的「Stateless(无状态)」原则 。

并且, 使用 JWT 认证可以有效避免 CSRF 攻击,因为 JWT 一般是存在在 localStorage 中,使用 JWT 进行身份验证的过程中是不会涉及到 Cookie 的。

什么时候你应该用JSON Web Tokens

常见的API 鉴权,用户凭证:一般应用在接口认证;每次请求获取数据接口都要求传token;

适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。

下列场景中使用JSON Web Token是很有用的:

  • Authorization (授权) : 这是使用JWT的最常见场景。一旦用户登录,后续每个请求都将包含JWT,允许用户访问该令牌允许的路由、服务和资源。单点登录是现在广泛使用的JWT的一个特性,因为它的开销很小,并且可以轻松地跨域使用。
  • Information Exchange (信息交换) : 对于安全的在各方之间传输信息而言,JSON Web Tokens无疑是一种很好的方式。因为JWTs可以被签名,例如,用公钥/私钥对,你可以确定发送人就是它们所说的那个人。另外,由于签名是使用头和有效负载计算的,您还可以验证内容没有被篡改。

JWT 数据结构:

JWT 本质上就是一组字串,通过(.)切分成三个为 Base64 编码的部分:

  • Header : 描述 JWT 的元数据,定义了生成签名的算法以及 Token 的类型。

  • Payload : 用来存放实际需要传递的数据

  • Signature(签名) :服务器通过 Payload、Header 和一个密钥(Secret)使用 Header 里面指定的签名算法(默认是 HMAC SHA256)生成。

JWT 通常是这样的:xxxxx.yyyyy.zzzzz

Header

Header 通常由两部分组成:

  • typ(Type):令牌类型,也就是 JWT。

  • alg(Algorithm) :签名算法,比如 HS256。

示例:

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

JSON 形式的 Header 被转换成 Base64 编码,成为 JWT 的第一部分。

jwt中的三种算法:

  • HMAC【哈希消息验证码(对称)】:HS256/HS384/HS512
  • RSASSA【RSA签名算法(非对称)】(RS256/RS384/RS512)
  • ECDSA【椭圆曲线数据签名算法(非对称)】(ES256/ES384/ES512)

 

对称加密中: secretKey指加密密钥,用来生成签名与验签;

非对称加密中: secretKey指私钥,只用来生成签名,但是不能用来验签(验签用的是公钥);

对称加密中使用同一个密钥进行加密与解密,因此这时候公钥不能泄漏,因此对称算法只适合服务端内部使用,不适合网络第三方,

非对称加密通常服务端有私钥和公钥,使用私钥加密,使用公钥可以解密,相对来说更安全

Payload

payload也是 JSON 格式数据,其中包含了 Claims(声明,包含 JWT 的相关信息)。

Claims 分为三种类型:

  • Registered Claims(注册声明) :预定义的一些声明,建议使用,但不是强制性的。

  • Public Claims(公有声明) :JWT 签发方可以自定义的声明,但是为了避免冲突,应该在 IANA JSON Web Token Registry 中定义它们。

  • Private Claims(私有声明) :JWT 签发方因为项目需要而自定义的声明,更符合实际项目场景使用。

下面是一些常见的注册声明:

  • iss(issuer):JWT 签发方。

  • iat(issued at time):JWT 签发时间。

  • sub(subject):JWT 主题。

  • aud(audience):JWT 接收方。

  • exp(expiration time):JWT 的过期时间。

  • nbf(not before time):JWT 生效时间,早于该定义的时间的 JWT 不能被接受处理。

  • jti(JWT ID):JWT 唯一标识。

也可以自定义;

示例:

{
 "uid": "ff1212f5-d8d1-4496-bf41-d2dda73de19a",
 "sub": "1234567890",
 "name": "John Doe",
 "exp": 15323232,
 "iat": 1516239022,
 "scope": ["admin", "user"]
}

Payload 部分默认是不加密的,一定不要将隐私信息存放在 Payload 当中!!!

JSON 形式的 Payload 被转换成 Base64 编码,成为 JWT 的第二部分。

Signature

Signature 部分是对前两部分的签名,作用是防止 JWT(主要是 payload) 被篡改。

这个签名的生成需要用到:

  • Header + Payload。

  • 存放在服务端的密钥(一定不要泄露出去)。

  • 签名算法。

签名的计算公式如下:

比如使用 HMAC SHA256 算法进行签名

HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)

算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,这个字符串就是 JWT 。

在基于 JWT 进行身份验证的的应用程序中,服务器通过 Payload、Header 和 Secret(密钥)创建 JWT 并将 JWT 发送给客户端。客户端接收到 JWT 之后,会将其保存在 Cookie 或者 localStorage 里面,以后客户端发出的所有请求都会携带这个令牌。

jwt使用:

  1. 应用(或者客户端)想授权服务器请求授权。例如,如果用授权码流程的话,就是/oauth/authorize
  2. 当授权被许可以后,授权服务器返回一个access token给应用,token 就是eccodeheader.eccodepayload.signature
  3. 应用使用access token访问受保护的资源(比如:API)

token是服务端给客户端的,客户端每次请求需要在header中带上token,然服务验证,服务通过了验证,才能返回请求的数据;

java-jwt的使用

 引入jar:

 

<dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.10.3</version>
        </dependency>

 

demo:

 1 package org.muses.ssm.demo.mgt.mock.test;
 2 
 3 import java.util.Calendar;
 4 import java.util.HashMap;
 5 import java.util.Map;
 6 
 7 import com.auth0.jwt.JWT;
 8 import com.auth0.jwt.JWTCreator;
 9 import com.auth0.jwt.JWTVerifier;
10 import com.auth0.jwt.algorithms.Algorithm;
11 import com.auth0.jwt.interfaces.Claim;
12 import com.auth0.jwt.interfaces.DecodedJWT;
13 
14 /**
15  * @author lixm
16  * @date 2023/8/29 11:51
17  */
18 public class JwtUtils {
19     /**
20      * key(按照签名算法的字节长度设置key)
21      */
22     private final static String SECRET_KEY = "!34ADAS";
23     /**
24      * 过期时间(毫秒单位)
25      */
26     private final static int TOKEN_EXPIRE_MILLIS = 1000 * 60 * 60;
27 
28     public static String getToken(Map<String, String> map) {
29         // 指定token过期时间
30         Calendar calendar = Calendar.getInstance();
31         calendar.add(Calendar.MILLISECOND, TOKEN_EXPIRE_MILLIS);
32         JWTCreator.Builder builder = JWT.create();
33         Map<String, Object> head = new HashMap<String, Object>();
34         if (map != null && !map.isEmpty()) {
35             for (String key : map.keySet()) {
36                 builder.withClaim(key, map.get(key));
37             }
38         }
39         builder.withExpiresAt(calendar.getTime());
40         return builder.sign(Algorithm.HMAC256(SECRET_KEY)).toString();
41     }
42 
43     public static Map<String, Object> parseToken(String token) {
44         Map<String, Object> res = new HashMap<>();
45         try {
46             // 创建解析对象,使用的算法和secret要与创建token时保持一致
47             JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(SECRET_KEY)).build();
48             // 解析指定的token
49             DecodedJWT decodedJWT = jwtVerifier.verify(token);
50             // 获取解析后的token中的payload信息
51             Map<String, Claim> map = decodedJWT.getClaims();
52 
53             for (String key : map.keySet()) {
54                 res.put(key, map.get(key).as(Object.class));
55             }
56             // 因为有超时时间,所以可能可能超时,输出超时时间
57             System.out.println(decodedJWT.getExpiresAt());
58         } catch (Exception e) {
59             System.out.println(e.getMessage());
60         }
61 
62         return res;
63 
64     }
65 
66     public static void main(String[] args) {
67         Map<String, String> claim = new HashMap<String, String>();
68         claim.put("userId", 21 + "");
69         claim.put("userName", "baobao");
70         String token = JwtUtils.getToken(claim);
71         System.out.println(JwtUtils.getToken(claim));
72         System.out.println(JwtUtils.parseToken(token));
73     }
74 
75 }

执行结果:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6ImJhb2JhbyIsImV4cCI6MTY5MzI5NDA0MCwidXNlcklkIjoiMjEifQ._6yhDh0XV3AINv_1jkyGga9cR8UCEvYuOQqQBejKqIc
Tue Aug 29 15:27:18 CST 2023
{userName=baobao, exp=1693294038, userId=21}

说明:exp返回了时间戳;

参考地址:

https://www.cnblogs.com/cjsblog/p/9277677.html 

https://www.cnblogs.com/yanglei2022/p/16689375.html