JWT(Json Web Token)

1.JWT(Json Web Token)

        JSON Web token简称JWT(令牌字符串), 是用于对应用程序上的用户进行身份验证的标记。JWT令牌由Header(头部)、Payload(负载)和 Signature(签名)三部分组成,本质就是一个字符串每部分中间使用点(.)分隔,比如:xxxxx.yyyyy.zzzzz

  • Header(头部)

头部包括令牌的类型(即JWT)及使用的哈希算法(如HMAC、SHA256或RSA),主要声明了JWT的签名算法。

  • Payload(负载)

第二部分是负载,内容也是一个Json对象,它是存放有效信息的地方,它可以存放JWT提供的现成字段,比 如:iss(签发者),exp(过期时间戳), sub(面向的用户)等,也可自定义字段。 此部分不建议存放敏感信息,因为此部分可以解码还原原始内容。主要承载了各种声明并传递明文数据。

  • Signature(签名)

第三部分是签名,此部分用于防止JWT内容被篡改。 这个部分使用base64url将前两部分进行编码,编码后使用点(.)连接组成字符串,最后使用header中声明 签名算法进行签名。

Signature(签名)是一个签证信息,这个签证信息由三部分组成:

        base64UrlEncode(header):JWT令牌的第一部分。

        base64UrlEncode(payload):JWT令牌的第二部分。

        secret:签名所使用的密钥(私钥)。

JWT就是一个字符串,经过加密处理与校验处理的字符串,形式为:A.B.C

  • A由JWT头部信息header编码(解码)得到,是一个Json 字符串,包含jwt的版本,签名算法

  • B由JWT用到的身份验证信息Json数据编码(解码)得到 叫载荷 自定义存储的数据

  • C由A和B+秘钥加密得到,是校验部分 signature 验证header与playload有没有被修改过

 JwtUtils工具类

package com.commons.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.apache.commons.lang3.StringUtils;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class JwtUtils {

    // TOKEN的有效期1小时(S)
    private static final int TOKEN_TIME_OUT = 1 * 3600;

    // 加密KEY
    private static final String TOKEN_SECRET = "jwtToken";


    // 生成Token
    public static String getToken(Map params){
        long currentTime = System.currentTimeMillis();
        return Jwts.builder()
                // 参数1:算法名;参数2:加盐
                .signWith(SignatureAlgorithm.HS512, TOKEN_SECRET) //加密方式
                // 设置token有效期时间
                .setExpiration(new Date(currentTime + TOKEN_TIME_OUT * 1000)) //过期时间戳
                // 参数:map集合
                .addClaims(params)
                .compact();
    }


    /**
     * 获取Token中的claims信息
     */
    public static Claims getClaims(String token) {
        return Jwts.parser()
                .setSigningKey(TOKEN_SECRET)
                .parseClaimsJws(token).getBody();
    }


    /**
     * 是否有效 true-有效,false-失效
     */
    public static boolean verifyToken(String token) {
        if(StringUtils.isEmpty(token)) {
            return false;
        }
        try {
            Claims claims = Jwts.parser()
                    .setSigningKey("jwtToken")
                    .parseClaimsJws(token)
                    .getBody();
        }catch (Exception e) {
            return false;
        }
        return true;
    }
}

JWT的主要目的是在服务端和客户端之间以安全的方式来转移声明。

主要的应用场景如下所示:

  1. 认证 Authentication;
  2. 授权 Authorization;
  3. 联合识别;
  4. 客户端会话(无状态的会话);
  5. 客户端机密。

2.JWS、JWE、JWK、JWKset、JWA和nonsecure JWT区别

JWS

JSON Web Signature(签名) 拥有Signature(签名)部分的JWT被称为JWS,也就是 JWT签名的JWS,也就是JWT Signature

JWE

JSON Web Encryption(加密) JWT部分payload经过加密的JWT

JWK

JSON Web Key JWT的密钥(私钥),也就是我们常说的 scret

JWKset

JSON Web Key set JWT key set在非对称加密中,需要的是密钥对而非单独的密钥,在后文中会阐释

JWA

JSON Web Algorithms(算法) 当前JWT所用到的密码学算法

nonsecure JWT

