使用 RSA 在 Web 前端加密传输至后台解密
在自定义登录过程中,用户名和密码通常是作为 form 表单中的 input 控件进行输入,然后明文传输到服务端进行校验登录的。
此时,可能会出现密码明文传输问题。如下图示例所示:
通常跟服务器的交互中,为保障数据传输的安全性,避免被人抓包篡改数据,除了 https 的应用,还需要对传输数据进行加解密。
本文介绍一种 RSA 加密 web 前端用户名密码加密传输至后台并解密 的方法。
公共类 RSAUtils
可能需要引入 Jar 包:
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.67</version>
</dependency>
编写加解密公共方法类 RSAUtils
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import java.security.*;
import java.security.interfaces.RSAPublicKey;
public class RSAUtils {
private static final KeyPair keyPair = initKey();
private static KeyPair initKey() {
try {
Provider provider = new org.bouncycastle.jce.provider.BouncyCastleProvider();
Security.addProvider(provider);
SecureRandom random = new SecureRandom();
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", provider);
generator.initialize(1024, random);
return generator.generateKeyPair();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static String generateBase64PublicKey() {
PublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
return new String(Base64.encodeBase64(publicKey.getEncoded()));
}
public static String decryptBase64(String string) {
return new String(decrypt(Base64.decodeBase64(string.getBytes())));
}
private static byte[] decrypt(byte[] byteArray) {
try {
Provider provider = new org.bouncycastle.jce.provider.BouncyCastleProvider();
Security.addProvider(provider);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", provider);
PrivateKey privateKey = keyPair.getPrivate();
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] plainText = cipher.doFinal(byteArray);
return plainText;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
后台生成公钥方法
@RequestMapping(value = "getPublicKey")
public @ResponseBody String getPublicKey(HttpServletRequest request){
String publicKey = RSAUtils.generateBase64PublicKey();
return publicKey;
}
前端获取公钥
前端在向后台发起登录请求之前,先请求后台获取公钥的方法
var publicKey;
$.ajax({
url: "getPublicKey",
type: "get",
async: false,
dataType: "text",
success: function(data) {
if(data){
publicKey = data;
};
}
});
前端引入加密依赖
前端引入 jsencrypt.min.js 文件:
<script src="/js/jsencrypt.min.js"></script>
npm 引入:
npm i jsencrypt
npm 使用:
import JsEncrypt from 'jsencrypt'
let encrypt = new JsEncrypt();
encrypt.setPublicKey(publicKey);
username = encrypt.encrypt(username.trim());
- 官网:http://travistidwell.com/jsencrypt/
- GitHub:https://github.com/travist/jsencrypt
- npm:https://www.npmjs.com/package/jsencrypt
前端加密
通过公钥对用户名和密码加密
var encrypt = new JSEncrypt();
encrypt.setPublicKey(publicKey);
var loginaccount = $('#loginaccount').val();
var loginpassword = $('#loginpassword').val();
loginaccount = encrypt.encrypt(loginaccount.trim());
loginpassword = encrypt.encrypt(loginpassword.trim());
加密请求后台
方法一:ajax 请求
通过 ajax 用加密后的用户名密码请求后台
$.ajax({
type: "POST",
url: "xxxxxx",
data: {
"username":username,
"password":password,
},
dataType: "json",
success: function (result) {
if (result.code == 0) {//登录成功
parent.location.href = 'index.html';
} else {
vm.error = true;
vm.errorMsg = result.msg;
vm.refreshCode();
}
}
});
方法二:form 表单
使用 form 表单请求后台
<form method="post" action="login" id="loginForm" onsubmit="return checkLoginTask()">
<input type="hidden" id="account" name="account" />
<input type="hidden" id="password" name="password" />
</form>
<button onclick="$('#loginForm').submit()">登录</button>
其中 checkLoginTask 方法如下:
function checkLoginTask(){
var publicKey;
$.ajax({
url: "getPublicKey",
type: "get",
async: false,
dataType: "text",
success: function(data) {
if(data){
publicKey = data;
};
}
});
if(publicKey == null){
return false;
}
var encrypt = new JSEncrypt();
encrypt.setPublicKey(publicKey);
var loginaccount = $('#loginaccount').val();
var loginpassword = $('#loginpassword').val();
loginaccount = encrypt.encrypt(loginaccount.trim());
loginpassword = encrypt.encrypt(loginpassword.trim());
$('#account').val(loginaccount);
$('#password').val(loginpassword);
return true;
}
方法三:axios 请求
const { data: publicKey } = await this.$http.get("/user/key");
var username = this.loginForm.username;
var password = this.loginForm.password;
let encrypt = new JsEncrypt();
encrypt.setPublicKey(publicKey);
username = encrypt.encrypt(username.trim());
password = encrypt.encrypt(password.trim());
console.log(username)
//发起登入请求
const { data: res } = await this.$http.post(
"user/login?username="+encodeURIComponent(username)+"&password="+encodeURIComponent(password)
);
后端解密
String account = request.getParameter("account");
String password = request.getParameter("password");
if (StringUtils.isNotBlank(account)) {
account = RSAUtils.decryptBase64(account.trim());
}
if (StringUtils.isNotBlank(password)) {
password = RSAUtils.decryptBase64(password.trim());
}
最终效果
从而可以避免出现密码明文传输问题。
测试代码
String key = HttpClient4.doGet("http://127.0.0.1:8181/user/key");
log.info(key);
Map<String, Object> paramMap = new HashMap<>();
try{
paramMap.put("username", RSAUtils.encryptByPublicKey(key,"admin"));
paramMap.put("password", RSAUtils.encryptByPublicKey(key,"123456"));
String result = HttpClient4.doPost("http://127.0.0.1:8181/user/login", paramMap);
log.info(result);
JsonObject jsonObject = new JsonParser().parse(result).getAsJsonObject();
if (jsonObject.get("code").getAsInt()==200){
log.info(jsonObject.get("data").getAsString());
}
} catch (Exception e){
log.error(e.getMessage(),e);
}
其中:
/**
* 公钥加密
* @param publicKeyText 公钥
* @param text 待加密的文本
* @return /
*/
public static String encryptByPublicKey(String publicKeyText, String text) throws Exception {
X509EncodedKeySpec x509EncodedKeySpec2 = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyText));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec2);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] result = cipher.doFinal(text.getBytes());
return Base64.encodeBase64String(result);
}
/**
* 私钥解密
* @param privateKeyText
* @param text
* @return
* @throws Exception
*/
public static String decryptByPrivateKey(String privateKeyText, String text) throws Exception {
PKCS8EncodedKeySpec pkcs8EncodedKeySpec5 = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyText));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec5);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] result = cipher.doFinal(Base64.decodeBase64(text));
return new String(result);
}
相关文章