若依版本:3.8.6
一,后端
1,新建工具类RSAUtils
package com.ruoyi.common.utils;
import org.apache.commons.codec.binary.Base64;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import javax.crypto.Cipher;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
@Component
public class RSAUtils {
// Rsa 私钥 也可固定秘钥对 若依原写法(不安全)
public static String privateKeys = "";
private static String publicKeyStr = "";
private static String privateKeyStr = "";
private static final RSAKeyPair rsaKeyPair = new RSAKeyPair();
/**
* 私钥解密
*
* @param text 待解密的文本
* @return 解密后的文本
*/
public static String decryptByPrivateKey(String text) throws Exception {
return decryptByPrivateKey(rsaKeyPair.getPrivateKey(), text);
}
/**
* 公钥解密
*
* @param publicKeyString 公钥
* @param text 待解密的信息
* @return 解密后的文本
*/
public static String decryptByPublicKey(String publicKeyString, String text) throws Exception {
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyString));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, publicKey);
byte[] result = cipher.doFinal(Base64.decodeBase64(text));
return new String(result);
}
/**
* 私钥加密
*
* @param privateKeyString 私钥
* @param text 待加密的信息
* @return 加密后的文本
*/
public static String encryptByPrivateKey(String privateKeyString, String text) throws Exception {
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyString));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
byte[] result = cipher.doFinal(text.getBytes());
return Base64.encodeBase64String(result);
}
/**
* 私钥解密
*
* @param privateKeyString 私钥
* @param text 待解密的文本
* @return 解密后的文本
*/
public static String decryptByPrivateKey(String privateKeyString, String text) throws Exception {
PKCS8EncodedKeySpec pkcs8EncodedKeySpec5 = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyString));
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);
}
/**
* 公钥加密
*
* @param publicKeyString 公钥
* @param text 待加密的文本
* @return 加密后的文本
*/
public static String encryptByPublicKey(String publicKeyString, String text) throws Exception {
X509EncodedKeySpec x509EncodedKeySpec2 = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyString));
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);
}
/**
* 构建RSA密钥对
*
* @return 生成后的公私钥信息
*/
@Bean
public void generateKeyPair() throws NoSuchAlgorithmException, NoSuchProviderException {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(1024);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
String publicKeyString = Base64.encodeBase64String(rsaPublicKey.getEncoded());
String privateKeyString = Base64.encodeBase64String(rsaPrivateKey.getEncoded());
rsaKeyPair.setPrivateKey(privateKeyString);
rsaKeyPair.setPublicKey(publicKeyString);
publicKeyStr = publicKeyString;
privateKeyStr = privateKeyString;
}
public static String getPublicKey() {
return publicKeyStr;
}
public static String getPrivateKey() {
return privateKeyStr;
}
public static RSAKeyPair rsaKeyPair() {
return rsaKeyPair;
}
/**
* RSA密钥对对象
*/
public static class RSAKeyPair {
private String publicKey;
private String privateKey;
public void setPublicKey(String publicKey) {
this.publicKey = publicKey;
}
public void setPrivateKey(String privateKey) {
this.privateKey = privateKey;
}
public RSAKeyPair() {
}
public RSAKeyPair(String publicKey, String privateKey) {
this.publicKey = publicKey;
this.privateKey = privateKey;
}
public String getPublicKey() {
return publicKey;
}
public String getPrivateKey() {
return privateKey;
}
}
}
2,添加getPublicKey的接口
SysLoginController
/**
* 获取公钥:前端用来密码加密
* @return
*/
@GetMapping("/getPublicKey")
public RSAUtils.RSAKeyPair getPublicKey() {
return RSAUtils.rsaKeyPair();
}
3,添加白名单
SecurityConfig
.antMatchers("/getPublicKey").permitAll()
4,修改登录生成Token
SysLoginService
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, RSAUtils.decryptByPrivateKey(password));
5,修改密码接口
SysProfileController
@Log(title = "个人信息", businessType = BusinessType.UPDATE)
@PutMapping("/updatePwd")
public AjaxResult updatePwd(String oldPassword, String newPassword) throws Exception {
LoginUser loginUser = getLoginUser();
String userName = loginUser.getUsername();
String password = loginUser.getPassword();
//解密
oldPassword = RSAUtils.decryptByPrivateKey(oldPassword);
newPassword = RSAUtils.decryptByPrivateKey(newPassword);
if (!SecurityUtils.matchesPassword(oldPassword, password))
{
return error("修改密码失败,旧密码错误");
}
if (SecurityUtils.matchesPassword(newPassword, password))
{
return error("新密码不能与旧密码相同");
}
if (userService.resetUserPwd(userName, SecurityUtils.encryptPassword(newPassword)) > 0)
{
// 更新缓存用户密码
loginUser.getUser().setPassword(SecurityUtils.encryptPassword(newPassword));
tokenService.setLoginUser(loginUser);
return success();
}
return error("修改密码异常,请联系管理员");
}
6,重置密码接口
SysUserController
@PreAuthorize("@ss.hasPermi('system:user:resetPwd')")
@Log(title = "用户管理", businessType = BusinessType.UPDATE)
@PutMapping("/resetPwd")
public AjaxResult resetPwd(@RequestBody SysUser user) throws Exception {
// 解密
String password = RSAUtils.decryptByPrivateKey(user.getPassword());
userService.checkUserAllowed(user);
userService.checkUserDataScope(user.getUserId());
user.setPassword(SecurityUtils.encryptPassword(password));
user.setUpdateBy(getUsername());
return toAjax(userService.resetPwd(user));
}
7,新建用户接口
SysUserController
/**
* 新增用户
*/
@PreAuthorize("@ss.hasPermi('system:user:add')")
@Log(title = "用户管理", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody SysUser user) throws Exception {
// 解密
user.setPassword(RSAUtils.decryptByPrivateKey(user.getPassword()));
if (!userService.checkUserNameUnique(user))
{
return error("新增用户'" + user.getUserName() + "'失败,登录账号已存在");
}
else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user))
{
return error("新增用户'" + user.getUserName() + "'失败,手机号码已存在");
}
else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user))
{
return error("新增用户'" + user.getUserName() + "'失败,邮箱账号已存在");
}
user.setCreateBy(getUsername());
user.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
return toAjax(userService.insertUser(user));
}
8,注释密码长度校验
SysLoginService
// 密码如果不在指定范围内 错误
// if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
// || password.length() > UserConstants.PASSWORD_MAX_LENGTH)
// {
// AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
// throw new UserPasswordNotMatchException();
// }
二,前端
1,修改加密方法
jsencrypt.js
import JSEncrypt from 'jsencrypt/bin/jsencrypt.min'
// 密钥对生成 http://web.chacuo.net/netrsakeypair
// 这里注掉了原来固定的公私钥
const publicKey = ''
const privateKey = ''
// 加密
export function encrypt(txt, publicKey) {
const encryptor = new JSEncrypt()
encryptor.setPublicKey(publicKey) // 设置公钥
return encryptor.encrypt(txt) // 对数据
}
// 解密(暂无使用)
export function decrypt(txt) {
const encryptor = new JSEncrypt()
encryptor.setPrivateKey(privateKey) // 设置私钥
return encryptor.decrypt(txt) // 对数据进行解密
}
2,添加获取公钥的方法
login.js
// 获取公钥
export function getPublicKey() {
return request({
url: '/getPublicKey',
method: 'get',
})
}
3,修改登录方法
store/modules/user.js
actions: {
getPublicKey() {
return new Promise((resolve, reject) => {
getPublicKey()
.then(res => {
resolve(res)
})
.catch(error => {
reject(error)
})
})
},
// 登录
Login({ commit, dispatch }, userInfo) {
return new Promise((resolve, reject) => {
dispatch('getPublicKey').then(res => {
let publicKey = res.publicKey
const username = userInfo.username.trim()
//调用加密方法(传密码和公钥)
const password = encrypt(userInfo.password, publicKey)
const code = userInfo.code
const uuid = userInfo.uuid
login(username, password, code, uuid)
.then(res => {
setToken(res.token)
commit('SET_TOKEN', res.token)
resolve()
})
.catch(error => {
reject(error)
})
})
})
},
4,修改密码方法
views/sysytem/user/profile/resetPwd.vue
methods: {
getPublicKey() {
return new Promise((resolve, reject) => {
getPublicKey()
.then(res => {
resolve(res)
})
.catch(error => {
reject(error)
})
})
},
submit() {
this.$refs["form"].validate(valid => {
if (valid) {
this.getPublicKey().then(res=>{
let publicKey = res.publicKey
const oldPassword = encrypt(this.user.oldPassword, publicKey)
const newPassword = encrypt(this.user.newPassword, publicKey)
updateUserPwd(oldPassword, newPassword).then(
response => {
this.$modal.msgSuccess("修改成功");
}
);
})
}
});
},
close() {
this.$tab.closePage();
}
}
5,重置密码方法
views/sysytem/user/index.vue
getPublicKey() {
return new Promise((resolve, reject) => {
getPublicKey()
.then(res => {
resolve(res)
})
.catch(error => {
reject(error)
})
})
},
/** 重置密码按钮操作 */
handleResetPwd(row) {
this.$prompt('请输入"' + row.userName + '"的新密码', "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
closeOnClickModal: false,
inputPattern: /^.{5,20}$/,
inputErrorMessage: "用户密码长度必须介于 5 和 20 之间"
}).then(({ value }) => {
this.getPublicKey().then(res=> {
let publicKey = res.publicKey
const encryptValue = encrypt(value,publicKey)
resetUserPwd(row.userId, encryptValue).then(response => {
this.$modal.msgSuccess("修改成功,新密码是:" + value);
});
})
}).catch(() => {});
},
6,新建用户方法
views/sysytem/user/index.vue
/** 提交按钮 */
submitForm: function () {
this.$refs["form"].validate((valid) => {
if (valid) {
if (this.form.userId != undefined) {
this.form.deptId = this.form.deIds[0];
this.form.deptIds = this.form.deIds.join(",");
updateUser(this.form).then((response) => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
} else {
this.getPublicKey().then(res=> {
let publicKey = res.publicKey
this.form.password = encrypt(this.form.password,publicKey)
this.form.deptId = this.form.deIds[0];
this.form.deptIds = this.form.deIds.join(",");
addUser(this.form).then((response) => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
});
})
}
}
});
},
三,添加密码复杂度规则
1,新建用户
新建用户的时候密码为默认密码,只需要把默认密码设置成符合复杂度规则的密码即可。
2,修改密码
views/sysytem/user/profile/resetPwd.vue
passwordRegex :正则校验表达式
callback:校验不通过的提示信息
data() {
const equalToPassword = (rule, value, callback) => {
if (this.user.newPassword !== value) {
callback(new Error("两次输入的密码不一致"));
} else {
callback();
}
};
const validateNewPassword = (rule, value, callback) => {
const passwordRegex = /^(?=.*\d)(?=.*[A-Z])(?=.*[a-z])(?=.*[!@#$%^&*()_+={}[\]|\\:;'",<>.?/-])[A-Za-z\d!@#$%^&*()_+={}[\]|\\:;'",<>.?/-]{6,}$/;
if (!passwordRegex.test(value)) {
callback(new Error("密码应至少6位且包含数字、大小写字母和特殊字符中的四种"));
} else {
callback();
}
}
return {
user: {
oldPassword: undefined,
newPassword: undefined,
confirmPassword: undefined
},
// 表单校验
rules: {
oldPassword: [
{ required: true, message: "旧密码不能为空", trigger: "blur" }
],
newPassword: [
{ validator: validateNewPassword, trigger: "blur" }
],
confirmPassword: [
{ required: true, message: "确认密码不能为空", trigger: "blur" },
{ required: true, validator: equalToPassword, trigger: "blur" }
]
}
};
},
3,重置密码
views/sysytem/user/index.vue
inputPattern:正则校验表达式
inputErrorMessage:校验不通过的提示信息
/** 重置密码按钮操作 */
handleResetPwd(row) {
this.$prompt('请输入"' + row.userName + '"的新密码', "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
closeOnClickModal: false,
inputPattern: /^(?=.*\d)(?=.*[A-Z])(?=.*[a-z])(?=.*[!@#$%^&*()_+={}\[\]|\\:;'",<>.?/-])[A-Za-z\d!@#$%^&*()_+={}\[\]|\\:;'",<>.?/-]{6,}$/,
inputErrorMessage: "密码应至少6位且包含数字、大小写字母和特殊字符中的四种"
}).then(({ value }) => {
this.getPublicKey().then(res=> {
let publicKey = res.publicKey
const encryptValue = encrypt(value,publicKey)
resetUserPwd(row.userId, encryptValue).then(response => {
this.$modal.msgSuccess("修改成功,新密码是:" + value);
});
})
}).catch(() => {});
},
四,修改默认密码
sys_config表中config_key列sys.user.initPassword这一行对应的config_value改成默认密码,记得备注remark列也同步修改。