• JWT


    把书本读薄,简单介绍一下JWT所具备的特征,最好提前了解 “编码”、“摘要” 和 “加密” 的区别。

    JWT简介

    JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA.

    JWT是一种设计令牌的开放标准。(开发标准,就意味着可以有多种实现)

    JWT结构

    JWT分为3段,用“.”连接,其中:“头部”和“载荷”通过Base64进行编码的,“签名”是将前2段连接在一起,通过加密算法得出的校验码。

    HEADER(头部).PAYLOAD(载荷).SIGNATURE(签名)

    头部

    Base64解码后是一段JSON,用于说明jwt所采用的算法

    alg: HS256
    typ: JWT

    载荷

    Base64解码后是一段JSON,用于存储jwt的详细信息,字段只是推荐,并没有强制要求这些字段必须存在

    iss:发行人
    exp:token的到期时间
    sub:主题
    aud:用户
    nbf:token在此之前不可用
    iat:发布时间
    jti:JWT ID用于标识该JWT

    签名

    “头部”和“载荷”使用Base64编码,编码并不是加密,用户可以随时解码,查看里面的内容。
    允许用户看token的内容,又不允许他们修改,这就是“签名”的作用——防止用户伪造token。

    比如:token过期了,用户偷偷修改了token的有效期,这时候,服务器只要把数据再加密一遍,如果加密的结果不一致,就说明token被修改过。

    DEMO

    自带的加密算法有这么多:none、HS256、HS384、HS512、RS256、RS384、RS512、ES256、ES384、ES512、PS256、PS384、PS512。

    挑2个展示效果,对称加密HS256,非对称加密RS256。

    Maven依赖

    <dependency>
        <groupId>com.auth0</groupId>
        <artifactId>java-jwt</artifactId>
        <version>3.17.0</version>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.9.1</version>
    </dependency>

    HS256

    对称加密,加密和解密需要相同的密钥。

    import io.jsonwebtoken.Claims;
    import io.jsonwebtoken.JwtBuilder;
    import io.jsonwebtoken.Jwts;
    import io.jsonwebtoken.SignatureAlgorithm;
    
    import javax.crypto.SecretKey;
    import java.util.Base64;
    import java.util.Date;
    
    /**
     * @author Mr.css
     * @date 2022-01-05 15:03
     */
    public class JwtHS256 {
    
    //    /**
    //     * 生成密钥
    //     *
    //     * @param key 长度必须是32的倍数
    //     * @return SecretKey
    //     * @throws IllegalArgumentException -
    //     */
    //    public static SecretKey generateSecretKey(byte[] key) {
    //        if (key.length % 32 != 0) {
    //            throw new IllegalArgumentException("key size must be a multiple of 32");
    //        }
    //        return new SecretKeySpec(key, "AES");
    //    }
    
        /**
         * 密钥,更复杂地,应该将密钥交由数据库管理
         */
        private static SecretKey secretKey = AES.generateSecretKey(MD5.hexBit32("this is a secret key!").getBytes());
    
        /**
         * 生成jwt
         *
         * @param id        ID
         * @param subject   主题
         * @param ttlMillis 超时时间
         * @return jwt
         */
        public static String encrypt(String id, String subject, long ttlMillis) {
            long nowMillis = System.currentTimeMillis();
            Date now = new Date(nowMillis);
            JwtBuilder builder = Jwts.builder();
            //ID
            builder.setId(id);
            //主题
            builder.setSubject(subject);
            // 签发者
            builder.setIssuer("system");
            // 签发时间
            builder.setIssuedAt(now);
            // 签名算法以及密匙
            builder.signWith(SignatureAlgorithm.HS256, secretKey);
            // 设置超时时间
            if (ttlMillis >= 0) {
                long expMillis = nowMillis + ttlMillis;
                Date expDate = new Date(expMillis);
                builder.setExpiration(expDate);
            }
            return builder.compact();
        }
    
        /**
         * 解析JWT字符串
         *
         * @param jwt jwt
         * @return Claims
         */
        public static Claims decrypt(String jwt) {
            return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(jwt).getBody();
        }
    
        public static void main(String[] args) throws InterruptedException {
            //解密
            String jwt = encrypt("id", "sub", 3000);
            System.out.println(jwt);
    
            //解密
            Claims claims = decrypt(jwt);
            System.out.println(claims);
    
            //解析头部
            System.out.println(
                    new String(Base64.getDecoder().decode("eyJhbGciOiJIUzI1NiJ9")));
            //解析载荷
            System.out.println(
                    new String(Base64.getDecoder().decode("eyJqdGkiOiJpZCIsInN1YiI6InN1YiIsImlzcyI6InN5c3RlbSIsImlhdCI6MTY0MTM3NTc3MywiZXhwIjoxNjQxMzc1Nzc2fQ")));
    
            //5秒之后token失效
            Thread.sleep(5000);
            claims = decrypt(jwt);
            System.out.println(claims);
        }
    }

    RS256

    代码上与HS256基本一致。JWT限定了只能用私钥加密,公钥解密。

    什么情况下使用RS256?

    令牌由其它服务器发出,我们的服务器只负责调用,我们在使用令牌之前,需要验证令牌是可信任的。

    (比如:微服务,一个服务器只负责造令牌,其它负责用,使用之前,需要保证令牌是自己人发出来的。)

    import io.jsonwebtoken.Claims;
    import io.jsonwebtoken.JwtBuilder;
    import io.jsonwebtoken.Jwts;
    import io.jsonwebtoken.SignatureAlgorithm;
    
    import java.security.*;
    import java.util.Date;
    
    /**
     * @author Mr.css
     * @date 2022-01-05 15:03
     */
    public class JwtRS256 {
    
        /**
         * 公钥/私钥一般交由数据库管理
         */
        private static PublicKey publicKey;
        private static PrivateKey privateKey;
    
    
    //    /**
    //     * 获取密钥对
    //     *
    //     * @param initialize 长度,1024
    //     * @return 密钥对
    //     */
    //    public static KeyPair getKeyPair(int initialize) throws GeneralSecurityException {
    //        KeyPairGenerator keyPairGen;
    //        keyPairGen = KeyPairGenerator.getInstance("RSA");
    //        keyPairGen.initialize(initialize);
    //        return keyPairGen.generateKeyPair();
    //    }
    
        static {
            try {
                KeyPair keyPair = RSA.getKeyPair();
                publicKey = keyPair.getPublic();
                privateKey = keyPair.getPrivate();
            } catch (GeneralSecurityException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 生成jwt
         *
         * @param id        ID
         * @param subject   主题
         * @param ttlMillis 超时时间
         * @return jwt
         */
        public static String encrypt(String id, String subject, long ttlMillis) {
            Date now = new Date();
            JwtBuilder builder = Jwts.builder();
            //ID
            builder.setId(id);
            //主题
            builder.setSubject(subject);
            // 签发者
            builder.setIssuer("system");
            // 签发时间
            builder.setIssuedAt(now);
            // 签名算法以及密匙
            builder.signWith(SignatureAlgorithm.RS256, privateKey);
            // 设置超时时间
            if (ttlMillis >= 0) {
                long expMillis = now.getTime() + ttlMillis;
                Date expDate = new Date(expMillis);
                builder.setExpiration(expDate);
            }
            return builder.compact();
        }
    
        /**
         * 解析JWT字符串
         *
         * @param jwt jwt
         * @return Claims
         */
        public static Claims decrypt(String jwt) {
            return Jwts.parser().setSigningKey(publicKey).parseClaimsJws(jwt).getBody();
        }
    
        public static void main(String[] args) throws InterruptedException {
            //加密
            String jwt = encrypt("id", "sub", 3000);
            System.out.println(jwt);
    
            //解密
            Claims claims = decrypt(jwt);
            System.out.println(claims);
    
            //暂停5秒再解密,超时报错
            Thread.sleep(5000);
            claims = decrypt(jwt);
            System.out.println(claims);
        }
    }

    公钥/私钥——字符串和对象的转换

        /**
         * 获取公钥字符串
         */
        public static String getPublicKey(KeyPair keyPair) {
            return Base64.encodeString(keyPair.getPublic().getEncoded());
        }
    
        /**
         * 获取私钥字符串
         */
        public static String getPrivateKey(KeyPair keyPair) {
            return Base64.encodeString(keyPair.getPrivate().getEncoded());
        }
    
        /**
         * 获取公钥对象
         *
         * @param publicKey 公钥字符串
         * @return 公钥
         * @throws GeneralSecurityException -
         */
        public static PublicKey generatePublicKey(String publicKey) throws GeneralSecurityException {
            byte[] keyBytes = Base64.decodeString(publicKey);
            X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);
            KeyFactory factory = KeyFactory.getInstance("RSA");
            return factory.generatePublic(x509EncodedKeySpec);
        }
    
        /**
         * 获取私钥对象
         *
         * @param privateKey 私钥字符串
         * @return 私钥
         * @throws GeneralSecurityException -
         */
        public static PrivateKey generatePrivateKey(String privateKey) throws GeneralSecurityException {
            byte[] keyBytes = Base64.decodeString(privateKey);
            PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes);
            KeyFactory factory = KeyFactory.getInstance("RSA");
            return factory.generatePrivate(pkcs8EncodedKeySpec);
        }
    疯狂的妞妞 :每一天,做什么都好,不要什么都不做! 文中有不妥或者错误的地方还望指出,如果觉得本文对你有所帮助不妨【推荐】一下!如果你有更好的建议,可以给我留言讨论,共同进步!
  • 相关阅读:
    codevs 1086 栈 2003年NOIP全国联赛普及组
    1200 同余方程 2012年NOIP全国联赛提高组
    【bzoj4939】【YNOI2016】掉进兔子洞(莫队)
    洛谷P3674 小清新人渣的本愿(莫队)
    Lucas卢卡斯定理
    组合数学习笔记
    洛谷P3178 [HAOI2015]树上操作(线段树)
    洛谷P3258 [JLOI2014]松鼠的新家(树上差分+树剖)
    洛谷P2526 [SHOI2001]小狗散步(二分图匹配)
    bzoj3140: [Hnoi2013]消毒(二分图)
  • 原文地址:https://www.cnblogs.com/chenss15060100790/p/15770434.html
Copyright © 2020-2023  润新知