1. 介绍
JWT (JSON Web Token) 是目前最流行的跨域认证解决方案,是一种基于 Token 的认证授权机制。 从 JWT 的全称可以看出,JWT 本身也是 Token,一种规范化之后的 JSON 结构的 Token。
JWT 自身包含了身份验证所需要的所有信息,因此,我们的服务器不需要存储 Session 信息。这显然增加了系统的可用性和伸缩性,大大减轻了服务端的压力。
2.JWT的组成
1.标头(Header)
 2.有效载荷(Payload)
 3.签名(Signature)
Header部分:
{
"alg": "HS256",
"typ": "JWT"
}
该字段为Json格式,alg属性表示签名使用的算法,默认为HMAC SHA256(写为HS256),
 typ属性表示令牌的类型,JWT令牌统一写为JWT(默认也为JWT)。
 JSON 形式的 Header 被转换成 Base64 编码,成为 JWT 的第一部分。
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(加密)。
- 存放在服务端的密钥(不能泄露)。
- 签名算法。
HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)
算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,这个字符串就是 JWT 。
3.生成,解析Token代码
    private static  long time = 1000*60*60*24;
    private static String  signature = "admin";  //加密 ,解密需要
    /**
     * setHeaderParam -> 设置 Header头信息
     * claim -> 携带的数据
     * setSubject -> 设置主机
     * setExpiration -> 保存时间
     * setId -> 设置ID
     * signWith -> 声明算法和签名
     * compact -> 拼接
     */
    @Test
    void contextLoads() {
        JwtBuilder builder = Jwts.builder();
        final String token = builder   
                 //header
                .setHeaderParam("typ", "JWT")
                .setHeaderParam("alg", "HS256")
                //保存用户名,以及角色,注意这里最好保存非私密信息,比如密码最好不要保存
                .claim("username", "admin")
                .claim("role", "admin")
                .setSubject("admin-test")
                //设置过期时间
                .setExpiration(new Date(System.currentTimeMillis() + time))
                .setId(UUID.randomUUID().toString())
                // 设置算法及密钥  java通过signature生成签名,解密也需要signature
                .signWith(SignatureAlgorithm.HS256, signature) 
                .compact();
        System.out.println(token);
    }
       @Test
    void parse() {
        String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9" +
                ".eyJ1c2VybmFtZSI6ImFkbWluIiwicm9sZSI6ImFkbWluIiwic3ViIjoiYWRtaW4tdGVzdCIsImV4cCI6MTY" +
                "2NjYxNTM5NSwianRpIjoiOGMyZTRiZGEtYTI5Mi00NzE3LThlZjYtYjExNTI0N2ZkMzljIn0" +
                ".XLF9HPezm4w6b8LIloQz_pDemshAp0ublOfbmft2nbA";
        JwtParser jwtParser = Jwts.parser();
        //通过该签名对token进行解析,解析成为多个claim
        Jws<Claims> claimsJws = jwtParser.setSigningKey(signature_key).parseClaimsJws(token);
        System.out.println(claimsJws.toString());
        Claims claims = claimsJws.getBody();
        System.out.println("用户名 "+claims.get("username"));
        System.out.println("角色信息 "+claims.get("role"));
    }
    }

4.token在用户登录时的应用
token都是在用户登录时生成,并且进行判断,进行更新,下图简略的概要token生成,以及更新。
 


















