前言:项目使用base64对用户账号和密码进行编码,然后通过网络输出到后台,由于base64是可逆的,所以攻击者很容易就知道了这些敏感信息。所以,要对这些敏感信息先加密输出到后台,然后后台进行解密,这就要前台和后台约定好加密和解密的密钥,刚开始想法是使用AES对称加密,但是由于要将密钥在网络上进行传输,这种就造成攻击者很容易截取信息进行伪造,所以改用了RSA非对称加密对账号和密码对这些敏感信息加密,前台先从后台获取公钥,后台生成对应的私钥并存储,然后使用公钥对敏感信息进行加密传输到后台,后台接收之后使用之前存储对应的私钥进行解密,之后再按照原先的逻辑处理。
1.前台引入加密相关js文件
<script src="${ctx}/static/js/jsencrypt.min.js"></script>
文件下载地址: https://pan.baidu.com/s/1Q5Y3YUgeptju8efK6mfkxw 提取码: trp3
2.项目引入加密相关的依赖
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.9</version>
</dependency>
3.后台创建一个RSA工具类RSAUtils
import org.apache.commons.codec.binary.Base64;
import org.apache.log4j.Logger;
import javax.crypto.Cipher;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
/**
* <p>rsa工具类</p>
*
* @author xxx
* @create 2021/3/3 21:45
*/
public class RSAUtils {
private static final Logger logger = Logger.getLogger(RSAUtils.class);
public static void main(String[] args) throws Exception {
String s= "123456";
Map<Integer,String> map = RSAUtils.genKeyPair();
String s2 = RSAUtils.encrypt(s, map.get(0));
System.out.println("公钥加密后:"+s2);
System.out.println("私钥解密后:"+RSAUtils.decrypt(s2, map.get(1)));
}
/**
* 随机生成密钥对
*/
public static Map<Integer, String> genKeyPair() {
Map<Integer, String> keyMap = new HashMap<Integer, String>(); //用于封装随机产生的公钥与私钥
try {
// KeyPairGenerator类用于生成公钥和私钥对,基于RSA算法生成对象
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
// 初始化密钥对生成器,密钥大小为96-1024位
keyPairGen.initialize(1024,new SecureRandom());
// 生成一个密钥对,保存在keyPair中
KeyPair keyPair = keyPairGen.generateKeyPair();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); // 得到私钥
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); // 得到公钥
// 得到公钥字符串
String publicKeyString = new String(Base64.encodeBase64(publicKey.getEncoded()));
// 得到私钥字符串
String privateKeyString = new String(Base64.encodeBase64((privateKey.getEncoded())));
// 将公钥和私钥保存到Map
keyMap.put(0,publicKeyString); //0表示公钥
keyMap.put(1,privateKeyString); //1表示私钥
} catch (Exception e) {
logger.info("生成公钥私钥异常:"+e.getMessage());
return null;
}
return keyMap;
}
/**
* RSA公钥加密
* @param str 需要加密的字符串
* @param publicKey 公钥
* @return 公钥加密后的内容
*/
public static String encrypt( String str, String publicKey ){
String outStr=null;
try {
//base64编码的公钥
byte[] decoded = Base64.decodeBase64(publicKey);
RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));
//RSA加密
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
outStr = Base64.encodeBase64String(cipher.doFinal(str.getBytes("UTF-8")));
} catch (Exception e) {
logger.info("使用公钥加密【"+str+"】异常:"+e.getMessage());
}
return outStr;
}
/**
* RSA私钥解密
* @param str 加密字符串
* @param privateKey 私钥
* @return 私钥解密后的内容
*/
public static String decrypt(String str, String privateKey){
String outStr=null;
try {
//64位解码加密后的字符串
byte[] inputByte = Base64.decodeBase64(str.getBytes("UTF-8"));
//base64编码的私钥
byte[] decoded = Base64.decodeBase64(privateKey);
RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded));
//RSA解密
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, priKey);
outStr = new String(cipher.doFinal(inputByte));
} catch (Exception e) {
logger.info("使用私钥解密异常:"+e.getMessage());
}
return outStr;
}
}
4.编写统一返回公共实体类ResponseEntity
public class ResponseEntity extends Throwable {
private static final long serialVersionUID = 2134346833392248650L;
/** 状态码,正常为0 其它为错误(默认为0) */
private Integer code = 0;
/** 错误信息 */
private Object msg = "";
/** 返回数据对象 */
private Object data = "";
/** jsessionId */
private String sid = "";
/** toke*/
private String toke = "";
public ResponseEntity() {
super();
}
public ResponseEntity(Integer code) {
super();
this.code = code;
}
public ResponseEntity(Integer code,String toke) {
super();
this.code = code;
this.toke = toke;
}
public ResponseEntity(Object data) {
super();
this.data = data;
}
public ResponseEntity(String toke) {
super();
this.toke = toke;
}
public ResponseEntity(String toke,Object data) {
super();
this.toke = toke;
this.data = data;
}
public ResponseEntity(Integer code, Object msg) {
super();
this.code = code;
this.msg = msg;
}
public ResponseEntity(Integer code, Object msg, Object data) {
super();
this.code = code;
this.msg = msg;
this.data = data;
}
public Integer getCode() {
return code;
}
public ResponseEntity setCode(Integer code) {
this.code = code;
return this;
}
public ResponseEntity setData(Object data) {
this.data = data;
return this;
}
public Object getData() {
return data;
}
public Object getMsg() {
return msg;
}
public ResponseEntity setMsg(Object msg) {
this.msg = msg;
return this;
}
public String getSid() {
return sid;
}
public ResponseEntity setSid(String sid) {
this.sid = sid;
return this;
}
public String getToke() {
return toke;
}
public ResponseEntity setToke(String toke) {
this.toke = toke;
return this;
}
}
5.编写后台接口用于获取公钥并存储对应私钥
//存储私钥的对象
public static Map<String, String> privateMap = new HashMap<>();
// 获取非对称加密的公钥,并存储对应的私钥
@ResponseBody
@RequestMapping(value = "/getPublicKey", method = RequestMethod.GET)
public ResponseEntity getPublicKey() {
Map<Integer, String> map = RSAUtils.genKeyPair();
privateMap.put("private", map.get(1));
return new ResponseEntity(map.get(0));
}
5.前台获取公钥,并使用公钥对账号和密码进行加密
<form method="" action="${ctx}/goLogin" id="loginForm">
<div class="position">
<input type="search" id="username" placeholder="请输入用户名">
</div>
<div class="position">
<input type="password" id="password" placeholder="请输入密码">
</div>
<input type="submit" value="登录"/>
<input type="hidden" id="digestu" name="digestu">
<input type="hidden" id="digest" name="digest">
</form>
jQuery(document).ready(function(){
// 先请求后台接口获取公钥
var _pubkey;
$.get("${ctx}/getPublicKey",{},function (data) {
_pubkey = data.toke;
}
);
// 使用非对称加密的方式,对用户名和密码进行公钥加密,私钥解密
$("#loginForm").submit(function(){
var encrypt = new JSEncrypt();
encrypt.setPublicKey(_pubkey);
var username = encrypt.encrypt($('#username').val());
var password = encrypt.encrypt($('#password').val());
$('#digestu').val(username);
$('#digest').val(password);
$('#username,#password').val('');
return true;
});
});
6.后台获取到加密之后账号和密码字段,使用之前存储对应的私钥进行解密
@RequestMapping(value = "/goLogin", method = RequestMethod.POST)
public String login(HttpServletRequest request, String digestu, String digest) {
// 后台用私钥解密用户名和密码
String privateKey = privateMap.get("private");
digestu = RSAUtils.decrypt(digestu, privateKey);
digest = RSAUtils.decrypt(digest, privateKey);
return "";
}