JwtAccessConverter

Mac

TestMacSigner

public class TestMacSigner {

    public static void main(String[] args) {

        // 创建1个MacSigner
        MacSigner macSigner = new MacSigner("abc123");

        // 使用 macSigner 对 "data" 进行签名
        byte[] signedBytes = macSigner.sign("data".getBytes());
        // macSigner 对 "data" 签名 的内容进行输出(直接转为字符串会乱码)
        System.out.println(Arrays.toString(signedBytes));

        // macSigner 对 "data" 签名 的内容 进行 base64 编码
        String based64Str = Base64.getEncoder().encodeToString(signedBytes);
        // 将签名的字节 以 base64编码 输出
        System.out.println(based64Str);
        // 将签名的字节 进行 base64编码 得到的字符串, 使用 base64解码 得到的内容 输出, 查看与原内容是否相同
        System.out.println(Arrays.toString(Base64.getDecoder().decode(based64Str)));

        // 使用macSigner 对 内容 与 签名 进行验签
        // (其实就是对 提供的内容 再一次签名 得到签名结果, 与 提供的签名 比较是否相同。
        //   如果相同, 说明使用的是同样的密钥, 如果不同, 说明使用的是不同的密钥。进而说明一定是拥有密钥的人通过签名颁发的此令牌)
        macSigner.verify("data".getBytes(), signedBytes);

    }

}

Rsa

使用jwt(非对称加密)
可参考:https://www.cnblogs.com/hellxz/p/12044340.html

生成jks证书

需要先安装openssl

https://slproweb.com/products/Win32OpenSSL.html下载对应的openssl版本,只需要一直点下一步即可,安装完毕后,将它的bin目录配置到环境变量
在这里插入图片描述

keytool生成jks (Java Key Store) 文件

