009 Spring Boot 整合 JWT

当我们在设计 api 接口时,都会给到客户端一个凭证,凭证的实现有很多,比如 cookie-session、jwt 等;jwt 对比其他的方式一个显著特点就是他是分布式的,即不依赖于某一台 server 的实现(存储),所以当前在 RestFul Api 都几乎用 jwt 做凭证(当然也可以用他的变种,比如传输中对 jwt token 进行编码加密等)。

这里我们不讲述 jwt 的具体结构,后面有空的话,再单独写一篇,本篇只讲述 Spring boot 集成 jwt,以及基本用法。

1. JWT 简介

1.1 JWT 组成

JWT: JSON Web Token

jwt 由三部分组成:Header.Payload.Signature

jwt 格式样式: xxxx.yyyy.zzzz

  1. Header: 描述 JWT 的元数据,定义了生成签名的算法以及 Token 的类型;
  2. Payload : 用来存放实际需要传递的数据;
  3. Signature(签名) :服务器通过 Payload、Header 和一个密钥(Secret)使用 Header 里面指定的签名算法(默认是 HMAC SHA256)生成。

Header

Header 通常由两部分组成:

  • typ(Type):令牌类型,也就是 JWT。
  • alg(Algorithm) :签名算法,比如 HS256。

比如:

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

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

Payload

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

Claims 分为三种类型:

  • Registered Claims(注册声明) :预定义的一些声明,建议使用,但不是强制性的。
  • Public Claims(公有声明) :JWT 签发方可以自定义的声明。
  • Private Claims(私有声明) :JWT 签发方因为项目需要而自定义的声明,更符合实际项目场景使用。

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

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

Signature

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

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

  • Header + Payload。
  • 存放在服务端的密钥(一定不要泄露出去)。
  • 签名算法。

计算公式如下:

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

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

2. 添加依赖

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

3. 代码编写

package org.example.util;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import javax.xml.crypto.Data;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Component
@ConfigurationProperties(prefix = "jwt")
public class JWTUtils {
    private String alg;
    private String typ;
    private String secret;

    private String header;

    private Integer expireTime;

    public void setHeader(String header) {
        this.header = header;
    }

    public String getSecret() {
        return secret;
    }

    public void setSecret(String secret) {
        this.secret = secret;
    }

    public Integer getExpireTime() {
        return expireTime;
    }

    public void setExpireTime(Integer expireTime) {
        this.expireTime = expireTime;
    }

    public String generate(Map<String, Object> playout) {
        System.out.println(header);

        Algorithm algorithm = Algorithm.HMAC256(this.secret);

        Map<String, Object> headerMap = new HashMap<>();
        headerMap.put("alg", this.alg);
        headerMap.put("type", this.typ);

        return JWT.create()
                .withHeader(headerMap)
                .withPayload(playout)
                .withClaim("xxxx", "1234")
                .sign(algorithm);
    }

    public Map<String, Object> parse(String token) {
        try {

            Algorithm algorithm = Algorithm.HMAC256(this.secret);
            Map<String, Claim> claimMap =  JWT.require(algorithm).build().verify(token).getClaims();
            System.out.println(claimMap.get("xxxx"));
            System.out.println(claimMap.get("abc"));
            System.out.println(claimMap.get("abc23"));
            DecodedJWT j =  JWT.decode(token);
            String p = JWT.require(Algorithm.HMAC256(this.secret))
                    .build()
                    .verify(token)
                    .getPayload();
            System.out.println(p);
        } catch (TokenExpiredException e) {
            System.out.println("token 过期");
        } catch (SignatureVerificationException e) {
            System.out.println("token 验证失败");
        }

        return null;
    }

    @Override
    public String toString() {
        return "JWTUtils{" +
                "secret='" + secret + '\'' +
                ", header='" + header + '\'' +
                ", expireTime=" + expireTime +
                '}';
    }

    public String getAlg() {
        return alg;
    }

    public void setAlg(String alg) {
        this.alg = alg;
    }

    public String getTyp() {
        return typ;
    }

    public void setTyp(String typ) {
        this.typ = typ;
    }
}

发表评论