整体思路就是后端Springboot根据jdk自带的RSA创建一个公私钥匙存入工具类中,前端页面点击登录按钮后首先向后端get到公钥,然后使用公钥加密密码向后端POST提交数据,后端接收后先用私钥解密,然后判断数据库能否查询出来同样的数据,存在即返回前端code码。(可以根据自己需要更改数据,工具类代码完全可以全部copy)
目录
一,先写后端,创建一个工具类(可以直接copy)
二,生成公私钥
三,定义前端get获取公钥的方法
四,在controller解码进行其他操作
五,前端所需操作
一,先写后端,创建一个工具类(可以直接copy)
package com.example.demo.utils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.tomcat.util.codec.binary.Base64;
import javax.crypto.Cipher;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
public class RSAUtils {
protected static final Log log = LogFactory.getLog(RSAUtils.class);
private static String KEY_RSA_TYPE = "RSA";
private static String KEY_RSA_TYPE_ALL = "RSA/ECB/PKCS1Padding";
private static int KEY_SIZE = 1024;//JDK方式RSA加密最大只有1024位
private static int ENCODE_PART_SIZE = KEY_SIZE/8;
public static final String PUBLIC_KEY_NAME = "public";
public static final String PRIVATE_KEY_NAME = "private";
/**
* 创建公钥秘钥
* @return
*/
public static Map<String,String> createRSAKeys(){
Map<String,String> keyPairMap = new HashMap<>();//里面存放公私秘钥的Base64位加密
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_RSA_TYPE);
keyPairGenerator.initialize(KEY_SIZE,new SecureRandom());
KeyPair keyPair = keyPairGenerator.generateKeyPair();
//获取公钥秘钥
String publicKeyValue = Base64.encodeBase64String(keyPair.getPublic().getEncoded());
String privateKeyValue = Base64.encodeBase64String(keyPair.getPrivate().getEncoded());
//存入公钥秘钥,以便以后获取
keyPairMap.put(PUBLIC_KEY_NAME,publicKeyValue);
keyPairMap.put(PRIVATE_KEY_NAME,privateKeyValue);
} catch (NoSuchAlgorithmException e) {
log.error("当前JDK版本没找到RSA加密算法!");
e.printStackTrace();
}
return keyPairMap;
}
/**
* 公钥加密
* 描述:
* 1字节 = 8位;
* 最大加密长度如 1024位私钥时,最大加密长度为 128-11 = 117字节,不管多长数据,加密出来都是 128 字节长度。
* @param sourceStr
* @param publicKeyBase64Str
* @return
*/
public static String encode(String sourceStr,String publicKeyBase64Str){
byte [] publicBytes = Base64.decodeBase64(publicKeyBase64Str);
//公钥加密
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicBytes);
List<byte[]> alreadyEncodeListData = new LinkedList<>();
int maxEncodeSize = ENCODE_PART_SIZE - 11;
String encodeBase64Result = null;
try {
KeyFactory keyFactory = KeyFactory.getInstance(KEY_RSA_TYPE);
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
Cipher cipher = Cipher.getInstance(KEY_RSA_TYPE_ALL);
cipher.init(Cipher.ENCRYPT_MODE,publicKey);
byte[] sourceBytes = sourceStr.getBytes("utf-8");
int sourceLen = sourceBytes.length;
for(int i=0;i<sourceLen;i+=maxEncodeSize){
int curPosition = sourceLen - i;
int tempLen = curPosition;
if(curPosition > maxEncodeSize){
tempLen = maxEncodeSize;
}
byte[] tempBytes = new byte[tempLen];//待加密分段数据
System.arraycopy(sourceBytes,i,tempBytes,0,tempLen);
byte[] tempAlreadyEncodeData = cipher.doFinal(tempBytes);
alreadyEncodeListData.add(tempAlreadyEncodeData);
}
int partLen = alreadyEncodeListData.size();//加密次数
int allEncodeLen = partLen * ENCODE_PART_SIZE;
byte[] encodeData = new byte[allEncodeLen];//存放所有RSA分段加密数据
for (int i = 0; i < partLen; i++) {
byte[] tempByteList = alreadyEncodeListData.get(i);
System.arraycopy(tempByteList,0,encodeData,i*ENCODE_PART_SIZE,ENCODE_PART_SIZE);
}
encodeBase64Result = Base64.encodeBase64String(encodeData);
} catch (Exception e) {
e.printStackTrace();
}
return encodeBase64Result;
}
/**
* 私钥解密
* @param sourceBase64RSA
* @param privateKeyBase64Str
*/
public static String decode(String sourceBase64RSA,String privateKeyBase64Str){
byte[] privateBytes = Base64.decodeBase64(privateKeyBase64Str);
byte[] encodeSource = Base64.decodeBase64(sourceBase64RSA);
int encodePartLen = encodeSource.length/ENCODE_PART_SIZE;
List<byte[]> decodeListData = new LinkedList<>();//所有解密数据
String decodeStrResult = null;
//私钥解密
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateBytes);
try {
KeyFactory keyFactory = KeyFactory.getInstance(KEY_RSA_TYPE);
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
Cipher cipher = Cipher.getInstance(KEY_RSA_TYPE_ALL);
cipher.init(Cipher.DECRYPT_MODE,privateKey);
int allDecodeByteLen = 0;//初始化所有被解密数据长度
for (int i = 0; i < encodePartLen; i++) {
byte[] tempEncodedData = new byte[ENCODE_PART_SIZE];
System.arraycopy(encodeSource,i*ENCODE_PART_SIZE,tempEncodedData,0,ENCODE_PART_SIZE);
byte[] decodePartData = cipher.doFinal(tempEncodedData);
decodeListData.add(decodePartData);
allDecodeByteLen += decodePartData.length;
}
byte [] decodeResultBytes = new byte[allDecodeByteLen];
for (int i = 0,curPosition = 0; i < encodePartLen; i++) {
byte[] tempSorceBytes = decodeListData.get(i);
int tempSourceBytesLen = tempSorceBytes.length;
System.arraycopy(tempSorceBytes,0,decodeResultBytes,curPosition,tempSourceBytesLen);
curPosition += tempSourceBytesLen;
}
decodeStrResult = new String(decodeResultBytes,"UTF-8");
}catch (Exception e){
e.printStackTrace();
}
return decodeStrResult;
}
}
二,生成公私钥
可以在application启动类中直接获取一下,也可以单独写个地方调用
// 生成共私钥
Map<String, String> rsaKeys = RSAUtils.createRSAKeys();
String publicKey = rsaKeys.get(RSAUtils.PUBLIC_KEY_NAME);
System.out.println("publicKey:"+ publicKey);
String privateKey = rsaKeys.get(RSAUtils.PRIVATE_KEY_NAME);
System.out.println("privateKey:"+ privateKey);
三,定义前端get获取公钥的方法
//引用公钥
@Value("${publicKey}")
private String publicKey;
//引用私钥
@Value("${privateKey}")
private String privateKey;
//前端get获取公钥
@CrossOrigin
@GetMapping("api/getPublicKey")
@ResponseBody
public String getPublicKey(){
System.out.print(this.publicKey);
return this.publicKey;
}
四,在controller解码进行其他操作
(userpw变量的操作) userpw= RSAUtils.decode(userpw,this.privateKey);
@CrossOrigin
@PostMapping(value = "api/login")
//@ResponseBody返回json格式的结果
@ResponseBody
public Result login(@RequestBody User requestUser, HttpSession session) {
// 对 html 标签进行转义,防止 XSS 攻击
String username = requestUser.getUsername();
String userpw=requestUser.getPassword();
username = HtmlUtils.htmlEscape(username);
System.out.print(userpw+"1");
userpw= RSAUtils.decode(userpw,this.privateKey);
System.out.print(userpw+"2");
//赋值给User
User user =userService.get(username,userpw);
//正常判断service返回的是否为空
if (null == user) {
String message = "账号密码错误";
System.out.println(message);
return new Result(400,message);
} else {
//将用户登录的信息存放到session中
session.setAttribute("user",user);
return new Result(200,user.getPassword());
}
}
至此后端所需工作已经准备完毕,前端get请求/getPublicKey时获取公钥然后加密post提交给/login,后端在进行解密后的判断。
五,前端所需操作
需要提前引入jsencrypt
npm install jsencrypt --save
<script>
//引入jsencrypt
import JSEncrypt from 'jsencrypt';
export default {
//登录按钮的单击事件login对应的登录方法、
//methods中可以写方法或函数
methods: {
login() {
var _this = this
console.log(this.$store.state)
//向后端get公钥
this.$axios.get('/getPublicKey').then(res => {
let publicKey = res.data;
//判断是否是否成功取到公钥
if (publicKey) {
// onsole.log(this.publicKey)
//new一个上方引用的jsencrypt
let encrypt = new JSEncrypt()
//将公钥存入内
encrypt.setPublicKey(publicKey)
this.$axios.post('/login', {
// post方式向后端传输数据,其地址应是/api/login
username: this.loginForm.username,
//这个时候就是直接使用公密加密密码提交给后台
password: encrypt.encrypt(this.loginForm.password)
}).then(successResponse => {
if (successResponse.data.code === 200) {
sessionStorage.setItem('name', successResponse.data.o)
// var data = this.loginForm
_this.$store.commit('login', _this.loginForm)
var path = this.$route.query.redirect
this.$router.replace({ path: path === '/' || path === undefined ? '/index' : path })
} else {
alert("账号或密码错误");
}
})
.catch(failResponse => {
})
} else{
this.$message.error('获取公钥失败')
}
})
}
}
}
</script>