【JWT】JWT 实现用户登录控制

目录

一、JWT 简介

二、JWT 的构成

2.1 头部(Header)

2.2 载荷 (Payload)

2.3 签名

2.4 最后生成出的 jwt 

2.5 测试和验证

三、工具类代码


一、JWT 简介

JSON Web Token(JWT) 是一种十分轻巧的规范,这个规范允许我们使用 JWT 在用户和服务器之间传递安全可靠的信息。

即 JWT 是用于在客户端和服务器之间传递用户信息的一段 JSON 格式的加密字符串,同时 JWT 可以用于让各个微服务识别用户的身份信息。即 JWT 中封装有用户的身份信息。

 

二、JWT 的构成

一个 JWT 实际上就是一个字符串,它由三部分组成:头部、载荷和签名

2.1 头部(Header)

头部用于描述关于该 JWT 的最基本信息,例如它的类型以及签名所用的算法等,这一部分被可以被表示成一个 JSON 对象。

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

这段 JSON 字符串指明了签名算法是 HS256 算法,实际使用中这段字符串会被 base64编码,被编码后的字符串为:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

转换地址:http://base64.xpcha.com/

 

2.2 载荷 (Payload)

载荷就是存放有效信息的地方

假设真实内容为:

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

base64 加密后:

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9

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

声明名称 作用
iss jwt 的签发者
sub 当前令牌的描述说明
aud 接收 jwt 的一方
exp jwt 的过期时间,这个时间必须大于签发时间
nbf 定义在什么时间之前,这个 jwt 是不可用的
iat jwt 的签发时间
jti jwt 的唯一身份标识,主要用来作为一次性 token,避免重放攻击

  

 

 

 

 

 

 

公共声明:

公共的声明可以添加任何信息,一般添加用户的相关信息或其他业务需要的必要信息,但由于这部分信息在客户端可以被解密,设置这部分可以不加密,故不建议添加敏感信息。这部分信息不参与令牌校验。

私有声明:

私有声明是提供者和消费者共同定义的声明,由于 base64是对称解密,即这部分信息可以被解密,故不建议存放敏感信息。这部分信息不参与令牌校验。

一个载荷的构成:

(标准中注册的声明 + 公共的声明 + 私有声明 ) => base64 加密 => 一个载荷

 

2.3 签名

jwt 的第三部分是一个签证信息,用于校验数据是否被篡改。签证信息由三部分组成:

  • header(base64后的)
  • payload(base64后的)
  • secret (秘钥,或称盐)

签名的生成方式是:

Base64(Header) . Base64(Payload) + 秘钥(盐) => 加密(使用 Header 中指定的算法加密) => 密文(签名)

例如(数据不准确):

Base64(Header)为:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

Base64(Payload)为:eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9

secret为: abcdefg

则签名为: HS256(eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.abcdefg) => TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

注意:

secret 是保存在服务器端的, jwt 的签发也是发生在服务器端,可以认为 secret 就是服务器端用于签名的私钥,在任何场景中都不应该泄漏出去,一旦客户端得知这个 secret,就意味着客户端可以自我签发 jwt 了。

签名的作用:

用于校验令牌是否被篡改

由于 secret 是保存在服务器的,若认为篡改了 jwt,则签名部分肯定发生了变化,服务器只要对比签名部分就能够判断出 jwt 是否被篡改。

 

2.4 最后生成出的 jwt 

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7Hg
Q

 

2.5 测试和验证


/**
 * @File: JwtTest
 * @Description: 令牌生成和解析测试
 * @Author: tom
 **/
@Slf4j
public class JwtTest {

    private String signKey = "tomTest";

    /**
     * 创建令牌
     */
    @Test
    public void testCreateToken() throws InterruptedException {
        // 构建jwt令牌对象
        JwtBuilder builder = Jwts.builder();
        builder.setIssuer("tom"); // 颁发者
        builder.setIssuedAt(new Date()); // 颁发日期
        builder.setExpiration(new Date(System.currentTimeMillis() + 20000)); // 设置定时,2秒过期
        builder.setSubject("jwt 令牌测试"); // 主题测试
        builder.signWith(SignatureAlgorithm.HS256, signKey); // 1. 签名算法 2. 秘钥
        // 自定义载荷信息
        Map<String, Object> userInfo = new HashMap<String,Object>();
        userInfo.put("company", "dis");
        userInfo.put("address", "北京");
        userInfo.put("money", 3500);
        // 添加载荷
        builder.addClaims(userInfo);

        // 获取生成 token
        String token = builder.compact();
        System.out.println(token);

//        Thread.sleep(3000);
        parseToken(token);
    }

    /**
     * 令牌解析
     */
    public void parseToken(String token) {
        Claims claims = Jwts.parser().setSigningKey(signKey).parseClaimsJws(token).getBody();
        System.out.println(claims.toString());
    }
}

 

三、工具类代码

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.Date;

/**
 * JWT工具类
 */
public class JwtUtil {

    // 有效期为
    public static final Long JWT_TTL = 3600000L;// 60 * 60 *1000  一个小时
    // 设置秘钥明文
    public static final String JWT_KEY = "tomcast";
    // 设置颁发者
    public static final String JWT_ISS = "tom";

    /**
     * 创建token
     * @param id
     * @param subject
     * @param ttlMillis
     * @return
     */
    public static String createJWT(String id, String subject, Long ttlMillis) {

        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        if(ttlMillis==null){
            ttlMillis=JwtUtil.JWT_TTL;
        }
        long expMillis = nowMillis + ttlMillis;
        Date expDate = new Date(expMillis);
        SecretKey secretKey = generalKey();

        JwtBuilder builder = Jwts.builder()
                .setId(id)              //唯一的ID
                .setSubject(subject)   // 主题  可以是JSON数据
                .setIssuer(JWT_ISS)     // 签发者
                .setIssuedAt(now)      // 签发时间
                .signWith(signatureAlgorithm, secretKey) //使用HS256对称加密算法签名, 第二个参数为秘钥
                .setExpiration(expDate);// 设置过期时间
        return builder.compact();
    }

    /**
     * 生成加密后的秘钥 secretKey
     * @return
     */
    public static SecretKey generalKey() {
        byte[] encodedKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);
        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
        return key;
    }

    /**
     * 解析令牌数据
     * @param jwt
     * @return
     */
    public static Claims parseJWT(String jwt) {
        SecretKey secretKey = generalKey();
        return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(jwt).getBody();
    }
}

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章