package com.sjyt.icom.asst.common.util;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ReflectUtil;
import com.sjyt.icom.asst.common.model.message.VerifyClaims;
import com.sjyt.icom.asst.common.constant.SrsConstant;

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.lang.reflect.Field;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>JjwtUtil</p>
 *
 * @author renyanru
 * @version V1.0.0
 * @date 2022/6/28 16:07
 */
public class JjwtUtil {
    // 过期时间，单位毫秒
    public static final int EXPIRE_TIME = 60 * 60 * 1000; // 一个小时
//	public static final long EXPIRE_TIME = 7 * 24 * 3600 * 1000; // 一个星期

    // 由字符串生成加密key
    public static SecretKey generalKey() {
        // 本地的密码解码
        // byte[] encodedKey = Base64.decodeBase64(SrsConstant.JWT_SECRET);
        byte[] encodedKey = SrsConstant.JWT_SECRET.getBytes();
        // 根据给定的字节数组使用AES加密算法构造一个密钥
        return new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
    }

    // 创建jwt
    public static String createJWT(String audience, VerifyClaims verifyClaims){
        // 指定header那部分签名的时候使用的签名算法，jjwt已经将这部分内容封装好了，只有{"alg":"HS256"}
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        // 创建payload的私有声明（根据特定的业务需要添加，如果要拿这个做验证，一般是需要和jwt的接收方提前沟通好验证的方式）
        Map<String, Object> claims = BeanUtil.beanToMap(verifyClaims);
        // jti用户id，例如：20da39f8-b74e-4a9b-9a0f-a39f1f73fe64
        // 生成JWT的时间
        long nowTime = System.currentTimeMillis();
        Date issuedAt = new Date(nowTime);
        // 生成签名的时候使用的秘钥secret，切记这个秘钥不能外露，是你服务端的私钥，在任何场景都不应该流露出去，一旦客户端得知这个secret，那就意味着客户端是可以自我签发jwt的
        SecretKey key = generalKey();
        // 为payload添加各种标准声明和私有声明
        JwtBuilder builder = Jwts.builder() // 表示new一个JwtBuilder，设置jwt的body
                .setClaims(claims) // 如果有私有声明，一定要先设置自己创建的这个私有声明，这是给builder的claim赋值，一旦写在标准的声明赋值之后，就是覆盖了那些标准的声明
                .setId(SrsConstant.JWT_ID) // jti(JWT ID)：jwt的唯一身份标识，根据业务需要，可以设置为一个不重复的值，主要用来作为一次性token，从而回避重放攻击
                .setIssuedAt(issuedAt) // iat(issuedAt)：jwt的签发时间
                .setIssuer(SrsConstant.ISSUER) // iss(issuer)：jwt签发者
                .signWith(signatureAlgorithm, key); // 设置签名，使用的是签名算法和签名使用的秘钥
        // 设置过期时间
        long exp = nowTime + (long) EXPIRE_TIME;
        builder.setExpiration(new Date(exp));
        // 设置jwt接收者
        if (audience == null || "".equals(audience)) {
            builder.setAudience(SrsConstant.AUDIENCE);
        } else {
            builder.setAudience(audience);
        }
        return builder.compact();
    }


    // 解密jwt
    public static Claims parseJWT(String jwt) {
        SecretKey key = generalKey(); // 签名秘钥，和生成的签名的秘钥一模一样
        return Jwts.parser() // 得到DefaultJwtParser
                .setSigningKey(key) // 设置签名的秘钥
                .parseClaimsJws(jwt).getBody();
    }

    /**
     * 得到载荷
     *
     * @param jwt jwt
     * @return r
     */
    public static VerifyClaims getClaims(String jwt) {
        final Claims claims = parseJWT(jwt);
        final Field[] fields = ReflectUtil.getFields(VerifyClaims.class);
        Map<String, String> map = new HashMap<>();
        for (Field field : Arrays.stream(fields).collect(Collectors.toList())) {
            map.put(field.getName(), claims.get(field.getName(), String.class));
        }
        return BeanUtil.toBean(map, VerifyClaims.class);
    }

}