当头部的签名算法被设定为none的时候,该JWT是不安全的;因为签名的部分空缺,所有人都可以修改 没有Signature(签名)部分的JWT被称为nonsecure JWT,也就是不安全的JWT

JJWT

用于在JVM上创建和验证JSON Web令牌(JWTs)的库

是基于JWT、JWS、JWE、JWK和JWA  RFC规范的Java实现(是提供端到端的JWT创建和验证的Java库)

3.JWT用户认证工作原理

        1.客户端使用用户名和密码请求登录
        2.服务端收到请求,验证用户名和密码
        3.验证成功后,服务端会签发一个token,再把这个token返回给客户端
        4.客户端收到token后可以把它存储起来,比如放到cookie中
        5.客户端每次向服务端请求资源时需要携带服务端签发的token,可以在cookie或者header中携带
        6.服务端收到请求,然后去验证客户端请求里面带着的token,如果验证成功,就向客户端返回请求数据

4.实现JWT生成token


4.1在Maven工程中pom.xml中添加JWT相关依赖

<!--JWT令牌-->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

4.2JWT工具类  

import com.SystemConstants;
import io.jsonwebtoken.*;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.*;

public class AppJwtUtil {

    // TOKEN的有效期时间(S)
    private static final int TOKEN_TIME_OUT = 3600;
    // 加密KEY
    private static final String TOKEN_ENCRY_KEY = "MDk4ZjZiY2Q0NjIxZDM3M2NhZGU0ZTgzMjYyN2I0ZjY";
    // 最小刷新间隔(S)
    private static final int REFRESH_TIME = 300;

    // 生产ID
    public static String createToken(Long id) {
        Map<String, Object> claimMaps = new HashMap<>();
        claimMaps.put("id", id);
        long currentTime = System.currentTimeMillis();
        return Jwts.builder()
                .setId(UUID.randomUUID().toString())
                .setIssuedAt(new Date(currentTime))  //签发时间
                .setSubject("system")  //说明
                .setIssuer("it") //签发者信息
                .setAudience("app")  //接收用户
                .compressWith(CompressionCodecs.GZIP)  //数据压缩方式
                .signWith(SignatureAlgorithm.HS512, generalKey()) //加密方式
                //过期一个小时
                .setExpiration(new Date(currentTime + TOKEN_TIME_OUT * 1000))  //过期时间戳
                .addClaims(claimMaps) //cla信息
                .compact();
    }

    /**
     * 获取token中的claims信息
     * @param token
     * @return
     */
    private static Jws<Claims> getJws(String token) {
        return Jwts.parser()
                .setSigningKey(generalKey())
                .parseClaimsJws(token);
    }

    /**
     * 获取payload body信息
     * @param token
     * @return
     */
    public static Claims getClaimsBody(String token) {
        try {
            return getJws(token).getBody();
        } catch (ExpiredJwtException e) {
            return null;
        }
    }

    /**
     * 获取hearder body信息
     * @param token
     * @return
     */
    public static JwsHeader getHeaderBody(String token) {
        return getJws(token).getHeader();
    }

    /**
     * 是否过期
     * @param token
     * @return 1 有效  0 无效  2 已过期
     */
    public static Integer verifyToken(String token) {

        try {
            Claims claims = AppJwtUtil.getClaimsBody(token);
            if (claims == null) {
                return SystemConstants.JWT_FAIL;
            }
            return SystemConstants.JWT_OK;
        } catch (ExpiredJwtException ex) {
            return SystemConstants.JWT_EXPIRE;
        } catch (Exception e) {
            return SystemConstants.JWT_FAIL;
        }
    }

    /**
     * 由字符串生成加密key
     * @return
     */
    public static SecretKey generalKey() {
        byte[] encodedKey = Base64.getEncoder().encode(TOKEN_ENCRY_KEY.getBytes());
        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
        return key;
    }
}

4.3JWT系统常量类

public class SystemConstants {
    //JWT TOKEN已过期
    public static final Integer JWT_EXPIRE = 2;
    //JWT TOKEN有效
    public static final Integer JWT_OK = 1;
    //JWT TOKEN无效
    public static final Integer JWT_FAIL = 0;

}


版权声明:本文为m0_61601521原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。