zoukankan      html  css  js  c++  java
  • 【Spring-Security】Re12 JsonWebToken

    一、认证机制种类:

    1、HTTP-Basic-Auth

    每次请求接口必须提供账号信息【username + password】

    但是信息有暴露风险,配合RestFul风格使用,逐渐淘汰

    2、Cookie-Auth

    首次请求在客户端和服务端分别创建Cookie + Session 对象

    通过两者的对象匹配实现状态管理,浏览器关闭会让Cookie对象销毁

    可以设置Cookie过期时间

    3、Open-Authorization [ Oauth ]

    开放授权,授权给第三方应用来访问服务资源

    4、Token-Auth

    基于令牌的验证,所有权限控制的判断全部以令牌为凭证通行访问

    二、Json Web Token [ JWT ]

    标准描述:

    https://tools.ietf.org/html/rfc7519

    官网地址:

    https://jwt.io/

    以前后端数据交互标准的JSON作为传输载体实现Token-Auth

    https://www.bilibili.com/video/BV12D4y1U7D8?p=39

    三、Java - JWT

    创建一个SpringBoot项目。

    需要Web组件和JJWT组件两个坐标:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt -->
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.9.1</version>
    </dependency>

    1、创建JWT

    JWT令牌生成测试:

    package cn.zeal4j;
    
    import io.jsonwebtoken.JwtBuilder;
    import io.jsonwebtoken.Jwts;
    import io.jsonwebtoken.SignatureAlgorithm;
    import org.junit.jupiter.api.Test;
    import org.springframework.boot.test.context.SpringBootTest;
    
    import java.util.Date;
    
    @SpringBootTest
    class JJwtApplicationTests {
    
        @Test
        void contextLoads() {
            creatTokenTest();
        }
        
        void creatTokenTest() {
            JwtBuilder jwtBuilder = Jwts.builder();
            jwtBuilder.
                    setId("8848").  // ID标识
                    setSubject("userL8").   // 用户主体
                    setIssuedAt(new Date()).    // 签发时间
                    signWith(SignatureAlgorithm.HS256, "This is a simple salty"); // 签名算法和盐
            
            // 生成的JWT令牌
            String jwtToken = jwtBuilder.compact();
            System.out.println(jwtToken);
        }
    }

    打印的令牌字符串:

    eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODQ4Iiwic3ViIjoidXNlckw4IiwiaWF0IjoxNjAxMzg1OTk5fQ.BM0CHf0kawFz6uTBjcF8aeFDYdX0M4CN0PswEPm9W0U

    令牌使用点号分割令牌的各个信息

    eyJhbGciOiJIUzI1NiJ9 # 头部信息
    .
    eyJqdGkiOiI4ODQ4Iiwic3ViIjoidXNlckw4IiwiaWF0IjoxNjAxMzg1OTk5fQ    # 荷载信息
    .
    BM0CHf0kawFz6uTBjcF8aeFDYdX0M4CN0PswEPm9W0U    # 签发信息

    2、解析JWT

    解密处理:

    package cn.zeal4j;
    
    import io.jsonwebtoken.JwtBuilder;
    import io.jsonwebtoken.Jwts;
    import io.jsonwebtoken.SignatureAlgorithm;
    import io.jsonwebtoken.impl.Base64Codec;
    import org.junit.jupiter.api.Test;
    import org.springframework.boot.test.context.SpringBootTest;
    import sun.misc.BASE64Decoder;
    
    import java.util.Date;
    
    @SpringBootTest
    class JJwtApplicationTests {
    
        @Test
        void contextLoads() {
            creatTokenTest();
        }
    
        void creatTokenTest() {
            JwtBuilder jwtBuilder = Jwts.builder();
            jwtBuilder.
                    setId("8848").  // ID标识
                    setSubject("userL8").   // 用户主体
                    setIssuedAt(new Date()).    // 签发时间
                    signWith(SignatureAlgorithm.HS256, "This is a simple salty"); // 签名算法和盐
    
            // 生成的JWT令牌
            String jwtToken = jwtBuilder.compact();
            System.out.println(jwtToken);
    
            System.out.println("- - - - - JWT-Token-Decoder!!! - - - -");
            String[] tokenParts = jwtToken.split("\.");
    
            String tokenHead = Base64Codec.BASE64.decodeToString(tokenParts[0]);
    
            String tokenCarrier = Base64Codec.BASE64.decodeToString(tokenParts[1]);
    
            String tokenSignature = Base64Codec.BASE64.decodeToString(tokenParts[2]);
    
            System.out.println("tokenHead -> " + tokenHead);
            System.out.println("tokenCarrier -> " + tokenCarrier);
            System.out.println("tokenSignature -> " + tokenSignature);
        }
    }

    输出结果:

    - - - - - JWT-Token-Decoder!!! - - - -
    tokenHead -> {"alg":"HS256"}
    tokenCarrier -> {"jti":"8848","sub":"userL8","iat":1601386514
    tokenSignature -> *�2ɥr�ԻjNz�4�����RzЂ97�+f

    签名是被盐加密过了的,所以就算用算法解密了,但是有盐打乱,还是不能知道原文是什么

    另外每次生成的Token信息的也会有不同的区别:

    eyJhbGciOiJIUzI1NiJ9.
    eyJqdGkiOiI4ODQ4Iiwic3ViIjoidXNlckw4IiwiaWF0IjoxNjAxMzg2NTE0fQ.
    KuQyyaVys9S7_ak56pTSWkJbtotxSetCCOTeeBitmmw
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
    eyJhbGciOiJIUzI1NiJ9.
    eyJqdGkiOiI4ODQ4Iiwic3ViIjoidXNlckw4IiwiaWF0IjoxNjAxMzg2NzU2fQ.
    LSnm3uzBW6T6fHqGLszv5jsoIoIIiKZRx_rMAwylLV0

    头密文是一直的,但是荷载密文是在结尾有区别,原因是我们加了签发日期

    而下面的签发密文是盐加密的,每次都会算出不一样的密文结果

    package cn.zeal4j;
    
    import io.jsonwebtoken.*;
    import io.jsonwebtoken.impl.Base64Codec;
    import org.junit.jupiter.api.Test;
    import org.springframework.boot.test.context.SpringBootTest;
    import sun.misc.BASE64Decoder;
    
    import java.util.Date;
    
    @SpringBootTest
    class JJwtApplicationTests {
    
        @Test
        void contextLoads() {
            // creatTokenTest();
    
            parseJwtTokenTest();
        }
    
        void creatTokenTest() {
            JwtBuilder jwtBuilder = Jwts.builder();
            jwtBuilder.
                    setId("8848").  // ID标识
                    setSubject("userL8").   // 用户主体
                    setIssuedAt(new Date()).    // 签发时间
                    signWith(SignatureAlgorithm.HS256, "This is a simple salty"); // 签名算法和盐
    
            // 生成的JWT令牌
            String jwtToken = jwtBuilder.compact();
            System.out.println(jwtToken);
    
            System.out.println("- - - - - JWT-Token-Decoder!!! - - - -");
            String[] tokenParts = jwtToken.split("\.");
    
            String tokenHead = Base64Codec.BASE64.decodeToString(tokenParts[0]);
    
            String tokenCarrier = Base64Codec.BASE64.decodeToString(tokenParts[1]);
    
            String tokenSignature = Base64Codec.BASE64.decodeToString(tokenParts[2]);
    
            System.out.println("tokenHead -> " + tokenHead);
            System.out.println("tokenCarrier -> " + tokenCarrier);
            System.out.println("tokenSignature -> " + tokenSignature);
        }
    
        void parseJwtTokenTest() {
            // 模拟客户端发送的JWT令牌
            final String simulationClientJwtToken = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODQ4Iiwic3ViIjoidXNlckw4IiwiaWF0IjoxNjAxMzg2NzU2fQ.LSnm3uzBW6T6fHqGLszv5jsoIoIIiKZRx_rMAwylLV0";
    
            Jws<Claims> claimsJws = Jwts.parser().setSigningKey("This is a simple salty").parseClaimsJws(simulationClientJwtToken);
    
    
            JwsHeader header = claimsJws.getHeader();
            String keyId = header.getKeyId(); // 获取ID标识
    
            // 负载对象
            Claims body = claimsJws.getBody();
            String subject = body.getSubject(); // 获取签发主体
            Date issuedAt = body.getIssuedAt(); // 获取签发时间
            String id = body.getId(); // 这也能获取ID?
    
            String signature = claimsJws.getSignature(); // 签名
    
            System.out.println("from JwsHeader KeyId -> " + keyId);
            System.out.println("from ClaimsBody id -> " + id);
    
            System.out.println("ClaimsBody subject -> " + subject);
            System.out.println("ClaimsBody issuedAt -> " + issuedAt);
            System.out.println("signature -> " + signature);
        }
    }

    打印结果:

    from JwsHeader KeyId -> null
    from ClaimsBody id -> 8848
    ClaimsBody subject -> userL8
    ClaimsBody issuedAt -> Tue Sep 29 21:39:16 CST 2020
    signature -> LSnm3uzBW6T6fHqGLszv5jsoIoIIiKZRx_rMAwylLV0

    3、Token过期时间设置

    package cn.zeal4j;
    
    import io.jsonwebtoken.*;
    import io.jsonwebtoken.impl.Base64Codec;
    import org.junit.jupiter.api.Test;
    import org.springframework.boot.test.context.SpringBootTest;
    import sun.misc.BASE64Decoder;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    @SpringBootTest
    class JJwtApplicationTests {
    
        @Test
        void contextLoads() {
            // 1、creatTokenTest();
    
            // 2、parseJwtTokenTest();
    
            // 3、parseExpiredJwtTokenTest(creatTokenTest());
    
            parseExpiredJwtTokenTest(creatTokenTest());
        }
    
        String creatTokenTest() {
            long currentTimeMillis = System.currentTimeMillis();
            long theExpireTimeMills = currentTimeMillis + (60 * 1000); // 1毫秒 * 1000(1秒) * 60 = 1 分钟
            JwtBuilder jwtBuilder = Jwts.builder();
            jwtBuilder.
                    setExpiration(new Date(theExpireTimeMills)).
                    setId("8848").  // ID标识
                    setSubject("userL8").   // 用户主体
                    setIssuedAt(new Date()).    // 签发时间
                    signWith(SignatureAlgorithm.HS256, "This is a simple salty"); // 签名算法和盐
    
            // 生成的JWT令牌
            String jwtToken = jwtBuilder.compact();
            System.out.println(jwtToken);
    
            System.out.println("- - - - - JWT-Token-Decoder!!! - - - -");
            String[] tokenParts = jwtToken.split("\.");
    
            String tokenHead = Base64Codec.BASE64.decodeToString(tokenParts[0]);
    
            String tokenCarrier = Base64Codec.BASE64.decodeToString(tokenParts[1]);
    
            String tokenSignature = Base64Codec.BASE64.decodeToString(tokenParts[2]);
    
            System.out.println("tokenHead -> " + tokenHead);
            System.out.println("tokenCarrier -> " + tokenCarrier);
            System.out.println("tokenSignature -> " + tokenSignature);
    
            return jwtToken;
        }
    
        void parseJwtTokenTest() {
            // 模拟客户端发送的JWT令牌
            final String simulationClientJwtToken = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODQ4Iiwic3ViIjoidXNlckw4IiwiaWF0IjoxNjAxMzg2NzU2fQ.LSnm3uzBW6T6fHqGLszv5jsoIoIIiKZRx_rMAwylLV0";
    
            Jws<Claims> claimsJws = Jwts.parser().setSigningKey("This is a simple salty").parseClaimsJws(simulationClientJwtToken);
    
            JwsHeader header = claimsJws.getHeader();
            String keyId = header.getKeyId(); // 获取ID标识
    
            // 负载对象
            Claims body = claimsJws.getBody();
            String subject = body.getSubject(); // 获取签发主体
            Date issuedAt = body.getIssuedAt(); // 获取签发时间
            String id = body.getId(); // 这也能获取ID?
    
            String signature = claimsJws.getSignature(); // 签名
    
            System.out.println("from JwsHeader KeyId -> " + keyId);
            System.out.println("from ClaimsBody id -> " + id);
    
            System.out.println("ClaimsBody subject -> " + subject);
            System.out.println("ClaimsBody issuedAt -> " + issuedAt);
            System.out.println("signature -> " + signature);
        }
    
        void parseExpiredJwtTokenTest(String jwtToken) {
            Jws<Claims> claimsJws = Jwts.parser().setSigningKey("This is a simple salty").parseClaimsJws(jwtToken);
            // eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2MDEzODgzODYsImp0aSI6Ijg4NDgiLCJzdWIiOiJ1c2VyTDgiLCJpYXQiOjE2MDEzODgzMjZ9.Ve0n_TylzsCFHnk4vrjWKM_fZPGteupsx2aLbJU2E0k
            
            JwsHeader header = claimsJws.getHeader();
            String keyId = header.getKeyId(); // 获取ID标识
    
            // 负载对象
            Claims body = claimsJws.getBody();
            String subject = body.getSubject(); // 获取签发主体
            Date issuedAt = body.getIssuedAt(); // 获取签发时间
            String id = body.getId(); // 这也能获取ID?
    
            String signature = claimsJws.getSignature(); // 签名
    
    //        System.out.println("from JwsHeader KeyId -> " + keyId);
    //        System.out.println("from ClaimsBody id -> " + id);
    //
    //        System.out.println("ClaimsBody subject -> " + subject);
    //        System.out.println("ClaimsBody issuedAt -> " + issuedAt);
    //        System.out.println("signature -> " + signature);
    
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String signTime = simpleDateFormat.format(body.getIssuedAt());
            String expireTime = simpleDateFormat.format(body.getExpiration());
            String thisTime = simpleDateFormat.format(new Date());
    
            System.out.println("- - - JwtTokenTimeExpireTest - - -");
            System.out.println("签发时间 -> " + signTime);
            System.out.println("过期时间 -> " + expireTime);
            System.out.println("现在时间 -> " + thisTime);
        }
    }

    结果打印:

    - - - JwtTokenTimeExpireTest - - -
    签发时间 -> 2020-09-29 22:05:26
    过期时间 -> 2020-09-29 22:06:26
    现在时间 -> 2020-09-29 22:05:26

    超过过期时间解析Token会让程序抛出TokenExpireException异常,详细信息自测,不赘述了

    4、自定义申明:

    String creatTokenTest() {
        long currentTimeMillis = System.currentTimeMillis();
        long theExpireTimeMills = currentTimeMillis + (60 * 1000); // 1毫秒 * 1000(1秒) * 60 = 1 分钟
    
        Map<String, Object> map = new HashMap<>();
        map.put("k1", "v1");
        map.put("k2", "v2");
        map.put("k3", "v3");
        // ......
    
        JwtBuilder jwtBuilder = Jwts.builder();
        jwtBuilder.
                setExpiration(new Date(theExpireTimeMills)).
                setId("8848").  // ID标识
                setSubject("userL8").   // 用户主体
                setIssuedAt(new Date()).    // 签发时间
                claim("customClaimKey01", "customClaimValue01"). // 自定义申明方式一
                claim("customClaimKey02", "customClaimValue02").
                addClaims(map). // 自定义申明方式二
                signWith(SignatureAlgorithm.HS256, "This is a simple salty"); // 签名算法和盐
    
        // 生成的JWT令牌
        String jwtToken = jwtBuilder.compact();
        System.out.println(jwtToken);
    
        System.out.println("- - - - - JWT-Token-Decoder!!! - - - -");
        String[] tokenParts = jwtToken.split("\.");
    
        String tokenHead = Base64Codec.BASE64.decodeToString(tokenParts[0]);
    
        String tokenCarrier = Base64Codec.BASE64.decodeToString(tokenParts[1]);
    
        String tokenSignature = Base64Codec.BASE64.decodeToString(tokenParts[2]);
    
        System.out.println("tokenHead -> " + tokenHead);
        System.out.println("tokenCarrier -> " + tokenCarrier);
        System.out.println("tokenSignature -> " + tokenSignature);
    
        return jwtToken;
    }
    
    void parseJwtTokenTest() {
        // 模拟客户端发送的JWT令牌
        final String simulationClientJwtToken = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODQ4Iiwic3ViIjoidXNlckw4IiwiaWF0IjoxNjAxMzg2NzU2fQ.LSnm3uzBW6T6fHqGLszv5jsoIoIIiKZRx_rMAwylLV0";
    
        Jws<Claims> claimsJws = Jwts.parser().setSigningKey("This is a simple salty").parseClaimsJws(simulationClientJwtToken);
    
        JwsHeader header = claimsJws.getHeader();
        String keyId = header.getKeyId(); // 获取ID标识
    
        // 负载对象
        Claims body = claimsJws.getBody();
        String subject = body.getSubject(); // 获取签发主体
        Date issuedAt = body.getIssuedAt(); // 获取签发时间
        String id = body.getId(); // 这也能获取ID?
    
        Object o = body.get("key-name"); // 获取申明
    
        String signature = claimsJws.getSignature(); // 签名
    
        System.out.println("from JwsHeader KeyId -> " + keyId);
        System.out.println("from ClaimsBody id -> " + id);
    
        System.out.println("ClaimsBody subject -> " + subject);
        System.out.println("ClaimsBody issuedAt -> " + issuedAt);
        System.out.println("signature -> " + signature);
    }
  • 相关阅读:
    IO 输入输出流
    「做自己」​​​​​​​写出我心(八十)
    「关于爱情,关于婚姻」​​​​​​​写出我心(七十九)
    「如何冥想?」​​​​​​​写出我心(七十八)
    「对这个世界温柔一点」​​​​​​写出我心(七十七)
    「7tips-克服焦虑」​​​​​写出我心(七十六)
    「十步学习法」​​​​写出我心(七十五)
    「成功的人生是台阶式向上」​​​写出我心(七十四)
    「世界是自己的」​​写出我心(七十三)
    「​八点工作中的小建议」​写出我心(七十二)
  • 原文地址:https://www.cnblogs.com/mindzone/p/13751180.html
Copyright © 2011-2022 走看看