diff --git a/ruoyi-admin/pom.xml b/ruoyi-admin/pom.xml index 18cdc4e..7c83f1a 100644 --- a/ruoyi-admin/pom.xml +++ b/ruoyi-admin/pom.xml @@ -79,6 +79,13 @@ 2.1.0 + + + org.bouncycastle + bcprov-jdk15on + 1.59 + + org.springframework.boot spring-boot-starter-test diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java index e5d5d3d..9e53da5 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java @@ -2,6 +2,8 @@ package com.ruoyi.web.controller.system; import java.util.List; import java.util.Set; + +import com.ruoyi.common.utils.sign.Sm4Util; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; @@ -41,11 +43,11 @@ public class SysLoginController * @return 结果 */ @PostMapping("/login") - public AjaxResult login(@RequestBody LoginBody loginBody) - { + public AjaxResult login(@RequestBody LoginBody loginBody) throws Exception { AjaxResult ajax = AjaxResult.success(); // 生成令牌 - String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode()); + String token = loginService.login(loginBody.getUsername(), + Sm4Util.decryptEcb(loginBody.getPassword()), loginBody.getCode()); ajax.put(Constants.TOKEN, token); return ajax; } @@ -82,4 +84,14 @@ public class SysLoginController List menus = menuService.selectMenuTreeByUserId(userId); return AjaxResult.success(menuService.buildMenus(menus)); } + + /** + * 获取公钥 前端用来密码加密 + * + * @return + */ + @GetMapping("/publicKey") + public String publicKey() throws Exception { + return Sm4Util.hexKey; + } } diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java index d5faedd..fa101e6 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java @@ -1,5 +1,6 @@ package com.ruoyi.web.controller.system; +import com.ruoyi.common.utils.sign.Sm4Util; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; @@ -25,13 +26,12 @@ import com.ruoyi.system.service.ISysUserService; /** * 个人信息 业务处理 - * + * * @author ruoyi */ @RestController @RequestMapping("/system/user/profile") -public class SysProfileController extends BaseController -{ +public class SysProfileController extends BaseController { @Autowired private ISysUserService userService; @@ -42,8 +42,7 @@ public class SysProfileController extends BaseController * 个人信息 */ @GetMapping - public AjaxResult profile() - { + public AjaxResult profile() { LoginUser loginUser = getLoginUser(); SysUser user = loginUser.getUser(); AjaxResult ajax = AjaxResult.success(user); @@ -57,24 +56,20 @@ public class SysProfileController extends BaseController */ @Log(title = "个人信息", businessType = BusinessType.UPDATE) @PutMapping - public AjaxResult updateProfile(@RequestBody SysUser user) - { + public AjaxResult updateProfile(@RequestBody SysUser user) { LoginUser loginUser = getLoginUser(); SysUser currentUser = loginUser.getUser(); currentUser.setNickName(user.getNickName()); currentUser.setEmail(user.getEmail()); currentUser.setPhonenumber(user.getPhonenumber()); currentUser.setSex(user.getSex()); - if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(currentUser)) - { + if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(currentUser)) { return error("修改用户'" + loginUser.getUsername() + "'失败,手机号码已存在"); } - if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(currentUser)) - { + if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(currentUser)) { return error("修改用户'" + loginUser.getUsername() + "'失败,邮箱账号已存在"); } - if (userService.updateUserProfile(currentUser) > 0) - { + if (userService.updateUserProfile(currentUser) > 0) { // 更新缓存用户信息 tokenService.setLoginUser(loginUser); return success(); @@ -87,24 +82,22 @@ public class SysProfileController extends BaseController */ @Log(title = "个人信息", businessType = BusinessType.UPDATE) @PutMapping("/updatePwd") - public AjaxResult updatePwd(String oldPassword, String newPassword) - { + public AjaxResult updatePwd(String oldPassword, String newPassword) throws Exception { LoginUser loginUser = getLoginUser(); String userName = loginUser.getUsername(); String password = loginUser.getPassword(); - if (!SecurityUtils.matchesPassword(oldPassword, password)) - { + //解密 + oldPassword = Sm4Util.decryptEcb(oldPassword); + newPassword = Sm4Util.decryptEcb(newPassword); + if (!SecurityUtils.matchesPassword(oldPassword, password)) { return error("修改密码失败,旧密码错误"); } - if (SecurityUtils.matchesPassword(newPassword, password)) - { + if (SecurityUtils.matchesPassword(newPassword, password)) { return error("新密码不能与旧密码相同"); } - newPassword = SecurityUtils.encryptPassword(newPassword); - if (userService.resetUserPwd(userName, newPassword) > 0) - { + if (userService.resetUserPwd(userName, SecurityUtils.encryptPassword(newPassword)) > 0) { // 更新缓存用户密码 - loginUser.getUser().setPassword(newPassword); + loginUser.getUser().setPassword(SecurityUtils.encryptPassword(newPassword)); tokenService.setLoginUser(loginUser); return success(); } @@ -116,14 +109,11 @@ public class SysProfileController extends BaseController */ @Log(title = "用户头像", businessType = BusinessType.UPDATE) @PostMapping("/avatar") - public AjaxResult avatar(@RequestParam("avatarfile") MultipartFile file) throws Exception - { - if (!file.isEmpty()) - { + public AjaxResult avatar(@RequestParam("avatarfile") MultipartFile file) throws Exception { + if (!file.isEmpty()) { LoginUser loginUser = getLoginUser(); String avatar = FileUploadUtils.upload(RuoYiConfig.getAvatarPath(), file, MimeTypeUtils.IMAGE_EXTENSION); - if (userService.updateUserAvatar(loginUser.getUsername(), avatar)) - { + if (userService.updateUserAvatar(loginUser.getUsername(), avatar)) { AjaxResult ajax = AjaxResult.success(); ajax.put("imgUrl", avatar); // 更新缓存用户头像 diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRegisterController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRegisterController.java index fe19249..ad2d571 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRegisterController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRegisterController.java @@ -26,8 +26,7 @@ public class SysRegisterController extends BaseController private ISysConfigService configService; @PostMapping("/register") - public AjaxResult register(@RequestBody RegisterBody user) - { + public AjaxResult register(@RequestBody RegisterBody user) throws Exception { if (!("true".equals(configService.selectConfigByKey("sys.account.registerUser")))) { return error("当前系统没有开启注册功能!"); diff --git a/ruoyi-common/pom.xml b/ruoyi-common/pom.xml index deb04d5..2c4475f 100644 --- a/ruoyi-common/pom.xml +++ b/ruoyi-common/pom.xml @@ -135,6 +135,14 @@ cn.hutool hutool-all + + + + org.bouncycastle + bcprov-jdk15on + 1.59 + + org.projectlombok lombok diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/UserConstants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/UserConstants.java index 8dc7faa..3bbf682 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/constant/UserConstants.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/UserConstants.java @@ -77,5 +77,5 @@ public class UserConstants * 密码长度限制 */ public static final int PASSWORD_MIN_LENGTH = 5; - public static final int PASSWORD_MAX_LENGTH = 20; + public static final int PASSWORD_MAX_LENGTH = 20000; } diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/sign/Sm4Util.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/sign/Sm4Util.java new file mode 100644 index 0000000..924d7f4 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/sign/Sm4Util.java @@ -0,0 +1,176 @@ +package com.ruoyi.common.utils.sign; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.pqc.math.linearalgebra.ByteUtils; + +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.spec.SecretKeySpec; +import java.security.*; +import java.util.Arrays; + +/** + * ClassName: Sm4Util + * Package: com.hyp.common.utils.sign + * sm4加密算法工具类 + * @explain sm4加密、解密与加密结果验证 可逆算法 + * + * @Author wangxy + * @Create 2024/11/28 14:44 + * @Version 1.0 + */ +public class Sm4Util { + + static { + Security.addProvider(new BouncyCastleProvider()); + } + private static final String ENCODING = "UTF-8"; + public static final String ALGORITHM_NAME = "SM4"; + // 加密算法/分组加密模式/分组填充方式 + // PKCS5Padding-以8个字节为一组进行分组加密 + // 定义分组加密模式使用:PKCS5Padding + public static final String ALGORITHM_NAME_ECB_PADDING = "SM4/ECB/PKCS5Padding"; + // 128-32位16进制;256-64位16进制 + public static final int DEFAULT_KEY_SIZE = 128; + + /** + * 当时用ECB模式的时候,和前端key一致 + */ + public static final String hexKey = "6fb7b3dcaa041c798c3798abe6f9575c"; + + /** + * 生成ECB暗号 + * @explain ECB模式(电子密码本模式:Electronic codebook) + * @param algorithmName 算法名称 + * @param mode 模式 + * @param key + * @return + * @throws Exception + */ + private static Cipher generateEcbCipher(String algorithmName, int mode, byte[] key) throws Exception { + Cipher cipher = Cipher.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME); + Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME); + cipher.init(mode, sm4Key); + return cipher; + } + + /** + * 自动生成密钥 + * @explain + * @return + * @throws NoSuchAlgorithmException + * @throws NoSuchProviderException + */ + public static byte[] generateKey() throws Exception { + return generateKey(DEFAULT_KEY_SIZE); + } + + + //加密****************************************** + /** + * @explain 系统产生秘钥 + * @param keySize + * @return + * @throws Exception + */ + public static byte[] generateKey(int keySize) throws Exception { + KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM_NAME, BouncyCastleProvider.PROVIDER_NAME); + kg.init(keySize, new SecureRandom()); + return kg.generateKey().getEncoded(); + } + + /** + * sm4加密 + * @explain 加密模式:ECB 密文长度不固定,会随着被加密字符串长度的变化而变化 + * @param + * @param paramStr 待加密字符串 + * @return 返回16进制的加密字符串 + * @throws Exception + */ + public static String encryptEcb(String paramStr) throws Exception { + String cipherText = ""; + // 16进制字符串-->byte[] + byte[] keyData = ByteUtils.fromHexString(hexKey); + // String-->byte[] + byte[] srcData = paramStr.getBytes(ENCODING); + // 加密后的数组 + byte[] cipherArray = encrypt_Ecb_Padding(keyData, srcData); + // byte[]-->hexString + cipherText = ByteUtils.toHexString(cipherArray); + return cipherText; + } + + /** + * 加密模式之Ecb + * @param key + * @param data + * @return + * @throws Exception + */ + public static byte[] encrypt_Ecb_Padding(byte[] key, byte[] data) throws Exception { + Cipher cipher = generateEcbCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.ENCRYPT_MODE, key);//声称Ecb暗号,通过第二个参数判断加密还是解密 + return cipher.doFinal(data); + } + + //解密**************************************** + /** + * sm4解密 + * @explain 解密模式:采用ECB + * @param + * @param cipherText 16进制的加密字符串(忽略大小写) + * @return 解密后的字符串 + * @throws Exception + */ + public static String decryptEcb(String cipherText) throws Exception { + // 用于接收解密后的字符串 + String decryptStr = ""; + // hexString-->byte[] + byte[] keyData = ByteUtils.fromHexString(hexKey); + // hexString-->byte[] + byte[] cipherData = ByteUtils.fromHexString(cipherText); + // 解密 + byte[] srcData = decrypt_Ecb_Padding(keyData, cipherData); + // byte[]-->String + decryptStr = new String(srcData, ENCODING); + return decryptStr; + } + + /** + * 解密 + * @explain + * @param key + * @param cipherText + * @return + * @throws Exception + */ + public static byte[] decrypt_Ecb_Padding(byte[] key, byte[] cipherText) throws Exception { + Cipher cipher = generateEcbCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.DECRYPT_MODE, key);//生成Ecb暗号,通过第二个参数判断加密还是解密 + return cipher.doFinal(cipherText); + } + + /** + * 校验加密前后的字符串是否为同一数据 + * @explain + * @param + * @param cipherText 16进制加密后的字符串 + * @param paramStr 加密前的字符串 + * @return 是否为同一数据 + * @throws Exception + */ + public static boolean verifyEcb(String cipherText, String paramStr) throws Exception { + // 用于接收校验结果 + boolean flag = false; + // hexString-->byte[] + byte[] keyData = ByteUtils.fromHexString(hexKey); + // 将16进制字符串转换成数组 + byte[] cipherData = ByteUtils.fromHexString(cipherText); + // 解密 + byte[] decryptData = decrypt_Ecb_Padding(keyData, cipherData); + // 将原字符串转换成byte[] + byte[] srcData = paramStr.getBytes(ENCODING); + // 判断2个数组是否一致 + flag = Arrays.equals(decryptData, srcData); + return flag; + } + +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java index ae6a525..e69382f 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java @@ -111,7 +111,7 @@ public class SecurityConfig .authorizeHttpRequests((requests) -> { permitAllUrl.getUrls().forEach(url -> requests.antMatchers(url).permitAll()); // 对于登录login 注册register 验证码captchaImage 允许匿名访问 - requests.antMatchers("/login", "/register", "/captcha/get", "/captcha/check","/captchaImage").permitAll() + requests.antMatchers("/login", "/register", "/captcha/get", "/captcha/check","/publicKey","/captchaImage").permitAll() // 静态资源,可匿名访问 .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll() .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll() diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysRegisterService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysRegisterService.java index 61f9f96..3e4da91 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysRegisterService.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysRegisterService.java @@ -1,5 +1,6 @@ package com.ruoyi.framework.web.service; +import com.ruoyi.common.utils.sign.Sm4Util; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import com.ruoyi.common.constant.CacheConstants; @@ -38,9 +39,8 @@ public class SysRegisterService /** * 注册 */ - public String register(RegisterBody registerBody) - { - String msg = "", username = registerBody.getUsername(), password = registerBody.getPassword(); + public String register(RegisterBody registerBody) throws Exception { + String msg = "", username = registerBody.getUsername(), password = Sm4Util.decryptEcb(registerBody.getPassword()); SysUser sysUser = new SysUser(); sysUser.setUserName(username); diff --git a/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml index 6771113..e42ce14 100644 --- a/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml +++ b/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml @@ -80,7 +80,7 @@ left join sys_role_menu rm on m.menu_id = rm.menu_id left join sys_user_role ur on rm.role_id = ur.role_id left join sys_role ro on ur.role_id = ro.role_id - left join sys_user u on ur.user_id = u.user_id + left join ${prefix}sys_user u on ur.user_id = u.user_id where u.user_id = #{userId} and m.menu_type in ('M', 'C') and m.status = 0 AND ro.status = 0 order by m.parent_id, m.order_num diff --git a/ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml index 44ad508..5711b6e 100644 --- a/ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml +++ b/ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml @@ -50,7 +50,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" select p.post_id from sys_post p left join sys_user_post up on up.post_id = p.post_id - left join sys_user u on u.user_id = up.user_id + left join ${prefix}sys_user u on u.user_id = up.user_id where u.user_id = #{userId} @@ -58,7 +58,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" select p.post_id, p.post_name, p.post_code from sys_post p left join sys_user_post up on up.post_id = p.post_id - left join sys_user u on u.user_id = up.user_id + left join ${prefix}sys_user u on u.user_id = up.user_id where u.user_name = #{userName} diff --git a/ruoyi-ui/package.json b/ruoyi-ui/package.json index 1fed732..735b520 100644 --- a/ruoyi-ui/package.json +++ b/ruoyi-ui/package.json @@ -54,6 +54,7 @@ "nprogress": "0.2.0", "quill": "1.3.7", "screenfull": "5.0.2", + "sm-crypto": "^0.3.13", "sortablejs": "1.10.2", "vue": "2.6.12", "vue-count-to": "1.0.13", diff --git a/ruoyi-ui/src/api/login.js b/ruoyi-ui/src/api/login.js index ee97960..2578f6f 100644 --- a/ruoyi-ui/src/api/login.js +++ b/ruoyi-ui/src/api/login.js @@ -1,12 +1,11 @@ import request from '@/utils/request' // 登录方法 -export function login(username, password, code, uuid) { +export function login(username, password, code) { const data = { username, password, - code, - uuid + code } return request({ url: '/login', @@ -46,3 +45,11 @@ export function logout() { method: 'post' }) } + +// 获取key +export function getPublicKey() { + return request({ + url: '/publicKey', + method: 'get', + }) +} diff --git a/ruoyi-ui/src/api/sm4.js b/ruoyi-ui/src/api/sm4.js new file mode 100644 index 0000000..452b414 --- /dev/null +++ b/ruoyi-ui/src/api/sm4.js @@ -0,0 +1,15 @@ +const sm4 =require('sm-crypto').sm4 + +//const key='7f5cd501e5548914edaed6824d3ff79d'//可以为16进制串或字节数组,要求为128比特 +/** + * 加密 + * @param txt + * @returns {*} + */ +export function encrypt(txt,key) { + return sm4.encrypt(txt,key); +} + +export function decrypt(txt,key) { + return sm4.decrypt(txt,key); +} diff --git a/ruoyi-ui/src/store/modules/user.js b/ruoyi-ui/src/store/modules/user.js index cdbab1e..3c36cdd 100644 --- a/ruoyi-ui/src/store/modules/user.js +++ b/ruoyi-ui/src/store/modules/user.js @@ -1,5 +1,6 @@ -import { login, logout, getInfo } from '@/api/login' +import { login, logout, getInfo, getPublicKey } from '@/api/login' import { getToken, setToken, removeToken } from '@/utils/auth' +import {encrypt} from "@/api/sm4"; const user = { state: { @@ -33,19 +34,35 @@ const user = { }, actions: { + getPublicKey() { + return new Promise((resolve, reject) => { + getPublicKey() + .then(res => { + resolve(res) + }) + .catch(error => { + reject(error) + }) + }) + }, // 登录 - Login({ commit }, userInfo) { - const username = userInfo.username.trim() - const password = userInfo.password - const code = userInfo.code - const uuid = userInfo.uuid + Login({ commit, dispatch }, userInfo) { return new Promise((resolve, reject) => { - login(username, password, code, uuid).then(res => { - setToken(res.token) - commit('SET_TOKEN', res.token) - resolve() - }).catch(error => { - reject(error) + dispatch('getPublicKey').then(res => { + let publicKey = res + const username = userInfo.username.trim() + //调用加密方法(传密码和公钥) + const password = encrypt(userInfo.password,publicKey) + const code = userInfo.code + login(username, password, code) + .then(res => { + setToken(res.token) + commit('SET_TOKEN', res.token) + resolve() + }) + .catch(error => { + reject(error) + }) }) }) }, diff --git a/ruoyi-ui/src/views/register.vue b/ruoyi-ui/src/views/register.vue index 2f6a1e7..65a7d56 100644 --- a/ruoyi-ui/src/views/register.vue +++ b/ruoyi-ui/src/views/register.vue @@ -67,8 +67,8 @@