IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> 针对JWT简介与原理代码实例以及oauth2+JWT+RSA的集成配置 -> 正文阅读

[系统运维]针对JWT简介与原理代码实例以及oauth2+JWT+RSA的集成配置

JWT简介

  • JWT 是基于 RFC 7519 标准定义的一种可以安全传输的规范, 这个规范允许我们使用 JWT 在前后端之间传递安全可靠的信息。
  • JWT 由于使用了数字签名,所以是可信任和安全的。

通过 session 管理用户登录状态成本越来越高,使用无状态的 token 的方式做登录身份校验显得越来越流行,不仅可以减轻服务器的压力, 由于 token 是在授权头(Authorization header)中发送的,因此做单点登陆的时候还没有cookie跨域问题.

而 JWT(java web token) 就是目前最流行的单点登录跨域身份验证解决方案。

JWT 组成

一个 JWT 实际上就是一个字符串,未加密前的 jwt 就是一个json, 它由头部、载荷与签名三部分组成。

  1. header(头部): 用于描述关于该 JWT 的最基本的信息,例如其类型以及签名所用的算法等。

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

    header 主要包含两个部分, alg 指加密类型,可选值为 HS256RSA 等等,typ=JWT 为固定值,表示 token 的类型。

  2. payload(载荷): 载荷就是存放有效信息的地方。

    {
      "sub": "1340502208",
      "name": "John Doe",
      "role": "admin"
    }
    

    官方定义的 payload 一般包括以下内容

    • iss:Issuer,发行者
    • sub:Subject,主题
    • aud:Audience,观众
    • exp:Expiration time,过期时间
    • nbf:Not before
    • iat:Issued at,发行时间
    • jti:JWT ID
  3. signature(签证): jwt 的第三部分是一个签证信息,这个签证信息由三部分组成

    1. 头部经 base64 编码后的字符
    2. 是载荷经 base64 编码后的字符
    3. 是盐(密钥),通常存于服务器

    之后将12.连接,通过头部中声明的加密算法进行加盐(3)计算,得到第三部分, 伪代码如下.

    EncodeString = Base64(header) + "." + Base64(payload)
    token = HS256(EncodeString, "秘钥")
    

    签名是用于验证消息在传递过程中有没有被更改,并且,对于使用私钥签名的 token,它还可以验证 JWT 的发送方是否为它所称的发送方。

  • 下面是一个JWT示例, 对应的密钥是 123456

       eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI0ODIzIiwic3ViIjoiQ1BGIiwiaWF0IjoxNjQzNDY1MzM3LCJleHAiOjE2NDM0Njg5MzcsInJvbGVzIjoiYWRtaW4sdXNlciIsImVtYWlsIjoiMTIzQG9oLmNvbSJ9.JBmv97KOYYNRsKKcHpy26uLPN6JrwJzV_WdqzIpwEx0
    

    可以去 https://jwt.io/ 上面去验证解析.

JWT 工作原理

  1. 首先, 用户登录后, 服务器向前台发送一个 JWT.

  2. 用户想要访问受保护的路由或者资源的时候,应当携带 JWT,通常放在请求头中的 Authorization 上, 如 Bearer token

  3. 服务器的路由或网关去检查 Authorization header 中的 JWT 是否有效,如果有效,则用户可以访问受保护的资源。

