1.问题介绍
前端:angularjs
后端:springcloud
登录时,前端页面对password通过RSA加密后传入后端API接口,后端对RSA进行解密,......
通过系统页面登录一切正常,但是通过postman调用登录接口会报错。
下面是用Postman测试接口,
- DUBBO - 服务响应: Client[consumer],InterfaceName=[SharetekRemoteClientService],MethodName=[queryByUserName],SpendTime=[23ms]
解密前1:G7BzsJn6LiCvXifIwW9KchNZq2u98dws9jLTCrkhjfLzXzmYRGaQr1lpYC1Y41TeBQ4h+BJZ2/1NPYu9FMrXDoYaOslNo0seCM9Ytg3XNxmvQZ6KDzxQxsDUorm4GPI1FvhI/tSOz9eSdohaWoQXzNkD4zUxX/8mKmqTt5NZUZY=
2024-07-03 18:43:12 [XNIO-1 task-2] INFO c.s.c.log.event.LogEventListener
- [127.0.0.1]内网IP[admin][Success][登录成功]
2024-07-03 18:43:12 [XNIO-1 task-2] INFO c.s.c.d.filter.DubboRequestFilter
- DUBBO - 服务调用: Client[consumer],InterfaceName=[RemoteLogService],MethodName=[saveLogininfor]
2024-07-03 18:43:12 [XNIO-1 task-2] INFO c.s.c.d.filter.DubboRequestFilter
- DUBBO - 服务响应: Client[consumer],InterfaceName=[RemoteLogService],MethodName=[saveLogininfor],SpendTime=[2ms]
2024-07-03 18:43:12 [XNIO-1 task-2] INFO c.s.auth.listener.UserActionListener
- user doLogin, useId:PC:1805808875250122752, token:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJsb2dpblR5cGUiOiJsb2dpbiIsImxvZ2luSWQiOiJQQzoxODA1ODA4ODc1MjUwMTIyNzUyIiwicm5TdHIiOiIyREhHcmx4NFhoa0g3eDJ5b2Z3ZWJNVkNpbllvYTZkWCIsInVzZXJJZCI6MTgwNTgwODg3NTI1MDEyMjc1MiwidXNlck5hbWUiOiJhZG1pbiIsImRlcHRJZCI6MSwiZGVwdE5hbWUiOiIiLCJkZXB0Q2F0ZWdvcnkiOiIifQ.ttv1dLxjSKPY8SO9JpyKsSl_jGyPva-yXQXc40pfd9Y
2024-07-03 18:43:12 [XNIO-1 task-2] INFO c.s.auth.listener.UserActionListener
- user doLogout, useId:PC:1805808875250122752, token:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJsb2dpblR5cGUiOiJsb2dpbiIsImxvZ2luSWQiOiJQQzoxODA1ODA4ODc1MjUwMTIyNzUyIiwicm5TdHIiOiJINEE3b2FUQ1BmWFFUZzB0UTI5M1l5c3I2U1JmUlp2cCIsInVzZXJJZCI6MTgwNTgwODg3NTI1MDEyMjc1MiwidXNlck5hbWUiOiJhZG1pbiIsImRlcHRJZCI6MSwiZGVwdE5hbWUiOiIiLCJkZXB0Q2F0ZWdvcnkiOiIifQ.BLGAMhsQvDza7AHGk1q2j7acF6UWg8n6psQp2rQNgp0
2024-07-03 18:43:17 [timeoutChecker_1_1] ERROR i.s.c.r.n.NettyClientChannelManager
- no available service endpoint found in cluster 'default', please make sure registry config correct and keep your seata-server is running
2024-07-03 18:43:18 [timeoutChecker_2_1] ERROR i.s.c.r.n.NettyClientChannelManager
- no available service endpoint found in cluster 'default', please make sure registry config correct and keep your seata-server is running
2024-07-03 18:43:27 [timeoutChecker_1_1] ERROR i.s.c.r.n.NettyClientChannelManager
- no available service endpoint found in cluster 'default', please make sure registry config correct and keep your seata-server is running
2024-07-03 18:43:28 [timeoutChecker_2_1] ERROR i.s.c.r.n.NettyClientChannelManager
- no available service endpoint found in cluster 'default', please make sure registry config correct and keep your seata-server is running
2024-07-03 18:43:37 [timeoutChecker_1_1] ERROR i.s.c.r.n.NettyClientChannelManager
- no available service endpoint found in cluster 'default', please make sure registry config correct and keep your seata-server is running
2024-07-03 18:43:38 [timeoutChecker_2_1] ERROR i.s.c.r.n.NettyClientChannelManager
- no available service endpoint found in cluster 'default', please make sure registry config correct and keep your seata-server is running
2024-07-03 18:43:40 [XNIO-1 task-2] INFO c.s.c.d.filter.DubboRequestFilter
- DUBBO - 服务调用: Client[consumer],InterfaceName=[SharetekRemoteClientService],MethodName=[queryByUserName]
2024-07-03 18:43:40 [XNIO-1 task-2] INFO c.s.c.d.filter.DubboRequestFilter
- DUBBO - 服务响应: Client[consumer],InterfaceName=[SharetekRemoteClientService],MethodName=[queryByUserName],SpendTime=[29ms]
2024-07-03 18:43:40 [XNIO-1 task-2] INFO c.s.c.d.filter.DubboRequestFilter
- DUBBO - 服务调用: Client[consumer],InterfaceName=[SharetekRemoteClientService],MethodName=[queryByUserName]
2024-07-03 18:43:40 [XNIO-1 task-2] INFO c.s.c.d.filter.DubboRequestFilter
- DUBBO - 服务响应: Client[consumer],InterfaceName=[SharetekRemoteClientService],MethodName=[queryByUserName],SpendTime=[30ms]
解密前1:G7BzsJn6LiCvXifIwW9KchNZq2u98dws9jLTCrkhjfLzXzmYRGaQr1lpYC1Y41TeBQ4h BJZ2/1NPYu9FMrXDoYaOslNo0seCM9Ytg3XNxmvQZ6KDzxQxsDUorm4GPI1FvhI/tSOz9eSdohaWoQXzNkD4zUxX/8mKmqTt5NZUZY=
2024-07-03 18:43:40 [XNIO-1 task-2] ERROR c.s.c.w.h.GlobalExceptionHandler
- 请求地址'/api/token',发生未知异常.
cn.hutool.crypto.CryptoException: BadBlockException: unable to decrypt block
at cn.hutool.crypto.asymmetric.AsymmetricCrypto.decrypt(AsymmetricCrypto.java:277)
at cn.hutool.crypto.asymmetric.RSA.decrypt(RSA.java:174)
at cn.hutool.crypto.asymmetric.AsymmetricDecryptor.decrypt(AsymmetricDecryptor.java:58)
at cn.hutool.crypto.asymmetric.AsymmetricDecryptor.decryptStr(AsymmetricDecryptor.java:71)
at cn.hutool.crypto.asymmetric.AsymmetricDecryptor.decryptStr(AsymmetricDecryptor.java:83)
at com.sharetek.auth.utils.RSAUtils.decrypt(RSAUtils.java:77)
at com.sharetek.auth.service.impl.SharetekPasswordAuthStrategy.sharetekLogin(SharetekPasswordAuthStrategy.java:122)
at com.sharetek.auth.service.ISharetekAuthStrategy.login(ISharetekAuthStrategy.java:32)
at com.sharetek.auth.controller.SharetekTokenController.login(SharetekTokenController.java:99)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:255)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:188)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:926)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:831)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
密文对比如下:
//正确解密前1:G7BzsJn6LiCvXifIwW9KchNZq2u98dws9jLTCrkhjfLzXzmYRGaQr1lpYC1Y41TeBQ4h+BJZ2/1NPYu9FMrXDoYaOslNo0seCM9Ytg3XNxmvQZ6KDzxQxsDUorm4GPI1FvhI/tSOz9eSdohaWoQXzNkD4zUxX/8mKmqTt5NZUZY=
//postman解密前1:G7BzsJn6LiCvXifIwW9KchNZq2u98dws9jLTCrkhjfLzXzmYRGaQr1lpYC1Y41TeBQ4h BJZ2/1NPYu9FMrXDoYaOslNo0seCM9Ytg3XNxmvQZ6KDzxQxsDUorm4GPI1FvhI/tSOz9eSdohaWoQXzNkD4zUxX/8mKmqTt5NZUZY=
2.解决方法
对rsa密文里的所有空格用'+'替换即可。
3.RSA加解密
在前端用js进行rsa加密和在java端用hutool进行rsa加密,秘钥相同的前提下,生成的密文格式不同,java生成的密文更长,但是在服务端用hutool解密后的结果是一样的,前端js的密文用hutool包也可以完成解密。
// js端对Sharetek加密rsa后的密文
G7BzsJn6LiCvXifIwW9KchNZq2u98dws9jLTCrkhjfLzXzmYRGaQr1lpYC1Y41TeBQ4h+BJZ2/1NPYu9FMrXDoYaOslNo0seCM9Ytg3XNxmvQZ6KDzxQxsDUorm4GPI1FvhI/tSOz9eSdohaWoQXzNkD4zUxX/8mKmqTt5NZUZY=
// 用hutool包进行SRA加密后:0d4109384740aa7508e47ec58a6a1870027b5cb4ae68de1343f49e6d2a1d8becfc6da7a3f3521323597522f54f896ffd271e458e406be0317532649becc5b88de9827c6c56ca2ba8f6a167cd889f8160a3f12473b4ff91c93b85d8dd61fad646c1394cc4d4e78db07776c4d445b704ac09d6958980ab3da37ab54bfeda6ded86
4.hutool包RSA加解密工具类
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-crypto</artifactId>
<version>5.8.27</version>
</dependency>
package com.sharetek.auth.utils;
import cn.hutool.core.io.file.FileReader;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
/**
* @author tianjunmou
* @title RSAUtils
* @date 2024/6/25 14:50
* @description RSA加解密工具类
*/
public class RSAUtils {
// 在classpath下的公私钥文件
// 文本格式的私钥内容(不包含-----BEGIN PRIVATE KEY-----,-----END PRIVATE KEY-----)
private static String pubKeyFilePathInClasspath = "keys/public_key.pem";
private static String priKeyFilePathInClasspath = "keys/private_key.pem";
public static void main(String[] args) throws Exception {
// 要加密的文本
String text = "Sharetek";
System.out.println("原始密码:" + text);
long startTime = System.currentTimeMillis();
// 获取公钥并进行rsa加密
String encryptedString = encrypt(text, getPublicKey(pubKeyFilePathInClasspath));
System.out.println("SRA加密后:" + encryptedString);
// 获取私钥并进行rsa解密
// encryptedString = "Kz72esN2/Ndx5wUmYPT9ntCFsbzrHCB1qKZu0AlCGDkLfmsnpW4b6wxT+iDEIQ9KAHRGkHgwi0DpgibmU5mxjsVQ+Ep1dvzvLyZP+HPLNitmjSf3F9149lLLqQRfrh5CrJdqRQb2mcUcA7NwVDa28qebdpkH8kahrVTPW2cZZ+Q=";
// encryptedString = "i4Q01UvbWIA0M2kS4iWFd0Bh6th/bDSS0I/tQ2xGG/MEFLJY3KgQgx5UBI5xKHA05/7oAPf7X2E+M5t/k7/nIOkafMX6QZTy6kSkcyipO1Nc8s68FLVYUWQWWD9uPm5xYAYTlh43aAfolEoow5XeeoMIih7mIJomq5SQ0CoTK2Q=";
encryptedString = "Fwoz75HNOffQfwgRkNt7IUJZoEuLrCI5gSZjpAx4npo2fe0gH6nlAO+hi6r3YwH7tC8mqgp1GP0mvujATpQEz4Zl+aZfkm/f8P0sqJTj6dBCPu2ph3Wcg1SvXI0TGeDsi+iyeTrzNM2cww53AeTpJS332bi39vso3p8iMA0aQL0=";
String decryptedString = decrypt(encryptedString);
System.out.println("耗时:" + (System.currentTimeMillis() - startTime) + "ms");
System.out.println("解密后:" + decryptedString);
}
/**
* 使用公钥加密
*
* @param text 待使用rsa加密的明文
* @param publicKey 文本格式的公钥内容(不包含-----BEGIN PUBLIC KEY-----,-----END PUBLIC KEY-----)
* @return
*/
private static String encrypt(String text, String publicKey) {
return new RSA(null, publicKey).encryptHex(text, KeyType.PublicKey);
}
/**
* 使用私钥解密
*
* @param encryptedString 使用rsa加密的密文串
* @return
*/
public static String decrypt(String encryptedString) {
System.out.println("解密前1:" + encryptedString);
encryptedString = encryptedString.replaceAll(" ", "+");
System.out.println("解密前2:" + encryptedString);
String privateKey = null;
String publickKey = null;
try {
privateKey = getPrivateKey(priKeyFilePathInClasspath);
publickKey = getPublicKey(pubKeyFilePathInClasspath);
} catch (Exception e) {
throw new RuntimeException(e);
}
return new RSA(privateKey, null).decryptStr(encryptedString, KeyType.PrivateKey);
}
/**
* 从公钥文件中读取公钥数据
*
* @param pubKeyFilePathInClasspath classpath下的公钥文件路径
* @return
* @throws Exception
*/
private static String getPublicKey(String pubKeyFilePathInClasspath) throws Exception {
return new FileReader(getAbsolutePath(pubKeyFilePathInClasspath)).readString();
}
/**
* 从私钥文件中读取私钥数据
*
* @param priKeyFilePathInClasspath classpath下的私钥文件路径
* @return
* @throws Exception
*/
private static String getPrivateKey(String priKeyFilePathInClasspath) throws Exception {
return new FileReader(getAbsolutePath(priKeyFilePathInClasspath)).readString();
}
/**
* 获取classpath下指定文件的绝对路径
*
* @param filePathInClasspath classpath下文件路径
* @return
*/
private static String getAbsolutePath(String filePathInClasspath) {
return ClassLoader.getSystemClassLoader()
.getResource(filePathInClasspath).getPath();
}
}
参考资料: