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);
    }
  • 相关阅读:
    把git项目放到个人服务器上
    关于fcitx无法切换输入法的问题解决
    博客变迁通知
    (欧拉回路 并查集 别犯傻逼的错了) 7:欧拉回路 OpenJudge 数据结构与算法MOOC / 第七章 图 练习题(Excercise for chapter7 graphs)
    (并查集) HDU 1856 More is better
    (并查集 不太会) HDU 1272 小希的迷宫
    (并查集 注意别再犯傻逼的错了) HDU 1213 How Many Tables
    (最小生成树 Kruskal算法) 51nod 1212 无向图最小生成树
    (并查集) HDU 1232 畅通工程
    (最小生成树 Prim) HDU 1233 还是畅通工程
  • 原文地址:https://www.cnblogs.com/mindzone/p/13751180.html
Copyright © 2011-2022 走看看