JWT 验证过程

  1. 签名验证

    服务器获取到一个 JWT 的时候,首先要对这个 JWT 的完整性进行验证,这个就是签名认证。

    验证方式如下:

    1. 截开 JWT 前缀, 根据上面内容来看是 Bearer, 获取到 token.

    2. 分离 token 为 header, payload, signature.

    3. 对 header 做 base64 解码, 获得 JWT 使用的算法编码.

    4. 使用后端保存的密钥, 和得到的编码对 header, payload 进行签证计算, 算出 signature.

      如 header 使用的编码为 HS256. 则执行以下伪代码.

      EncodeString = Base64(header) + "." + Base64(payload)
      token = HS256(EncodeString, "秘钥")
      

      接收方生成签名的时候必须使用跟 JWT 发送方相同的密钥,意味着要做好密钥的安全传递或共享

    5. 将算出的 signature 与 token 带的 signature 进行对比, 若完全相同, 表明信息是正确的. 若不同,就可以认为这个 JWT 是一个被篡改过的串,自然就属于验证失败了。

  2. 载体验证

    在验证一个 JWT 的时候,签名认证是每个实现库都会自动做的,但是 payload 的认证是由使用者来决定的。由于载体里面存放的内容根据需求各有不同, 因此, 载体验证一般需要由使用者亲自完成.

    以下是官方的载体验证规范.

    • iss(Issuser):如果签发的时候这个 claim 的值是“a.com”,验证的时候如果这个 claim 的值不是“a.com”就属于验证失败
    • sub(Subject):如果签发的时候这个 claim 的值是“liuyunzhuge”,验证的时候如果这个 claim 的值不是“liuyunzhuge”就属于验证失败
    • aud(Audience):如果签发的时候这个 claim 的值是“[‘b.com’,‘c.com’]”,验证的时候这个 claim 的值至少要包含 b.com,c.com 的其中- 一个才能验证通过
    • exp(Expiration time):如果验证的时候超过了这个 claim 指定的时间,就属于验证失败;nbf(Not Before):如果验证的时候小于这- 个 claim 指定的时间,就属于验证失败
    • iat(Issued at):它可以用来做一些 maxAge 之类的验证,假如验证时间与这个 claim 指定的时间相差的时间大于通过 maxAge 指定的一个- 值,就属于验证失败
    • jti(JWT ID):如果签发的时候这个 claim 的值是“1”,验证的时候如果这个 claim 的值不是“1”就属于验证失败

    以登录认证来说,在签发 JWT 的时候,完全可以只用 sub 跟 exp 两个 claim,用 sub 存储用户的 id,用 exp 存储它本次登录之后的过期时间,然后在验证的时候仅验证 exp 这个 claim,以实现会话的有效期管理。

