先看一下改造后的样子,输入的密码不会再以明文展示。
下面我主要把前后端改造的代码贴出来。
1.后端代码
RsaUtils类
在com.ruoyi.common.utils包下新建RsaUtils类,RsaUtils添加了@Component注解 generateKeyPair()构建密钥对添加了@Bean注解 在项目启动时通过@Bean的方式将普通类事例化到spring容器中 所以在系统启动后 每次调用接口会是一样的公私秘钥 每次重启后公私钥不同
import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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();private static final Logger logger = LoggerFactory.getLogger(RsaUtils.class);/*** 私钥解密** @param text 待解密的文本* @return 解密后的文本*/public static String decryptByPrivateKey(String text) throws Exception {
// logger.info(rsaKeyPair.getPrivateKey());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 生成后的公私钥信息*/@Beanpublic 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;}}
}
在SysLoginController添加获取公钥接口
/*** 获取公钥 前端用来密码加密** @return*/@GetMapping("/publicKey")public RsaUtils.RsaKeyPair publicKey() {return RsaUtils.rsaKeyPair();}
修改SysLoginService类中的login方法
/*** 登录验证** @param username 用户名* @param password 密码* @param code 验证码* @param uuid 唯一标识* @return 结果*/public String login(String username, String password, String code, String uuid){// 验证码校验validateCaptcha(username, code, uuid);// 登录前置校验loginPreCheck(username, password);// 用户验证Authentication authentication = null;try{//RsaUtils.decryptByPrivateKey(password)解密密码UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, RsaUtils.decryptByPrivateKey(password));AuthenticationContextHolder.setContext(authenticationToken);// 该方法会去调用UserDetailsServiceImpl.loadUserByUsernameauthentication = authenticationManager.authenticate(authenticationToken);}catch (Exception e){if (e instanceof BadCredentialsException){AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));throw new UserPasswordNotMatchException();}else{AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));throw new ServiceException(e.getMessage());}}finally{AuthenticationContextHolder.clearContext();}AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));LoginUser loginUser = (LoginUser) authentication.getPrincipal();recordLoginInfo(loginUser.getUserId());// 生成tokenreturn tokenService.createToken(loginUser);}
修改重置密码
/*** 重置密码*/@Log(title = "个人信息", businessType = BusinessType.UPDATE)@PutMapping("/updatePwd")public AjaxResult updatePwd(String oldPassword, String newPassword) throws Exception {LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());String userName = loginUser.getUsername();//加密后的String password = loginUser.getPassword();//解密oldPassword = RsaUtils.decryptByPrivateKey(oldPassword);newPassword = RsaUtils.decryptByPrivateKey(newPassword);//拿原密码和加密后的解密if (!SecurityUtils.matchesPassword(oldPassword, password)) {return AjaxResult.error("修改密码失败,旧密码错误");}if (SecurityUtils.matchesPassword(newPassword, password)) {return AjaxResult.error("新密码不能与旧密码相同");}if (userService.resetUserPwd(userName, SecurityUtils.encryptPassword(newPassword)) > 0) {// 更新缓存用户密码loginUser.getUser().setPassword(SecurityUtils.encryptPassword(newPassword));tokenService.setLoginUser(loginUser);return AjaxResult.success();}return AjaxResult.error("修改密码异常,请联系管理员");}
在SecurityConfig中添加获取公钥接口
因为密码校验时会校验长度,加密后的密码会超出框架内原来设定的长度,修改UserConstants类中的PASSWORD_MAX_LENGTH
2.前端代码变动
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) // 对数据进行解密
}
login.js添加获取公钥路由
// 获取key
export function getPublicKey() {return request({url: '/publicKey',method: 'get',})
}
user.js修改登入方法,方便copy我直接全放进来了
import { login, logout, getInfo,getPublicKey } from '@/api/login'
import { getToken, setToken, removeToken } from '@/utils/auth'
import { encrypt, decrypt } from '@/utils/jsencrypt'const user = {state: {token: getToken(),id: '',name: '',avatar: '',roles: [],permissions: []},mutations: {SET_TOKEN: (state, token) => {state.token = token},SET_ID: (state, id) => {state.id = id},SET_NAME: (state, name) => {state.name = name},SET_AVATAR: (state, avatar) => {state.avatar = avatar},SET_ROLES: (state, roles) => {state.roles = roles},SET_PERMISSIONS: (state, permissions) => {state.permissions = permissions}},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.publicKeyconst username = userInfo.username.trim()//调用加密方法(传密码和公钥)const password = encrypt(userInfo.password, publicKey)const code = userInfo.codeconst uuid = userInfo.uuidlogin(username, password, code, uuid).then(res => {setToken(res.token)commit('SET_TOKEN', res.token)resolve()}).catch(error => {reject(error)})})})},// 获取用户信息GetInfo({ commit, state }) {return new Promise((resolve, reject) => {getInfo().then(res => {const user = res.userconst avatar = (user.avatar == "" || user.avatar == null) ? require("@/assets/images/profile.jpg") : process.env.VUE_APP_BASE_API + user.avatar;if (res.roles && res.roles.length > 0) { // 验证返回的roles是否是一个非空数组commit('SET_ROLES', res.roles)commit('SET_PERMISSIONS', res.permissions)} else {commit('SET_ROLES', ['ROLE_DEFAULT'])}commit('SET_ID', user.userId)commit('SET_NAME', user.userName)commit('SET_AVATAR', avatar)resolve(res)}).catch(error => {reject(error)})})},// 退出系统LogOut({ commit, state }) {return new Promise((resolve, reject) => {logout(state.token).then(() => {commit('SET_TOKEN', '')commit('SET_ROLES', [])commit('SET_PERMISSIONS', [])removeToken()resolve()}).catch(error => {reject(error)})})},// 前端 登出FedLogOut({ commit }) {return new Promise(resolve => {commit('SET_TOKEN', '')removeToken()resolve()})}}
}export default user
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.publicKeyconsole.log("res.publicKey",res.publicKey)const oldPassword = encrypt(this.user.oldPassword, publicKey)const newPassword = encrypt(this.user.newPassword, publicKey)updateUserPwd(oldPassword, newPassword).then(response => {this.msgSuccess("修改成功");});})}});},close() {this.$store.dispatch("tagsView/delView", this.$route);this.$router.push({ path: "/index" });}}
这样就完成改造了。