JWT

1、什么是JWT

JSON Web Token (JWT) 是一个开放标准 ( RFC 7519 ),它定义了一种紧凑且自包含的方式,用于在各方之间作为 JSON 对象安全地传输信息。该信息可以被验证和信任,因为它是经过数字签名的。JWT 可以使用秘密(使用HMAC算法)或使用RSAECDSA的公钥/私钥对进行签名

2、什么时候使用JWT

  • 授权:这是使用 JWT 最常见的场景。用户登录后,每个后续请求都将包含 JWT,允许用户访问该令牌允许的路由、服务和资源。单点登录是当今广泛使用 JWT 的一项功能,因为它的开销很小,并且能够轻松跨不同域使用。
  • 信息交换:JSON Web Tokens 是一种在各方之间安全传输信息的好方法。因为 JWT 可以被签名——例如,使用公钥/私钥对——你可以确定发件人就是他们所说的那样。此外,由于使用标头和有效负载计算签名,因此您还可以验证内容是否未被篡改。

3、JWT长什么样?

JWT是由三段信息构成的,将这三段信息文本用.链接一起就构成了Jwt字符串。就像这样:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

4、JWT的构成

  • 头部(header)
  • 载荷(payload, 类似于飞机上承载的物品)
  • 签证(signature)

header

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

  • 声明类型(typ),这里是jwt
  • (alg)声明加密的算法 通常直接使用 HMAC SHA256

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

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

然后将头部进行base64加密(该加密是可以对称解密的),构成了第一部分.

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

playload

载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分

  • 标准中注册的声明
  • 公共的声明
  • 私有的声明

标准中注册的声明 (建议但不强制使用) :

  • iss: jwt签发者
  • sub: jwt所面向的用户
  • aud: 接收jwt的一方
  • exp: jwt的过期时间,这个过期时间必须要大于签发时间
  • nbf: 定义在什么时间之前,该jwt都是不可用的.
  • iat: jwt的签发时间
  • jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。

公共的声明
公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.

私有的声明
私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。

定义一个payload:

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

然后将其进行base64加密,得到Jwt的第二部分。

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9

请注意,对于已签名的令牌,此信息虽然受到防篡改保护,但任何人都可以读取。除非加密,否则不要将机密信息放在 JWT 的负载或标头元素中。

signature

jwt的第三部分是一个签证信息,这个签证信息由三部分组成:

  • header (base64后的)
  • payload (base64后的)
  • secret

这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。

// javascript
var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);

var signature = HMACSHA256(encodedString, 'secret'); // TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

将这三部分用.连接成一个完整的字符串,构成了最终的jwt:

  eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

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

5、JWT应该存储在哪里

每当用户想要访问受保护的路由或资源时,用户代理应该发送 JWT,通常在使用 Bearer 模式的 Authorization 标头中。标题的内容应如下所示:

Authorization: Bearer <token>

在某些情况下,这可以是无状态授权机制。服务器的受保护路由将检查Authorization标头中的有效 JWT ,如果存在,则用户将被允许访问受保护的资源。如果 JWT 包含必要的数据,则可能会减少为某些操作查询数据库的需要,尽管情况并非总是如此。

令牌在Authorization标头中发送,因为它不使用 cookie ,所以跨源资源共享 (CORS) 不会成为问题。

6、JWT 工具类编写

导入依赖

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

工具类的编写

public class JWTUtil {
    private final static String SECRET = "ZHOUZHAO";     //签名秘钥
    private static Integer expiresTimeSeconds = 60 * 30;   //有效时长(秒)


    /**
     * 生成Token的工具类
     * @param params    payload中负载的个人信息
     * @return          返回生成的Token
     */
    public static String createToken(Map<String, Object> params) {
        //获取过期时间是多少
        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.SECOND, expiresTimeSeconds);

        return JWT.create()
                .withPayload(params)   //可以将基本信息放到claims中 payload
                .withExpiresAt(instance.getTime())   //过期时间
                .withIssuedAt(new Date())       //签证时间
                .sign(Algorithm.HMAC256(SECRET));  //设置HMAC256签名
    }

    /**
     * 验证Token并返回负载信息
     * @param token  要验证的Token字符串
     * @return 返回负载信息
     */
    public static Map<String, Claim> verifyToken(String token) {

        JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SECRET)).build();
        DecodedJWT jwt = verifier.verify(token);

        return jwt.getClaims();
    }


    /**
     * 设置过期时长,单位为秒
     * @param seconds  秒
     */
    public static void setExpiresTimeSeconds(Integer seconds) {
        expiresTimeSeconds = seconds;
    }

}

7、总结

  • 首先,前端传输账号密码
  • 后端核对用户名和密码成功后,将用户的id等其他信息作为JWT Payload(负载),将其与头部分别进行Base64编码拼接后签名,形成一个JWT(Token)
  • 后端将Token字符串作为登录成功的返回结果返回给前端。前端可以将token保存在localStoragesessionStorage上,退出登录时前端删除保存的JWT即可。
  • 前端在每次请求时将token放入HTTP Header中的Authorization位。(解决XSS和XSRF问题)
  • 后端检查是否存在,如存在验证JWT的有效性。例如,检查签名是否正确;检查Token是否过期;检查Token的接收方是否是自己
  • 验证通过后后端使用 token 中包含的用户信息进行其他逻辑操作,返回相应结果。
最后修改:2021 年 11 月 01 日 11 : 06 AM