JWT 生成与解析代码实例

  1. 引入依赖

    <dependency>
       <groupId>io.jsonwebtoken</groupId>
       <artifactId>jjwt</artifactId>
       <version>0.9.1</version>
    </dependency>
    
  2. 编写测试类

    import io.jsonwebtoken.Claims;
    import io.jsonwebtoken.JwtBuilder;
    import io.jsonwebtoken.Jwts;
    import io.jsonwebtoken.SignatureAlgorithm;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    
    import java.util.Date;
    
    public class JwtTest {
    
       @Test
       public void testJwt() throws InterruptedException {
          Date date = new Date();
    
          final String encodeString = "123456";
    
          //生成jwt令牌
          JwtBuilder jwtBuilder = Jwts.builder()
                   // 设置载荷 : jwt编码, 对象, 签发日期, 过期时间
                   .setId("4823")
                   .setSubject("CPF")
                   .setIssuedAt(date)
                   .setExpiration(new Date(date.getTime() + 3600 * 1000))
                   // 设置自定义的载荷
                   .claim("roles","admin,user")
                   .claim("email","123@oh.com")
                   // 使用 HS256 对称加密算法签名, 第二个参数为秘钥
                   .signWith(SignatureAlgorithm.HS256, encodeString);
    
          // 生成 jwt
          final String jwtToken = jwtBuilder.compact();
          System.out.println("生成的JWT: " + jwtToken);
    
          // 解析 jwt
          final Claims claims = Jwts.parser().setSigningKey(encodeString).parseClaimsJws(jwtToken).getBody();
          System.out.println("解析的内容: " + claims);
    
          // 更换一个 ID
          jwtBuilder.setId("1234");
          final String jwtToken2 = jwtBuilder.compact();
          System.out.println("\n替换id后的Jwt: " + jwtToken2);
    
          final String[] split1 = jwtToken.split("\\.");
          final String[] split2 = jwtToken2.split("\\.");
          final String errToken = split1[0] + "." + split2[1] + "." + split1[2];
          System.out.println("将两个JWT的载荷进行拼接JWT : " + errToken);
    
          System.out.println("\n然后就发现解析会失败!");
          // 解析 jwt
          Jwts.parser().setSigningKey(encodeString).parseClaimsJws(errToken).getBody();
       }
    
    }
    
  3. 输出

    生成的JWT: eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI0ODIzIiwic3ViIjoiQ1BGIiwiaWF0IjoxNjQzNDQ4MzEyLCJleHAiOjE2NDM0NTE5MTIsInJvbGVzIjoiYWRtaW4sdXNlciIsImVtYWlsIjoiMTIzQG9oLmNvbSJ9.h-0EDrWj7Lnqfz7Jnjt0DKrGwvdicaA8BKlxJroYWB0
    解析的内容: {jti=4823, sub=CPF, iat=1643448312, exp=1643451912, roles=admin,user, email=123@oh.com}
    
    替换id后的Jwt: eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxMjM0Iiwic3ViIjoiQ1BGIiwiaWF0IjoxNjQzNDQ4MzEyLCJleHAiOjE2NDM0NTE5MTIsInJvbGVzIjoiYWRtaW4sdXNlciIsImVtYWlsIjoiMTIzQG9oLmNvbSJ9.4cf8EQO1Vfm9Bcu2GCIJGlAt4eKbqb_Q_26-tfFUHao
    将两个JWT的载荷进行拼接JWT : eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxMjM0Iiwic3ViIjoiQ1BGIiwiaWF0IjoxNjQzNDQ4MzEyLCJleHAiOjE2NDM0NTE5MTIsInJvbGVzIjoiYWRtaW4sdXNlciIsImVtYWlsIjoiMTIzQG9oLmNvbSJ9.h-0EDrWj7Lnqfz7Jnjt0DKrGwvdicaA8BKlxJroYWB0
    
    然后就发现解析会失败!
    
    Exception in thread "main" io.jsonwebtoken.SignatureException: JWT signature does not match locally computed signature. JWT validity cannot be asserted and should not be trusted.
       at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:354)
       at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:481)
       at io.jsonwebtoken.impl.DefaultJwtParser.parseClaimsJws(DefaultJwtParser.java:541)
    

JWT 使用 RSA 加密实例

相对于 HS256 对称加密, RSA 非对称加密更加安全一些.

为了生成 JWT Token 我们还需要使用 RSA 算法来进行签名。这里我们使用 JDK 提供的证书管理工具 Keytool 来生成 RSA 证书 ,格式为 jks 格式。

JDK-keytool 生成证书命令参考:

keytool -genkey -alias felordcn -keypass felordcn -keyalg RSA -storetype PKCS12 -keysize 1024 -validity 365 -keystore d:/keystores/felordcn.jks -storepass 123456 -dname "CN=(Felord), OU=(felordcn), O=(felordcn), L=(zz), ST=(hn), C=(cn)"

命令参数详解

参数默认含义
genkey在用户主目录中创建一个默认文件".keystore",还会产生一个 mykey 的别名,mykey 中包含用户的公钥、私钥和证书
aliasmykey产生别名
keystore用户系统默认目录指定密钥库的名称(产生的各类信息将不在.keystore 文件中)
keyalgDSA指定密钥的算法 (如 RSA DSA(如果不指定默认采用 DSA))
validity指定创建的证书有效期多少天
keysize1024指定密钥长度
storepass指定密钥库的密码(获取 keystore 信息所需的密码)
keypass指定别名条目的密码(私钥的密码)
dname指定证书拥有者信息 例如: “CN=名字与姓氏,OU=组织单位名称,O=组织名称,L=城市或区域名称,ST=州或省份名称,C=单位的两字母国家代码”
list显示密钥库中的证书信息 keytool -list -v -keystore 指定 keystore -storepass 密码
v显示密钥库中的证书详细信息
export将别名指定的证书导出到文件 keytool -export -alias 需要导出的别名 -keystore 指定 keystore -file 指定导出的证书位置及证书名称 -storepass 密码
file参数指定导出到文件的文件名
delete删除密钥库中某条目 keytool -delete -alias 指定需删除的别 -keystore 指定 keystore -storepass 密码
printcert查看导出的证书信息 keytool -printcert -file yushan.crt
keypasswd修改密钥库中指定条目口令 keytool -keypasswd -alias 需修改的别名 -keypass 旧密码 -new 新密码 -storepass keystore 密码 -keystore sage
storepasswd修改 keystore 口令 keytool -storepasswd -keystore e:/yushan.keystore(需修改口令的 keystore) -storepass 123456(原始密码) -new yushan(新密码)
import将已签名数字证书导入密钥库 keytool -import -alias 指定导入条目的别名 -keystore 指定 keystore -file 需导入的证书