首先使用keytool生成jks (Java Key Store) 证书,每个证书包含公钥和私钥`

keytool -genkeypair -alias my-auth -keyalg RSA -keypass 123456 -keystore my-auth.jks -storepass 123456
  • alias: 秘钥别名
  • keyalg: 使用的hash算法
  • keypass: 秘钥访问密码
  • keystore: 秘钥库文件名,生成证书文件
  • storepass: 证书的访问密码

在这里插入图片描述

在my-auth.jks同目录下,执行命令导出公钥,需要输入口令:123456

keytool -list -rfc --keystore my-auth.jks | openssl x509 -inform pem -pubkey

在这里插入图片描述

测试密钥
package com.zzhua;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Test;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.jwt.Jwt;
import org.springframework.security.jwt.JwtHelper;
import org.springframework.security.jwt.crypto.sign.RsaSigner;
import org.springframework.security.jwt.crypto.sign.RsaVerifier;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;

import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class TestJwt {

    @Test
    public void test_encodeJWT() throws JsonProcessingException, NoSuchAlgorithmException, InvalidKeySpecException {

        // (这里读取jks文件的流程可以参考:
        //		ResourceServerTokenServicesConfiguration$JwtKeyStoreConfiguration#accessTokenConverter()的源码处理过程)
        /*
        	// 这个bean只有在JwtKeyStoreCondition条件满足时,才会生效
        	// (亦即:只要配置了security.oauth2.resource.jwt.key-store就会生效)
        	@Bean
			public JwtAccessTokenConverter accessTokenConverter() {

				// security.oauth2.resource.jwt.key-store
				// security.oauth2.resource.jwt.key-store-password
				// security.oauth2.resource.jwt.key-alias
				// 如果提供了key-store, 则以上三个不能为空
				Assert.notNull(this.resource.getJwt().getKeyStore(), "keyStore cannot be null");
				Assert.notNull(this.resource.getJwt().getKeyStorePassword(), "keyStorePassword cannot be null");
				Assert.notNull(this.resource.getJwt().getKeyAlias(), "keyAlias cannot be null");

				JwtAccessTokenConverter converter = new JwtAccessTokenConverter();

				// 拿到jks证书
				Resource keyStore = this.context.getResource(this.resource.getJwt().getKeyStore());
				// 证书访问密码
				char[] keyStorePassword = this.resource.getJwt().getKeyStorePassword().toCharArray();
				KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(keyStore, keyStorePassword);

				// 秘钥别名
				String keyAlias = this.resource.getJwt().getKeyAlias();
				// 密钥访问密码 ,先获取秘钥访问密码,如果没有提供,则使用证书访问密码(所以最好这2个保持一致)
				char[] keyPassword = Optional.ofNullable(
						this.resource.getJwt().getKeyPassword())
						.map(String::toCharArray).orElse(keyStorePassword);

				 // 这个setKeyPair方法在下面有解释
				 converter.setKeyPair(keyStoreKeyFactory.getKeyPair(keyAlias, keyPassword));

			return converter;
		}


		*/
        //加载证书(参数:jks证书文件名)
        ClassPathResource classPathResource = new ClassPathResource("my-auth.jks");

        //密钥库(第二个参数:证书的访问密码)
        KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(classPathResource, "123456".toCharArray());

        //获取秘钥对(第一个参数: 密钥别名;第二个参数:证书里面的密钥的访问密码)
        KeyPair keyPair = keyStoreKeyFactory.getKeyPair("my-auth", "123456".toCharArray());

        //获取私钥 , 私钥加密,公钥验证,是谓签名
        RSAPrivateKey privateKey = (RSAPrivateKey)keyPair.getPrivate();

        // 获取公钥 (与通过命令获取的公钥一致,只不过没有换行符,一样的效果)
        byte[] decode = Base64.getEncoder().encode(keyPair.getPublic().getEncoded());
        String s = new String(decode);
        System.out.println(s);
        // MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgdlyFwN0MWeLGvqrXbdZsvgHbtTWWpr/bzTlydZt5w0S1TUHwhwZiMXuMWvJU2wtlrw0AlftRbsqEy11sFECwtZ50/V7aEd1O1nHHCrVXgUjjktKBW02mmqF5AjlIMqTeS0QFB2iOI3Zs0YcbIIMP8DQFG5laKg4/C3F0LzSDqLOk1GH14+p+EdJ+fgvu0ip9s6eDA2mVF6Og5PlXdYjnvQwQDduZd/zxzXXxT9ZP4kQDRULjNxleJKljRwIa/mEJokWR4Xmu41uFetBpIjYtLV9teaKEB0GT0XVYmMSmonkqB4pHx8VUknNs7AQgQgniNBLX0nuuqiVx1q2wNxvIQIDAQAB

        //准备载荷数据
        Map<String,Object> data = new HashMap<>();
        data.put("id",1L);
        data.put("username","zzhua");
        data.put("role","admin");
        String content = new ObjectMapper().writeValueAsString(data);
        System.out.println(content); // {"role":"admin","id":1,"username":"zzhua"}

        //创建令牌
        Jwt jwt = JwtHelper.encode(content, new RsaSigner(privateKey));
        //获取创建的令牌
        String token = jwt.getEncoded();

        System.out.println(token);
        // eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.
        // eyJyb2xlIjoiYWRtaW4iLCJpZCI6MSwidXNlcm5hbWUiOiJ6emh1YSJ9.
        // Yw-qmMtW6qqIkrj-8i_3m9QxIMOL9Xb7BKHGUIy7V4sTfkmJ7jAVTD6ij_vAHg_nNBrUtxWxcUnOLWCZGAiMKzjGl4yms8ThFQCpM5LNdH0crZi1fRZHKM0LbMSnPKO7IsuogiTbXNYdk-W3zcmk2zWoNiKMRtbflWlrKJMJEOjv5CPJwxBoWw6v0YIbFDGYC8YijnJ_b-U9YchSlpshEAiAO96l3OfwcTN99nXvMnQoGA-iOQaVNXMU_0Qa-A6xxfL8NtpqLX76ucLr6CcgmJky-VT4SyDOlA7AwiM2nsXwllBkA84T8AtFA9xSvlzN6yKPeAu_PKbJucTg8O51dw


        // 在上面的过程中, 我们把公钥的字节转成了Base64编码后的字符串,
        // 并且注意到编码后的字符串和使用命令获取的公钥是一致的。
        // 既然公钥可以这样玩,那么私钥是不是也可以这样玩呢?
        // 如果可以这样玩的话,那在后面的处理过程中就可以舍弃掉这个jks文件了,直接使用公钥和私钥的字符串就行了
        // (只是说可以这样玩,但是这样玩私钥就以明文的方式暴露了)
        // 私钥以base64字符串编码的形式暴露出去后,该怎么使用呢?
        byte[] encoded = privateKey.getEncoded(); // 获取到私钥的字节数据
        byte[] encoded2 = Base64.getEncoder().encode(encoded);// 对私钥的字节进行base64编码
        String s1 = new String(encoded2); // 获取到私钥进行base64编码后的字符串(这个就可以暴露给外界了, 使用base64编码的好处是,不会出现乱码的情况)

        byte[] decode1 = Base64.getDecoder().decode(s1.getBytes()); // 使用外界暴露的私钥字符串,获取到base64编码后的字节,然后对这些字节数据解码,来获取原始的字节数据

        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decode1);
        RSAPrivateKey generatePrivate = (RSAPrivateKey) keyFactory.generatePrivate(keySpec); // 强转成RSAPrivateKey
        Jwt jwt2 = JwtHelper.encode(content, new RsaSigner(generatePrivate)); // 使用新的私钥对象
        //获取创建的令牌
        String token2 = jwt2.getEncoded();
        System.out.println(token2); // 生成的token与前面完全一致
        // eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.
        // eyJyb2xlIjoiYWRtaW4iLCJpZCI6MSwidXNlcm5hbWUiOiJ6emh1YSJ9.
        // Yw-qmMtW6qqIkrj-8i_3m9QxIMOL9Xb7BKHGUIy7V4sTfkmJ7jAVTD6ij_vAHg_nNBrUtxWxcUnOLWCZGAiMKzjGl4yms8ThFQCpM5LNdH0crZi1fRZHKM0LbMSnPKO7IsuogiTbXNYdk-W3zcmk2zWoNiKMRtbflWlrKJMJEOjv5CPJwxBoWw6v0YIbFDGYC8YijnJ_b-U9YchSlpshEAiAO96l3OfwcTN99nXvMnQoGA-iOQaVNXMU_0Qa-A6xxfL8NtpqLX76ucLr6CcgmJky-VT4SyDOlA7AwiM2nsXwllBkA84T8AtFA9xSvlzN6yKPeAu_PKbJucTg8O51dw

        // 既然私钥以base64编码的方式暴露出去了,那么公钥也以这种方式暴露出去,该怎么用呢?
        RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
        byte[] encode = Base64.getEncoder().encode(rsaPublicKey.getEncoded());
        String s2 = new String(encode); // 暴露给外界的公钥字符串

        // 解析暴露给外界的公钥字符串,以获取RSA公钥对象
        X509EncodedKeySpec keySpec2 = new X509EncodedKeySpec(Base64.getDecoder().decode(s2.getBytes()));
        RSAPublicKey rsaPublicKey2 = (RSAPublicKey) keyFactory.generatePublic(keySpec2);
        String JWTToken = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYWRtaW4iLCJpZCI6MSwidXNlcm5hbWUiOiJ6emh1YSJ9.Yw-qmMtW6qqIkrj-8i_3m9QxIMOL9Xb7BKHGUIy7V4sTfkmJ7jAVTD6ij_vAHg_nNBrUtxWxcUnOLWCZGAiMKzjGl4yms8ThFQCpM5LNdH0crZi1fRZHKM0LbMSnPKO7IsuogiTbXNYdk-W3zcmk2zWoNiKMRtbflWlrKJMJEOjv5CPJwxBoWw6v0YIbFDGYC8YijnJ_b-U9YchSlpshEAiAO96l3OfwcTN99nXvMnQoGA-iOQaVNXMU_0Qa-A6xxfL8NtpqLX76ucLr6CcgmJky-VT4SyDOlA7AwiM2nsXwllBkA84T8AtFA9xSvlzN6yKPeAu_PKbJucTg8O51dw";
        Jwt decodedJwt = JwtHelper.decodeAndVerify(JWTToken, new RsaVerifier(rsaPublicKey2));
        //获取载荷数据
        String claims = decodedJwt.getClaims();
        System.out.println(claims);
        //{"role":"admin","id":1,"username":"zzhua"}
    }

    // 注意一下,下面的校验只会校验签名是否正确(即: 被签名的内容有没有被篡改),不会校验jwt令牌是否过期
    @Test
    public void test_decodeJWT() {
        //JWT的token
        String JWTToken = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYWRtaW4iLCJpZCI6MSwidXNlcm5hbWUiOiJ6emh1YSJ9.Yw-qmMtW6qqIkrj-8i_3m9QxIMOL9Xb7BKHGUIy7V4sTfkmJ7jAVTD6ij_vAHg_nNBrUtxWxcUnOLWCZGAiMKzjGl4yms8ThFQCpM5LNdH0crZi1fRZHKM0LbMSnPKO7IsuogiTbXNYdk-W3zcmk2zWoNiKMRtbflWlrKJMJEOjv5CPJwxBoWw6v0YIbFDGYC8YijnJ_b-U9YchSlpshEAiAO96l3OfwcTN99nXvMnQoGA-iOQaVNXMU_0Qa-A6xxfL8NtpqLX76ucLr6CcgmJky-VT4SyDOlA7AwiM2nsXwllBkA84T8AtFA9xSvlzN6yKPeAu_PKbJucTg8O51dw";
        //公钥验证,通过 ‘keytool -list -rfc --keystore whale.jks | openssl x509 -inform pem -pubkey’ 得到公钥
        String publicKey = "-----BEGIN PUBLIC KEY-----\n" +
                "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgdlyFwN0MWeLGvqrXbdZ\n" +
                "svgHbtTWWpr/bzTlydZt5w0S1TUHwhwZiMXuMWvJU2wtlrw0AlftRbsqEy11sFEC\n" +
                "wtZ50/V7aEd1O1nHHCrVXgUjjktKBW02mmqF5AjlIMqTeS0QFB2iOI3Zs0YcbIIM\n" +
                "P8DQFG5laKg4/C3F0LzSDqLOk1GH14+p+EdJ+fgvu0ip9s6eDA2mVF6Og5PlXdYj\n" +
                "nvQwQDduZd/zxzXXxT9ZP4kQDRULjNxleJKljRwIa/mEJokWR4Xmu41uFetBpIjY\n" +
                "tLV9teaKEB0GT0XVYmMSmonkqB4pHx8VUknNs7AQgQgniNBLX0nuuqiVx1q2wNxv\n" +
                "IQIDAQAB\n" +
                "-----END PUBLIC KEY-----";
        //解密和验证令牌(在这里,我们可以看到公钥就是一个字符串,可以直接封装到RsaVerifier中,
        //             所以只要暴露了这个公钥字符串, 资源服务器拿着这个公钥字符串,就可以校验签名了)
        Jwt jwt = JwtHelper.decodeAndVerify(JWTToken, new RsaVerifier(publicKey));
        //获取载荷数据
        String claims = jwt.getClaims();
        System.out.println(claims);
        //{"role":"admin","id":1,"username":"zzhua"}

    }
}

相关推荐

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-06-08 10:14:01       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-06-08 10:14:01       106 阅读
  3. 在Django里面运行非项目文件

    2024-06-08 10:14:01       87 阅读
  4. Python语言-面向对象

    2024-06-08 10:14:01       96 阅读

热门阅读

  1. npm发布自己的插件包

    2024-06-08 10:14:01       36 阅读
  2. npm发布自己的插件包

    2024-06-08 10:14:01       28 阅读
  3. ubantu安装第三库到指定目录

    2024-06-08 10:14:01       31 阅读
  4. C#面:AJAX的底层实现原理

    2024-06-08 10:14:01       31 阅读
  5. 类的定义和对象的引用

    2024-06-08 10:14:01       30 阅读
  6. c++ 左右值与引用折叠

    2024-06-08 10:14:01       29 阅读
  7. 【例0808】create daxis using face 使用面创建基准轴

    2024-06-08 10:14:01       31 阅读
  8. 【Linux】GNU编译器基础

    2024-06-08 10:14:01       28 阅读
  9. 微信小程序按钮设计与交互:打造极致用户体验

    2024-06-08 10:14:01       29 阅读