• JWT令牌研究


    1、JWT介绍

    在介绍JWT之前先看一下传统校验令牌的方法,如下图:

    问题:

    ​ 传统授权方法的问题是用户每次请求资源服务,资源服务都需要携带令牌访问认证服务去校验令牌的合法性,并根据令牌获取用户的相关信息,性能低下。为什么会说性能低下呢? 原因就是用户(客户端)请求资源服务,资源服务都需要携带令牌访问认证服务去校验令牌的合法性,要明确一来一回的请求和响应都是需要消耗时间的哦。

    ​ 解决方法:

    ​ 使用JWT的思路是,用户认证通过后 会得到一个JWT令牌,JWT令牌中已经包括了用户相关的信息,客户端只需要携带JWT访问资源服务,资源服务根据事先约定的算法自行完成令牌校验(这就是比较厉害的哦),无需每次都请求认证服务完成授权(耗时短)。

    JWT令牌授权过程如下图:

    说半天了,还不知道什么是JWT令牌吧,请听老夫细细道来:

    1.1、概念

    什么是JWT?

    JSON Web Token(JWT)是一个开放的行业标准(RFC 7519),它定义了一种简介的、自包含的协议格式,用于在通信双方传递json对象,传递的信息经过数字签名可以被验证和信任。JWT可以使用HMAC算法或使用RSA的公钥/私钥对来签名,防止被篡改。

    英语比较好的同学,可以去官网看看哦(前提你必须能看老外的网站)。

    官网:https://jwt.io/

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

    我给你贴个图,慢慢看吧

    JWT令牌的优点:

    1、jwt基于json,非常方便解析。

    2、可以在令牌中自定义丰富的内容,易扩展。

    3、通过非对称加密算法及数字签名技术,JWT防止篡改,安全性高。

    4、资源服务使用JWT可不依赖认证服务即可完成授权。

    缺点:

    1、JWT令牌较长,占存储空间比较大。

    1.2、令牌结构

    通过学习JWT令牌结构为自定义jwt令牌打好基础。

    JWT令牌由三部分组成(Header,Payload,Signature),每部分中间使用点(.)分隔,比如:xxxxx.yyyyy.zzzzz

    1、Header

    第二部分是头部包括令牌的类型(即JWT)及使用的哈希算法(如HMAC SHA256或RSA)

    一个例子如下:

    下边是Header部分的内容

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

    将上边的内容使用Base64Url编码,得到一个字符串就是JWT令牌的第一部分。

    2、Payload

    第二部分是负载,内容也是一个json对象,它是存放有效信息的地方,它可以存放jwt提供的现成字段,比如:iss(签发者),exp(过期时间戳), sub(面向的用户)等,也可自定义字段。

    此部分不建议存放敏感信息,因为此部分可以解码还原原始内容。

    最后将第二部分负载使用Base64Url编码,得到一个字符串就是JWT令牌的第二部分。

    一个例子:

     
    {
      "sub": "1234567890",
      "name": "456",
      "admin": true
    }
    ​

    3、Signature

    第三部分是签名,此部分用于防止jwt内容被篡改。

    这个部分使用base64url将前两部分进行编码,编码后使用点(.)连接组成字符串,最后使用header中声明签名算法进行签名。

    一个例子:

     
    HMACSHA256(
      base64UrlEncode(header) + "." +
      base64UrlEncode(payload),
      secret)

    base64UrlEncode(header):jwt令牌的第一部分。

    base64UrlEncode(payload):jwt令牌的第二部分。

    secret:签名所使用的密钥。

    1.3、JWT入门

    Spring Security 提供对JWT的支持,本文我们使用Spring Security 提供的JwtHelper来创建JWT令牌,校验JWT令牌等操作。

    生成私钥和公钥

    JWT令牌生成采用非对称加密算法

    1、生成密钥证书

    下边命令生成密钥证书,采用RSA 算法每个证书包含公钥和私钥

    keytool -genkeypair -alias jykey -keyalg RSA -keypass jiaoyu -keystore jy.keystore -storepass jiaoyukeystore

    Keytool 是一个java提供的证书管理工具

    -alias:密钥的别名

    -keyalg:使用的hash算法

    -keypass:密钥的访问密码

    -keystore:密钥库文件名,jy.keystore保存了生成的证书

    -storepass:密钥库的访问密码

    查询证书信息:

    keytool -list -keystore jy.keystore

    删除别名

    keytool -delete -alias jykey -keystore jy.keystore

    2、导出公钥

    openssl是一个加解密工具包,这里使用openssl来导出公钥信息。

    安装 openssl:http://slproweb.com/products/Win32OpenSSL.html

    我的安装资料为Win64OpenSSL-1_1_0g.exe

    配置openssl的path环境变量(可以参考哦),本教程配置在D:OpenSSL-Win64in

    cmd进入jy.keystore文件所在目录执行如下命令:

    keytool -list -rfc --keystore jy.keystore | openssl x509 -inform pem -pubkey

    输入密钥库密码:

    下边这一段就是公钥内容:

    -----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAijyxMdq4S6L1Af1rtB8SjCZHNgsQG8JTfGy55eYvzG0B/E4AudR2prSRBvF7NYPL47scRCNPgLnvbQczBHbBug6uOr78qnWsYxHlW6Aa5dI5NsmOD4DLtSw8eX0hFyK5Fj6ScYOSFBz9cd1nNTvx2+oIv0lJDcpQdQhsfgsEr1ntvWterZt/8r7xNN83gHYuZ6TM5MYvjQNBc5qC7Krs9wM7UoQuL+s0X6RlOib7/mcLn/lFLsLDdYQAZkSDx/6+t+1oHdMarChIPYT1sx9Dwj2j2mvFNDTKKKKAq0cv14Vrhz67Vjmz2yMJePDqUi0JYS2r0iIo7n8vN7s83v5uOQIDAQAB-----END PUBLIC KEY-----

    将上边的公钥拷贝到文本文件中,合并为一行。

    1.4、生成jwt令牌

    在认证工程创建测试类,测试jwt令牌的生成与验证。

     
    //生成一个jwt令牌
    @Test
    public void testCreateJwt(){
        //证书文件
        String key_location = "jy.keystore";
        //密钥库密码
        String keystore_password = "jiaoyukeystore";
        //访问证书路径
        ClassPathResource resource = new ClassPathResource(key_location);
        //密钥工厂
        KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(resource, keystore_password.toCharArray());
        //密钥的密码,此密码和别名要匹配
        String keypassword = "jiaoyu";
        //密钥别名
        String alias = "jykey";
        //密钥对(密钥和公钥)
        KeyPair keyPair = keyStoreKeyFactory.getKeyPair(alias,keypassword.toCharArray());
        //私钥
        RSAPrivateKey aPrivate = (RSAPrivateKey) keyPair.getPrivate();
        //定义payload信息
        Map<String, Object> tokenMap = new HashMap<>();
        tokenMap.put("id", "123");
        tokenMap.put("name", "mrt");
        tokenMap.put("roles", "r01,r02");
        tokenMap.put("ext", "1");
        //生成jwt令牌
        Jwt jwt = JwtHelper.encode(JSON.toJSONString(tokenMap), new RsaSigner(aPrivate));
        //取出jwt令牌
        String token = jwt.getEncoded();
        System.out.println("token="+token);
    ​
    }

    1.5、验证jwt令牌

     
    //资源服务使用公钥验证jwt的合法性,并对jwt解码
        @Test
        public void testVerify(){
            //jwt令牌
            String token ="eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHQiOiIxIiwicm9sZXMiOiJyMDEscjAyIiwibmFtZSI6Im1ydCIsImlkIjoiMTIzIn0.KK7_67N5d1Dthd1PgDHMsbi0UlmjGRcm_XJUUwseJ2eZyJJWoPP2IcEZgAU3tUaaKEHUf9wSRwaDgwhrwfyIcSHbs8oy3zOQEL8j5AOjzBBs7vnRmB7DbSaQD7eJiQVJOXO1QpdmEFgjhc_IBCVTJCVWgZw60IEW1_Lg5tqaLvCiIl26K48pJB5f-le2zgYMzqR1L2LyTFkq39rG57VOqqSCi3dapsZQd4ctq95SJCXgGdrUDWtD52rp5o6_0uq-mrbRdRxkrQfsa1j8C5IW2-T4eUmiN3f9wF9JxUK1__XC1OQkOn-ZTBCdqwWIygDFbU7sf6KzfHJTm5vfjp6NIA";
            //公钥
            String publickey = "-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAijyxMdq4S6L1Af1rtB8SjCZHNgsQG8JTfGy55eYvzG0B/E4AudR2prSRBvF7NYPL47scRCNPgLnvbQczBHbBug6uOr78qnWsYxHlW6Aa5dI5NsmOD4DLtSw8eX0hFyK5Fj6ScYOSFBz9cd1nNTvx2+oIv0lJDcpQdQhsfgsEr1ntvWterZt/8r7xNN83gHYuZ6TM5MYvjQNBc5qC7Krs9wM7UoQuL+s0X6RlOib7/mcLn/lFLsLDdYQAZkSDx/6+t+1oHdMarChIPYT1sx9Dwj2j2mvFNDTKKKKAq0cv14Vrhz67Vjmz2yMJePDqUi0JYS2r0iIo7n8vN7s83v5uOQIDAQAB-----END PUBLIC KEY-----";
            //校验jwt
            Jwt jwt = JwtHelper.decodeAndVerify(token, new RsaVerifier(publickey));
            //获取jwt原始内容
            String claims = jwt.getClaims();
            //jwt令牌
            String encoded = jwt.getEncoded();
            System.out.println(encoded);
        }

    到这里本文就告一段落了,如果的新的内容将会随时更新的,尽请关注。

    如果感觉写的好,确实帮助了你,那就麻烦帮忙点个赞,多谢!

    如果需要转载,请注明出处,谢谢!本文为博主原创文章,博客地址:https://blog.csdn.net/weixin_44299027

  • 相关阅读:
    Codeforces1420E. Battle Lemmings 题解 动态规划
    C++使用partial_sum求前缀和
    HDU6171 Admiral 题解 折半搜索
    HDU3017 Treasure Division 题解 折半搜索
    使用re.split 按标点+空格的一种分割办法
    实现CString的Format功能,支持跨平台
    转载一篇makefile,说的很详细
    Microsoft C++ 异常: std::system_error std::thread
    源码一样,运行结果不一致的解决办法
    记录一次阿里的电话面试
  • 原文地址:https://www.cnblogs.com/no8g/p/13415647.html
Copyright © 2020-2023  润新知