keytool 生成命令实操

  1. 用 JDK 中的 keytool 生成 RSA 证书, 生成 rsa.jws

    keytool -genkey -keyalg RSA -alias oh -keypass 212003 -storepass 456123 -keystore P:\git\rsa.jws

    其中 -alias oh -keypass 212003 -storepass 456123 我们要作为配置使用要记下来。我们要使用下面定义的这个类来读取证书

    • 在控制台输入之后, 会提示名字与姓氏, 组织名称之类的乱七八糟的东西, 先填就填, 不填可以直接回车通过, 最后在否的地方输入个 y 就可以.
    • 最后还会提示 迁移到行业标准格式 PKCS12 之类的, 可以不用管,
    D:\programing\sdk\jdk-current\bin>keytool -genkey -keyalg RSA -alias oh -keypass 212003 -storepass 456123 -keystore P:\git\rsa.jws
    您的名字与姓氏是什么?
    [Unknown]:  chen
    您的组织单位名称是什么?
    [Unknown]:  hydroxyl
    您的组织名称是什么?
    [Unknown]:
    您所在的城市或区域名称是什么?
    [Unknown]:
    您所在的省/市/自治区名称是什么?
    [Unknown]:
    该单位的双字母国家/地区代码是什么?
    [Unknown]:
    CN=chen, OU=hydroxy, O=Unknown, L=Unknown, ST=Unknown, C=Unknown是否正确?
    []:  y
    
    Warning:
    JKS 密钥库使用专用格式。建议使用 "keytool -importkeystore -srckeystore P:\git\rsa.jws -destkeystore P:\git\rsa.jws -deststoretype pkcs12" 迁移到行业标准格式 PKCS12。
    
  2. 通过代码去验证生成的rsa.jwt文件是否能正常使用

    
    import javax.crypto.BadPaddingException;
    import javax.crypto.Cipher;
    import javax.crypto.IllegalBlockSizeException;
    import javax.crypto.NoSuchPaddingException;
    import java.io.FileInputStream;
    import java.security.*;
    import java.security.cert.Certificate;
    import java.util.Base64;
    import java.util.Enumeration;
    
    public class RsaUtils {
    
       private static Signature getSignature(String algorithm, String provider) throws NoSuchAlgorithmException, NoSuchProviderException {
          Signature signature;
          if (null == provider || provider.length() == 0) {
                signature = Signature.getInstance(algorithm);
          } else {
                signature = Signature.getInstance(algorithm, provider);
          }
          return signature;
       }
    
       /**
       * 验签
       */
       public static boolean verify(byte[] message, byte[] signMessage, PublicKey publicKey, String algorithm,
                                     String provider) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException, SignatureException {
          Signature signature = getSignature(algorithm, provider);
          signature.initVerify(publicKey);
          signature.update(message);
          return signature.verify(signMessage);
       }
    
       /**
       * 签名
       */
       public static byte[] sign(byte[] message, PrivateKey privateKey, String algorithm, String provider) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException, SignatureException {
          Signature signature = getSignature(algorithm, provider);
          signature.initSign(privateKey);
          signature.update(message);
          return signature.sign();
       }
    
       /**
       * 公钥加密
       */
       public static byte[] encrypt(byte[] content, PublicKey publicKey) throws NoSuchPaddingException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException {
          Cipher cipher = Cipher.getInstance("RSA");
          cipher.init(Cipher.ENCRYPT_MODE, publicKey);
          return cipher.doFinal(content);
       }
    
       /**
       * 私钥解密
       */
       public static byte[] decrypt(byte[] content, PrivateKey privateKey) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
          Cipher cipher = Cipher.getInstance("RSA");
          cipher.init(Cipher.DECRYPT_MODE, privateKey);
          return cipher.doFinal(content);
       }
    
       @SuppressWarnings("java:S106")
       public static void main(String[] args) throws Exception {
    
          final String jwtFilePath = "P:\\git\\rsa.jws";
          final String storePass = "456123";
          final String keyPass = "212003";
          final String keyAlias = "oh";
    
          // 加载 JWS 文件
          KeyStore keyStore = KeyStore.getInstance("JKS");
          try (FileInputStream in = new FileInputStream(jwtFilePath)) {
                keyStore.load(in, storePass.toCharArray());
          }
    
          // 获取公钥
          Certificate certificate = keyStore.getCertificate(keyAlias);
          PublicKey publicKey = certificate.getPublicKey();
          String publicKeyString = Base64.getEncoder().encodeToString(publicKey.getEncoded());
          System.out.println("publicKey ==> " + publicKeyString);
    
          // 加载私钥, 加载私钥需要密码
          PrivateKey privateKey  = (PrivateKey) keyStore.getKey(keyAlias, keyPass.toCharArray());
          String privateKeyString = Base64.getEncoder().encodeToString(privateKey.getEncoded());
          System.out.println("privateKey ==> "+ privateKeyString);
          System.out.println();
    
          /* 公钥加密, 私钥解密 */
          final String secretString = "这是密文-这是密文-这是密文";
          System.out.println("加密前 ==> " + secretString);
    
          byte[] secretByte = encrypt(secretString.getBytes(), publicKey);
          System.out.println("公钥加密 ==> " + Base64.getEncoder().encodeToString(secretByte));
    
          byte[] decodeByte = decrypt(secretByte, privateKey);
          System.out.println("私钥解密 ==> " + new String(decodeByte));
          System.out.println();
    
    
          /* 私钥签名加密, 公钥验证 */
          final String signSecretString = "密文信息";
          // 测试签名
          final byte[] sha1withRSAS = sign(signSecretString.getBytes(), privateKey, "SHA1withRSA", null);
          System.out.println("私钥签名: " + Base64.getEncoder().encodeToString(sha1withRSAS));
    
          // 公钥验证
          boolean verify = verify(signSecretString.getBytes(), sha1withRSAS, publicKey, "SHA1withRSA", null);
          System.out.println("公钥验证: " + verify);
       }
    }
    
  3. 打印输出

    keyAlias: oh
    publicKey ==> MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlQUvw6ZEAhlxpSZWPyHuFsFiWAYgdSiM4hEVxzweZsOMian+VvsonVUzv9358Zqh+rZ/i1uQ5CeJmarfXL83LM7KNcv/O7YP98wh9N3BtCpyQwo9wcx0Ph7//T4mWnc75qCmTeOxIidi68NRisHDzg1T61u2LDyALWtb1SoOV5wt3Nts0cq+lPNxh4QTGGe2nP+mbI88VxQviCTUNneKPpBiJ7rQ2E8UYwC7WYrqLc7ZlpuYCUsZvDfsxFvBqWnCAuVMd/GxBkrOB+YbWCh7ez5TdEofU1LrgsPyrILRR7kW7NZZ2nCGfv/p8TEYqaDiyXZZzGvmDwsD/8b3YYgohwIDAQAB
    privateKey ==> MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCVBS/DpkQCGXGlJlY/Ie4WwWJYBiB1KIziERXHPB5mw4yJqf5W+yidVTO/3fnxmqH6tn+LW5DkJ4mZqt9cvzcszso1y/87tg/3zCH03cG0KnJDCj3BzHQ+Hv/9PiZadzvmoKZN47EiJ2Lrw1GKwcPODVPrW7YsPIAta1vVKg5XnC3c22zRyr6U83GHhBMYZ7ac/6ZsjzxXFC+IJNQ2d4o+kGInutDYTxRjALtZiuotztmWm5gJSxm8N+zEW8GpacIC5Ux38bEGSs4H5htYKHt7PlN0Sh9TUuuCw/KsgtFHuRbs1lnacIZ+/+nxMRipoOLJdlnMa+YPCwP/xvdhiCiHAgMBAAECggEAVM4O0JjeOxOfyQx4KJV2mRyUiuNxtTrOchim/CsKYhEG+ZD0XSuxgVfri1UX2JbXd4ZEL1p8qlqVxA2p724iSC2mhdcB+Uky7SIOcPuCMLW3MM+zNYbU4EVkCQpFaVZRkH38JnddZsJjWSheT0jV1X1gNKCMm8ASccaXDEhSwSgP2/0O7YbN2AuS6gP/9Ut/0zlX6KVNUQGmYZHA5Zyd2cPCsstkli/Jz9U6Cmyl4x0NH8PJo5d6Zyda/47BR5WHo37WIU6skejPYK94O4ymKnGGoT+SRFFaoMmrHgmAdX9uOoskYh+HHtwLbcQCNGo+8eiK8Lv6CQUQX4XLxeIbgQKBgQDfh7cero/xsPNGO/6vQ3vdFVwbwvb/7zjHRCS27F19Oz83B29Mj8WJbCAD+DNap16zrhby3cPPNUwvXocGbEltKrgnZWocBEpdoZRN4GgDt5lLuxUQ9ZI2DJscr9LSUJaIPD8r505xO4syHBRgshtXmF4dLgQHDDsPiaOKYxq75QKBgQCqqrdI5zn0GxFWFonTBQ4whPCr7O+dcmhCyyNZ3J/K9wF13xKTwfkDqeXPbyJLN1g0bUoLXZnBXbmJEvTndFIyaFS5qdWSXjeP2qGVYcDlNc8V3ynwk8LpnQH8e4CsDhbH1vb0F1kmvJH4c5ecrDTo5exwSEGUE58g6KSAWkFD+wKBgQCXtOVMdo8NKtpBHbDBxJxJNRj5Yn3+v54aZ54/Y/Yja1WBBJO+M4mOtgqYhxhbe2JjslCy7l3ZwMN/FrmvW0kORUMMweCdOTA7kdE0dYxCkZYB9uvaQcDE3BNeCdqckMNJnRIGuwrbAN182d/erKKv9aJSTYvAOMXQyspqvs5DHQKBgQCkMBKeP11guz2lbY9whJePFAYZ0JsBBNTLFXTP+dF8yL8N7+qGXgE7hhLByi/a3sarwUyPvJ+0CH/7IFKd7Sk6t2ZzK7F828lmSrZS6TVTDb5JU2WcvfqxFsyXYxV58R/3Z5YzY9bvzlA8DrCYGI/aU4Bw0QLN+0aGuWmw1aOeSwKBgDqbOYI6R7Lu5V5uBTD99cNfK9xtygLy3BVOQVAnyubUL1IWZMuwKKOiPAFRiSwin3OvcQVFJfndgifWRnUqWQdNktXr/xZGUa7t1yVSzuvLtZeTVkzjyeiWNLO2QqUdJsC4ZsDm9BUoK3IzFWCXrQxJU+MboiYAaopA5ZKJaRI7
    
    加密前 ==> 这是密文-这是密文-这是密文
    公钥加密 ==> HYQeegMdF9Jmp9jd2mvkGjaMJ0as/gVLnfp1WjxAG62/HNfVXQWpJZZa/4aioiHYP23JYuM3a84hMw+Rb2/fkZJrSWEZBF2IbOtzPsBi2EqOKrzTdnc4FyCWNnJHwMyz2TpTsQDuU1QB3imsuUvr2xz5OQpD4KiyiechpreWqFw8Ns+1lTMGyRIyTba9bFGTBvjR2KSBVro9cUN0XaCKiqMzwjg7E7CBiO4GgIfU6fCiGAPydnYUDsAufYv/qBSKv5EuFnXuo8uWzbfxaHqzHycQ/eRXlDHIfAIlYrk/jzFz2jEtUL6D49IFstHsykWzu+mzu/EXT8P4s5FWk6ZKzg==
    私钥解密 ==> 这是密文-这是密文-这是密文
    
    私钥签名: bXCQ974mmP5q3F19Pwzj3u+UHBTOWrRA057+RgWXu3EDbQa+w1pHo8S4YInI+JJFN18OPVoQOrIdoa3ipwJgTa1rNuDsCOE5FfPa2yqiHDpD/t6q11L5cGC91+Y5c46NSBrXUTjpta4t+0Y6Xld62vVrAHq7XdrieLkbzn2+gQGPbkxiLxkvHoJDTl0S5EB86qcMlezRcxKWvzGPmugK83YMMUcBVi12YHB583EJeQ2QKXIIJqOLE8FL45zudhqfVOcgXXifRs0T5mwN0hBEHgi4cc2jbbViZwodf81NM0USaolNPVcB1WbWcmCn/MUS2dp8vTDAN0cmOMVD17SMxw==
    公钥验证: true
    

