RuoYi-Vue登录模块改造实录:当Spring Security遇上国密SM4
RuoYi-Vue安全升级实战Spring Security与SM4国密加密的无缝融合在数字化转型加速的今天数据安全已成为企业级应用不可忽视的核心需求。作为国内广泛使用的快速开发框架RuoYi-Vue默认采用Spring Security提供的安全机制但在特定行业场景下尤其是涉及政务、金融等领域时国密算法的合规性要求往往成为技术选型的硬性指标。本文将深入探讨如何在不破坏Spring Security原有安全架构的前提下将SM4国密算法优雅地集成到认证流程中实现从传输到存储的全链路加密合规。1. 国密算法与Spring Security的融合设计国密算法SM系列是由国家密码管理局颁布的一系列商用密码标准其中SM4作为分组对称加密算法在安全性上与AES相当但更符合国内监管要求。与常见的改造误区不同我们并非要替换Spring Security的整个加密体系而是在其认证流程的关键节点插入SM4的解密逻辑。典型集成痛点分析前端加密传输与后端解密验证的密钥一致性密码修改场景下的双向加密处理与原有BCrypt密码存储机制的兼容过滤器链中认证时机的精准把控在RuoYi-Vue的默认实现中密码传输采用明文或简单哈希这显然无法满足高安全场景。我们的改造目标是在以下三个关键环节引入SM4用户登录时的密码传输加密密码修改时的旧密码验证与新密码加密用户注册时的初始密码处理2. 后端核心改造实战2.1 依赖引入与SM4工具类封装首先需要引入BouncyCastle提供的国密算法支持dependency groupIdorg.bouncycastle/groupId artifactIdbcprov-jdk15on/artifactId version1.70/version /dependency随后构建SM4加解密工具类关键点在于处理ECB模式下的PKCS5Paddingpublic class Sm4Util { private static final String ALGORITHM_NAME SM4; private static final String ALGORITHM_NAME_ECB SM4/ECB/PKCS5Padding; public static String decryptEcb(String cipherText, String hexKey) throws Exception { byte[] keyData ByteUtils.fromHexString(hexKey); byte[] cipherData ByteUtils.fromHexString(cipherText); Cipher cipher Cipher.getInstance(ALGORITHM_NAME_ECB, BC); cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(keyData, ALGORITHM_NAME)); return new String(cipher.doFinal(cipherData), StandardCharsets.UTF_8); } }注意实际生产环境中应将hexKey存储在配置中心或使用密钥管理系统避免硬编码2.2 认证流程改造关键点在UsernamePasswordAuthenticationFilter的处理过程中我们需要在密码验证前插入解密步骤public class LoginService { public String login(String username, String encryptedPassword) { // SM4解密 String rawPassword Sm4Util.decryptEcb(encryptedPassword, hexKey); UsernamePasswordAuthenticationToken token new UsernamePasswordAuthenticationToken(username, rawPassword); // 后续Spring Security标准认证流程 authenticationManager.authenticate(token); // ...生成token等逻辑 } }密码修改场景需要特别注意新旧密码的双向处理PutMapping(/updatePwd) public AjaxResult updatePwd(String encryptedOldPwd, String encryptedNewPwd) { String oldPwd Sm4Util.decryptEcb(encryptedOldPwd, hexKey); String newPwd Sm4Util.decryptEcb(encryptedNewPwd, hexKey); if (!passwordEncoder.matches(oldPwd, currentPwd)) { throw new BusinessException(旧密码校验失败); } userService.updatePassword(passwordEncoder.encode(newPwd)); }3. 前端适配方案3.1 加密模块集成前端采用sm-crypto库实现同步加密import { sm4 } from sm-crypto const encryptPassword (password, key) { return sm4.encrypt(password, key) }3.2 登录流程改造关键是在发起请求前完成加密处理async handleLogin() { const publicKey await getPublicKey() this.loginForm.password encryptPassword( this.loginForm.password, publicKey ) login(this.loginForm).then(response { // 处理登录结果 }) }对于修改密码和注册流程需要同样的加密处理async submitChangePwd() { const { oldPassword, newPassword } this.form const key await getPublicKey() const params { oldPassword: encryptPassword(oldPassword, key), newPassword: encryptPassword(newPassword, key) } updatePassword(params).then(() { this.$message.success(修改成功) }) }4. 安全增强与性能优化4.1 密钥管理策略推荐采用动态密钥方案替代固定密钥GetMapping(/sm4/key) public String generateTempKey() { String sessionKey KeyGenerator.getInstance(SM4).generateKey(); redisTemplate.opsForValue().set( sm4:SecurityUtils.getUserId(), sessionKey, 5, TimeUnit.MINUTES ); return sessionKey; }4.2 性能优化方案针对高频认证场景可采用以下优化策略算法加速// 启用BC硬件加速 static { Security.addProvider(new BouncyCastleProvider()); Security.setProperty(crypto.policy, unlimited); }缓存策略Cacheable(value sm4Cache, key #cipherText) public String decryptWithCache(String cipherText) { return Sm4Util.decryptEcb(cipherText, key); }连接池优化# 适当增大Tomcat线程池 server.tomcat.max-threads200 server.tomcat.min-spare-threads204.3 审计与监控增强安全审计功能Aspect Component public class SecurityAuditAspect { AfterReturning(pointcut execution(* *..login(..)), returning result) public void auditLogin(JoinPoint jp, Object result) { String username (String) jp.getArgs()[0]; log.info(登录成功审计 - 用户:{}, username); // 发送审计事件... } }5. 兼容性处理与异常场景5.1 多算法兼容方案为平稳过渡可设计多算法支持策略public PasswordDecoder passwordDecoder() { return new PasswordDecoder() { Override public String decode(String encrypted) { try { // 尝试SM4解密 return Sm4Util.decryptEcb(encrypted, key); } catch (Exception e) { // 降级处理 return encrypted; } } }; }5.2 典型异常处理完善各类异常场景的应对try { return sm4.decrypt(cipherText); } catch (InvalidCipherTextException e) { throw new CryptoException(SM4解密失败密文格式异常); } catch (IllegalStateException e) { throw new CryptoException(SM4初始化失败请检查BouncyCastle配置); }在前端统一处理加密异常async encryptedRequest(apiFunc, params) { try { const key await getPublicKey() const encryptedParams encryptParams(params, key) return await apiFunc(encryptedParams) } catch (error) { if (error.message.includes(SM4)) { showError(加密服务异常请刷新重试) } throw error } }经过完整改造后系统安全架构同时具备传输层SM4国密算法保障存储层BCrypt强哈希保护认证层Spring Security完整机制合规性满足等保2.0相关要求