附: 单点登录: Spring security + auth2+ JWT + RSA 相关局部配置

这仅仅是部分配置: 针对 Jwt 转换器 的配置

package cn.hydroxyl.sso.auth2.config;

import cn.hydroxyl.sso.auth2.config.comp.JwtTokenEnhancer;
import cn.hydroxyl.sso.auth2.service.UserServiceImpl;
import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.rsa.crypto.KeyStoreKeyFactory;

import java.security.KeyPair;
import java.util.ArrayList;
import java.util.List;

@AllArgsConstructor
@Configuration
@EnableAuthorizationServer
public class Oauth2ServerConfig extends AuthorizationServerConfigurerAdapter {

   private final UserServiceImpl userDetailsService;
   private final AuthenticationManager authenticationManager;
   private final JwtTokenEnhancer jwtTokenEnhancer;

   /**
   * 用来配置授权(authorization)以及令牌(token)的访问端点和令牌服务(token services)。
   *
   * 1. 密码模式下配置认证管理器 AuthenticationManager 以及 jwt
   * 2. 设置 AccessToken的存储介质tokenStore, 默认使用内存当做存储介质。
   */
   @Override
   public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
      List<TokenEnhancer> delegates = new ArrayList<>();
      delegates.add(jwtTokenEnhancer);
      delegates.add(accessTokenConverter());
      // 密码模式下使用 authenticationManager 管理器
      endpoints.authenticationManager(authenticationManager)
               // 配置加载用户信息的服务
               .userDetailsService(userDetailsService)
               .accessTokenConverter(accessTokenConverter());
   }

   /**
   * Jwt 转换器
   */
   @Bean
   public JwtAccessTokenConverter accessTokenConverter() {
      JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
      jwtAccessTokenConverter.setKeyPair(keyPair());
      return jwtAccessTokenConverter;
   }

   @Bean
   public KeyPair keyPair() {
      // rsa 密钥文件, 以及对应的 storePass, keyPass, alias
      final ClassPathResource rsaJksRes = new ClassPathResource("rsa.jks");
      final String storePass = "456123";
      final String keyPass = "212003";
      final String alias = "oh";
      return new KeyStoreKeyFactory(rsaJksRes, storePass.toCharArray()).getKeyPair(alias, keyPass.toCharArray());
   }
}

参考自:

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-01-30 19:17:57  更:2022-01-30 19:19:26 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/16 7:33:29-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码