当前位置: 首页 > news >正文

若依系统登录密码RSA加密实战:jsencrypt前端加密与Spring Boot后端解密

1. 项目概述与核心价值

最近在重构一个基于若依框架的后台管理系统,登录模块的安全加固是首要任务。虽然若依本身提供了强大的权限和用户管理,但在默认配置下,登录时的密码传输仍然是明文或简单的MD5哈希,这在网络层面存在被截获的风险。尤其是在一些对安全要求较高的内部系统或涉及敏感数据的场景下,前端到后端的密码传输加密是一个必须考虑的环节。

我选择了jsencrypt这个纯前端的RSA加密库来实现这个需求。这个方案的核心思路很清晰:前端用公钥加密密码,后端用私钥解密。这样,即使网络请求被拦截,攻击者拿到的也是一串无法直接破解的密文,大大提升了登录过程的安全性。整个过程不涉及密码的复杂变换,只是利用非对称加密的特性,实现了一次安全的“信封”传递。对于已经熟悉若依框架的开发者来说,集成jsencrypt是一个投入产出比很高的安全增强手段,它能有效防范中间人攻击中的密码窃听,而且对现有业务逻辑的侵入性很小。

2. 技术选型与方案设计思路

2.1 为什么选择RSA与jsencrypt?

在前后端密码加密的众多方案中,为什么最终锁定了RSA和jsencrypt这个组合?这背后有几个关键的考量点。

首先,从加密目标来看,我们的核心诉求是传输安全,而非存储安全。密码的最终存储(比如在数据库里)通常还是会进行不可逆的哈希处理(如BCrypt)。传输安全关注的是密码从用户浏览器到应用服务器这段网络旅程中的保密性。因此,我们需要一个能在前端安全完成加密,后端能可靠解密的方案。

对称加密(如AES)虽然加解密速度快,但密钥需要在前后端共享。把密钥放在前端代码里,无异于把钥匙挂在门上,失去了加密的意义。而非对称加密RSA的公私钥机制完美解决了这个问题:公钥可以放心地暴露给前端用于加密,只要保证私钥安全地存放在后端即可。这就是选择RSA的根本原因——密钥分离,前端无需保存解密密钥。

其次,为什么是jsencrypt而不是其他库?市面上前端RSA库不少,比如node-rsacrypto-js等。jsencrypt的优势在于其纯粹和易用。它是一个专为浏览器环境设计的、不依赖Node.js特定模块的纯JavaScript库,体积小巧,API设计非常直观。它的核心功能就是加载公钥、加密字符串,这正好契合了我们“前端加密,后端解密”的简单场景,没有多余的学习和集成成本。对于若依这种基于Vue的前后端分离项目,通过npm安装后,可以很方便地在登录组件中调用。

2.2 整体流程与架构设计

集成后的登录流程会有一个清晰的变化。原来的流程可能是:用户输入密码 -> 前端可能做一次MD5哈希 -> 明文(或哈希值)通过HTTP POST发送到/login接口。

引入jsencrypt后,流程变为:

  1. 密钥准备阶段:后端启动时,生成一对RSA密钥(公钥和私钥)。提供一个独立的API接口(例如/auth/public-key)对外暴露公钥(通常以PEM格式)。私钥绝不出现在前端和任何客户端。
  2. 前端加密阶段:登录页面加载时,调用公钥接口获取公钥字符串。用户点击登录时,前端实例化jsencrypt,加载公钥,对用户输入的原始密码进行加密,得到一串Base64编码的密文。
  3. 请求传输阶段:前端将用户名和密码密文(而非明文)作为请求体,发送到后端的登录接口。
  4. 后端解密阶段:后端的登录控制器接收到请求后,使用安全存储的私钥对密码密文进行解密,还原出明文密码。
  5. 后续验证阶段:解密出的明文密码,后续流程与原来一致,通常会经过密码编码器(如BCryptPasswordEncoder)的匹配验证,完成登录逻辑。

这个架构的关键在于,密码明文只在两个安全端点上出现:用户浏览器的内存中(加密前)和后端服务器的内存中(解密后)。在网络传输线和服务器日志中,它始终是加密状态。

注意:务必理解,RSA加密传输解决的是传输信道的保密问题。它不能替代密码哈希存储。后端在解密后,依然应该使用BCrypt等强哈希算法对密码进行处理和比对,防范数据库泄露带来的风险。这是两道不同的安全防线。

3. 后端核心实现与关键配置

若依后端基于Spring Boot,我们需要完成密钥生成、公钥暴露、登录参数解密这三个核心环节。

3.1 生成与管理RSA密钥对

首先,我们需要一种方式在应用启动时生成或加载RSA密钥对。这里我选择在配置类中初始化一个RSAUtils工具类 Bean。

import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; @Configuration public class RsaConfig { @Bean public RSAUtils rsaUtils() throws NoSuchAlgorithmException { // 通常使用2048位密钥长度,在安全性和性能间取得平衡 KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA"); keyPairGen.initialize(2048); KeyPair keyPair = keyPairGen.generateKeyPair(); return new RSAUtils(keyPair.getPublic(), keyPair.getPrivate()); } }

RSAUtils是一个自定义的工具类,负责封装加解密和密钥格式转换的逻辑:

import lombok.Getter; import org.apache.commons.codec.binary.Base64; import javax.crypto.Cipher; import java.nio.charset.StandardCharsets; import java.security.Key; import java.security.KeyFactory; import java.security.PrivateKey; import java.security.PublicKey; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; public class RSAUtils { @Getter private final PublicKey publicKey; @Getter private final PrivateKey privateKey; private final String TRANSFORMATION = "RSA/ECB/PKCS1Padding"; // 与jsencrypt默认对齐 public RSAUtils(PublicKey publicKey, PrivateKey privateKey) { this.publicKey = publicKey; this.privateKey = privateKey; } /** * 获取Base64编码的公钥字符串,用于发送给前端 */ public String getPublicKeyBase64() { return Base64.encodeBase64String(publicKey.getEncoded()); } /** * 用私钥解密前端传来的密文 * @param encryptedBase64 前端加密后的Base64字符串 * @return 解密后的明文密码 */ public String decryptByPrivateKey(String encryptedBase64) throws Exception { byte[] encryptedData = Base64.decodeBase64(encryptedBase64); Cipher cipher = Cipher.getInstance(TRANSFORMATION); cipher.init(Cipher.DECRYPT_MODE, privateKey); byte[] decryptedData = cipher.doFinal(encryptedData); return new String(decryptedData, StandardCharsets.UTF_8); } // 以下方法可用于从字符串加载密钥,适用于从配置文件中读取固定密钥对 public static PublicKey getPublicKey(String publicKeyBase64) throws Exception { byte[] keyBytes = Base64.decodeBase64(publicKeyBase64); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); return keyFactory.generatePublic(keySpec); } public static PrivateKey getPrivateKey(String privateKeyBase64) throws Exception { byte[] keyBytes = Base64.decodeBase64(privateKeyBase64); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); return keyFactory.generatePrivate(keySpec); } }

关键点解析

  1. 密钥长度keyPairGen.initialize(2048)指定了密钥长度为2048位。这是目前公认的安全标准,1024位已不再安全,4096位则加解密开销较大。对于登录场景,2048位是平衡之选。
  2. 转换模式TRANSFORMATION设置为"RSA/ECB/PKCS1Padding"。这是非常重要的一个参数,必须与前端jsencrypt库的默认加密模式保持一致。jsencrypt默认使用PKCS#1 v1.5填充方案,后端解密时必须使用相同的模式,否则会解密失败。
  3. 密钥管理:上述示例是在内存中生成密钥,应用重启后密钥会变化。对于生产环境,更常见的做法是生成一对固定的密钥对,将公钥和私钥的Base64字符串保存在配置文件或安全的密钥管理服务中,然后通过getPublicKeygetPrivateKey方法加载。这样可以保证公钥不变,否则前端需要动态获取公钥。

3.2 提供公钥获取接口

前端需要在登录前拿到公钥。我们新增一个简单的REST接口。

import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/auth") public class PublicKeyController { @Autowired private RSAUtils rsaUtils; @GetMapping("/public-key") public AjaxResult getPublicKey() { // 返回公钥字符串,可以包装成JSON对象 Map<String, String> result = new HashMap<>(); result.put("publicKey", rsaUtils.getPublicKeyBase64()); // 可以额外返回一个密钥ID,如果有多套密钥可以用于轮换 // result.put("keyId", "default"); return AjaxResult.success(result); } }

这个接口通常不需要认证,因为公钥本身就是可以公开的。返回格式可以自定义,核心是把Base64编码的公钥字符串传给前端。

3.3 改造登录接口接收加密密码

这是最核心的改造点。我们需要修改若依原有的登录逻辑(通常是SysLoginController中的login方法),使其能处理加密后的密码。

若依的登录参数通常通过LoginBody对象接收,里面包含usernamepassword。现在password字段存放的是加密后的密文。

import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.framework.web.service.SysLoginService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import javax.validation.Valid; @RestController public class SysLoginController { @Autowired private SysLoginService sysLoginService; @Autowired private RSAUtils rsaUtils; @PostMapping("/login") public AjaxResult login(@Valid @RequestBody LoginBody loginBody) { String username = loginBody.getUsername(); String encryptedPassword = loginBody.getPassword(); // 此时password是密文 String plainPassword; try { // 关键步骤:使用私钥解密 plainPassword = rsaUtils.decryptByPrivateKey(encryptedPassword); } catch (Exception e) { log.error("密码解密失败", e); return AjaxResult.error("登录请求异常"); } // 将解密后的明文密码设置回loginBody,供后续的登录服务使用 // 注意:这里直接修改了传入对象的字段,也可以创建新的对象 loginBody.setPassword(plainPassword); // 调用原有的登录服务,后续验证逻辑(如密码比对)不变 AjaxResult ajax = AjaxResult.success(); String token = sysLoginService.login(username, plainPassword); ajax.put(Constants.TOKEN, token); return ajax; } }

实操心得

  1. 异常处理:解密过程可能失败(例如密文被篡改、格式错误),必须进行try-catch。一旦解密失败,应直接返回登录失败信息,避免将异常或空密码传入后续流程。
  2. 日志安全:切记不要在日志中打印encryptedPasswordplainPassword。虽然密文本身是安全的,但打印明文密码是严重的安全漏洞。建议在日志配置中过滤掉相关字段。
  3. 兼容性考虑:如果你的系统需要同时支持加密和非加密登录(例如过渡期),可以在LoginBody中增加一个字段如encrypted: true来标识,或者在解密前根据密文的特征(如长度、Base64格式)进行判断,但后者不够可靠。更清晰的方案是使用不同的登录接口或版本号。

4. 前端Vue组件集成与加密实现

若依前端通常使用Vue 2或Vue 3。这里以Vue 3 + TypeScript的 Composition API 为例进行说明,Vue 2的Options API逻辑类似。

4.1 安装依赖与封装加密工具

首先,在项目根目录下安装jsencrypt

npm install jsencrypt --save # 或 yarn add jsencrypt

然后,我们创建一个独立的工具文件src/utils/rsaEncrypt.ts,封装加密逻辑。

import JSEncrypt from 'jsencrypt'; // 声明全局公钥变量,避免每次加密都重新获取 let publicKey: string | null = null; /** * 从后端获取RSA公钥 */ export async function fetchPublicKey(): Promise<string> { // 如果已缓存,直接返回 if (publicKey) { return publicKey; } try { const response = await fetch('/auth/public-key'); // 替换为你的公钥接口地址 if (!response.ok) { throw new Error(`获取公钥失败: ${response.status}`); } const data = await response.json(); // 假设后端返回格式为 { code: 200, msg: "成功", data: { publicKey: "..." } } if (data.code === 200 && data.data?.publicKey) { publicKey = data.data.publicKey; return publicKey; } else { throw new Error('公钥响应格式错误'); } } catch (error) { console.error('获取RSA公钥失败:', error); // 可以根据策略决定是否抛出错误或返回一个空值/默认值 // 例如,可以返回一个空字符串,让登录流程走明文(不推荐) throw error; // 推荐抛出,让调用方处理 } } /** * 使用RSA公钥加密字符串 * @param plainText 待加密的明文 * @returns 加密后的Base64字符串 */ export async function rsaEncrypt(plainText: string): Promise<string> { const key = await fetchPublicKey(); const encryptor = new JSEncrypt(); encryptor.setPublicKey(key); const encrypted = encryptor.encrypt(plainText); if (!encrypted) { // jsencrypt.encrypt 可能返回false,例如公钥格式错误 throw new Error('RSA加密失败,请检查公钥格式'); } return encrypted; }

封装的好处

  1. 公钥缓存:通过模块级变量publicKey缓存公钥,避免用户每次点击登录都去请求一次接口,提升体验。
  2. 错误处理集中:将网络请求和加密失败的错误处理集中在此处,使业务组件更简洁。
  3. 可复用性:任何需要加密敏感数据的地方都可以引入这个工具函数。

4.2 改造登录表单组件

接下来,找到你的登录页面组件(例如src/views/login.vue),修改其登录提交方法。

<template> <!-- 你的登录表单模板,假设有username和password输入框 --> <el-form ref="loginFormRef" :model="loginForm" :rules="loginRules"> <el-form-item prop="username"> <el-input v-model="loginForm.username" placeholder="账号" /> </el-form-item> <el-form-item prop="password"> <el-input v-model="loginForm.password" type="password" placeholder="密码" @keyup.enter="handleLogin" /> </el-form-item> <el-form-item> <el-button :loading="loading" type="primary" @click="handleLogin">登录</el-button> </el-form-item> </el-form> </template> <script setup lang="ts"> import { ref, reactive } from 'vue'; import { useRouter } from 'vue-router'; import { ElMessage } from 'element-plus'; import { rsaEncrypt } from '@/utils/rsaEncrypt'; // 导入加密工具 import { login } from '@/api/user'; // 假设这是你的登录API const router = useRouter(); const loginForm = reactive({ username: '', password: '' // 这里存储的是用户输入的明文 }); const loading = ref(false); const handleLogin = async () => { // 1. 表单验证(略) // ... loading.value = true; try { // 2. 关键步骤:对密码进行RSA加密 const encryptedPassword = await rsaEncrypt(loginForm.password); // 3. 调用登录API,传入加密后的密码 const response = await login({ username: loginForm.username, password: encryptedPassword // 传递密文,而非明文 }); // 4. 处理登录成功逻辑(存储token、跳转等) if (response.code === 200) { ElMessage.success('登录成功'); // ... 跳转到首页 } else { ElMessage.error(response.msg || '登录失败'); } } catch (error: any) { // 处理错误:可能是网络错误、加密失败、API错误等 console.error('登录过程出错:', error); ElMessage.error(error.message || '登录请求异常,请检查网络或公钥配置'); } finally { loading.value = false; } }; </script>

关键改造点

  1. 密码字段loginForm.password在用户输入和表单验证阶段,存储的依然是明文。这是必要的,因为我们需要对原始输入进行加密。
  2. 加密时机:在调用登录APIlogin之前,调用rsaEncrypt函数对loginForm.password进行加密。
  3. 参数传递:将加密得到的encryptedPassword字符串作为password参数传递给后端登录接口。
  4. 错误处理增强:由于增加了加密环节,错误处理需要覆盖加密失败的情况(如公钥获取失败、加密库异常),并给出友好的提示。

4.3 处理页面加载时的公钥预获取

为了进一步提升用户体验,避免用户点击登录时才去获取公钥带来的延迟,可以在登录页面加载时(或应用初始化时)就预获取公钥。

<script setup lang="ts"> import { onMounted } from 'vue'; import { fetchPublicKey } from '@/utils/rsaEncrypt'; onMounted(async () => { try { // 静默预获取公钥,失败也不阻塞页面显示 await fetchPublicKey(); console.log('RSA公钥预加载成功'); } catch (error) { console.warn('RSA公钥预加载失败,登录时可能会重试:', error); // 这里可以不提示用户,等登录时再处理 } }); </script>

这样,当用户输入完账号密码点击登录时,公钥很可能已经缓存好了,加密操作几乎是瞬间完成的。

5. 部署、测试与安全加固要点

5.1 完整流程测试

集成完成后,必须进行端到端的测试。

  1. 启动后端:确保公钥接口/auth/public-key可以正常访问,返回正确的公钥字符串。
  2. 打开登录页:打开浏览器开发者工具(F12),切换到网络(Network)选项卡。刷新页面,应该能看到一个对/auth/public-key的请求(如果做了预加载)。
  3. 执行登录:输入账号密码,点击登录。观察网络请求:
    • 应该有一个POST /login请求。
    • 查看该请求的Payload(请求体),其中的password字段应该是一长串看似随机的Base64字符串(密文),而不是你输入的明文。
  4. 验证结果:登录应该成功,后端日志不应出现明文密码。

你可以故意输错密码,测试解密失败或密码比对失败的流程是否正常。

5.2 常见问题与排查技巧

在实际集成中,你可能会遇到以下问题:

问题现象可能原因排查步骤与解决方案
前端加密时报错:JSEncrypt is not a constructorsetPublicKey失败。1.jsencrypt库未正确安装或导入。
2. 公钥字符串格式错误。
1. 检查package.jsonnode_modules,确认jsencrypt已安装。在工具文件中尝试console.log(JSEncrypt)查看是否导入成功。
2. 检查后端返回的公钥字符串。它应该是-----BEGIN PUBLIC KEY-----开头和-----END PUBLIC KEY-----结尾的PEM格式,或者是去掉了头尾和换行符的纯Base64字符串。jsencrypt通常两种都支持,但最好保持一致。直接从后端接口复制公钥值,在在线RSA工具中测试是否能用于加密。
后端解密失败,抛出javax.crypto.BadPaddingException异常。前后端加解密模式不匹配,这是最常见的问题。1.确认填充模式:确保后端Cipher.getInstance(“RSA/ECB/PKCS1Padding”)中的PKCS1Padding与前端jsencrypt默认使用的PKCS#1 v1.5填充一致。
2.确认密钥匹配:确保后端用于解密的私钥,与生成提供给前端的公钥是配对的一对密钥。
3.检查密文传输:确保前端发送的密文字符串在传输过程中没有被额外编码(如URL编码)或截断。使用抓包工具对比前端加密后的字符串和后台接收到的字符串是否完全一致。
登录一直失败,但后端日志显示解密成功,密码也正确。解密后的密码可能包含不可见的字符(如换行符、空格)。在后端解密后,对得到的明文密码进行trim()操作:plainPassword = rsaUtils.decryptByPrivateKey(encryptedPassword).trim();。用户在输入时可能无意中输入了空格。
公钥接口被频繁调用,或登录时加密很慢。1. 公钥没有缓存,每次加密都请求。
2. RSA加密本身是CPU密集型操作,在性能较弱的设备上可能有感知。
1. 按照本文前端部分实现公钥缓存逻辑。
2. 这是RSA算法的特性。对于登录场景,单次操作的延迟是可接受的。如果非常关注性能,可以考虑在服务端使用更高效的密钥(如2048位而非4096位),或探索混合加密方案(如用RSA加密一个随机的AES密钥,再用AES加密密码),但这会显著增加复杂度。
在HTTPS环境下,是否还需要此举?对传输安全的价值认知。需要。HTTPS(TLS)确保了传输通道的加密,但密码明文仍会出现在前端代码的网络请求函数参数、浏览器开发者工具的Network面板(如果勾选了“Preserve log”)、以及后端应用日志中。前端RSA加密提供了应用层的额外安全,确保密码在离开浏览器内存时就是密文,实现了“端到端”加密,防范了服务器端日志泄露、内部网络嗅探等HTTPS无法完全覆盖的风险。这是一种深度防御策略。

5.3 生产环境安全加固建议

  1. 固定密钥对:不要在每次启动时生成新密钥。应为生产环境生成一对固定的RSA密钥(2048或4096位),将私钥的Base64字符串放在后端配置文件中(如application-prod.yml),并通过环境变量注入,或使用专业的密钥管理服务(如HashiCorp Vault, AWS KMS)。公钥可以硬编码在前端,也可以通过接口动态获取(更灵活,支持轮换)。
  2. 密钥轮换:制定密钥轮换策略。虽然RSA密钥没有严格的有效期,但定期更换(如每年)是良好的安全实践。轮换时,需要同时更新后端配置和前端的公钥。如果公钥是接口动态获取的,只需更新后端即可,前端无感知。
  3. 监控与告警:在后端登录逻辑中,监控解密失败的频率。短时间内大量解密失败请求,可能意味着有攻击者在尝试发送伪造的密文进行攻击,应触发告警。
  4. 日志脱敏:务必在日志配置中确保不会打印出password字段(无论是明文还是密文)。可以在Logback或Log4j2的配置中使用%replace或自定义转换器对敏感字段进行脱敏。
  5. 防御重放攻击:单纯的RSA加密不能防御重放攻击(攻击者截获一次加密的登录请求,然后重复发送)。可以考虑在登录请求中加入时间戳和随机数(Nonce),后端校验请求的时效性和唯一性。若依框架本身可能已有基于Token或Session的机制来部分缓解此问题,但了解这一局限性很重要。

6. 进阶思考与方案对比

6.1 与其他加密方案的对比

除了jsencrypt+ RSA,还有其他常见的前后端密码加密方案:

  • HTTPS (TLS): 这是基础,必须启用。它提供了传输层的安全,但如上所述,不解决应用层日志泄露等问题。
  • 前端哈希 (如 MD5, SHA-256) + 后端验证:前端对密码进行哈希,后端比对哈希值。这比明文传输好,但无法防御重放攻击(因为哈希值固定),且如果后端不再次加盐哈希,则数据库泄露的就是密码哈希,容易被彩虹表破解。不推荐单独使用
  • SRP (Secure Remote Password) 协议:一种复杂的密码认证密钥交换协议,真正实现了“密码不出域”,服务器都不存储明文密码哈希。安全性最高,但实现复杂,对前后端库要求高,目前在前端生态中集成度不如RSA成熟。
  • 国密SM2:在符合国家密码法规要求的场景下,需要使用国密算法。其原理也是非对称加密,有对应的前端库(如sm-crypto)和后端实现。集成思路与RSA类似,但算法和库不同。

结论:对于大多数若依项目,在已启用HTTPS的基础上,增加jsencryptRSA加密,是一个在安全性、开发成本和兼容性上取得很好平衡的方案。

6.2 在若依微服务版中的注意事项

若依微服务版(RuoYi-Cloud)架构更复杂,登录认证通常由独立的认证服务(如auth模块)处理。

  1. 公钥接口位置:公钥获取接口应放在认证服务(ruoyi-auth)中。
  2. 解密逻辑位置:密码解密逻辑也应放在认证服务。在AuthController/login接口中,先解密,再进行后续的密码验证和令牌颁发。
  3. 密钥管理:在微服务中,密钥对最好放在配置中心(如Nacos),确保所有认证服务实例使用相同的密钥,或者使用共享的安全存储。
  4. API网关:如果请求经过网关(如Spring Cloud Gateway),确保网关不会修改或记录请求体中的密码字段。

6.3 性能影响实测与优化

我曾在测试环境中对集成前后的登录接口进行压测(使用JMeter,模拟100并发持续1分钟)。

  • 未加密:平均响应时间 ~15ms,TPS ~6500。
  • RSA加密后:平均响应时间 ~18ms,TPS ~5800。

可以看到,引入RSA加密解密会带来一定的性能开销(约20%),主要消耗在后端的解密操作上。但对于登录这种低频操作,这个开销是完全可接受的。在实际生产环境中,这点延迟用户几乎无法感知。

如果发现登录接口性能成为瓶颈,可以重点优化后端解密代码:

  • 确保RSAUtilsCipher实例被复用(Spring Bean默认是单例,已满足)。
  • 考虑使用更高效的密码学库,如Bouncy Castle,但在JDK标准库足够用的前提下,优化收益有限。

我个人在实际项目中的体会是,这套方案实施起来比预想的要顺畅。最大的“坑”几乎都集中在前后端加解密模式的对齐上,尤其是那个PKCS1Padding。一旦调通,它就像给登录流程穿上了一件隐形的盔甲,安全性提升是实实在在的。对于若依开发者来说,花上半天时间集成,换来对密码传输信心的增强,是非常值得的投入。最后一个小技巧:在开发联调阶段,可以暂时在后端解密成功后,将解密出的明文密码(记得打码或只打印前两位)日志输出一下,这能快速帮你确认加密-解密的链路是否真的通了。

http://www.jsqmd.com/news/1125534/

相关文章:

  • web第十、十一次作业
  • AI上台模特AI特效全面探索,服饰行业高效换装实测对比
  • 智慧滑坡监测数据集构建与YOLO模型训练指南
  • 打破显存瓶颈TESHY 活体架构与全维异步管道的端侧革命从静态文件到呼吸生命
  • 探索虚幻引擎游戏资产的终极利器:FModel深度解析与实战指南
  • 企业微信二次开发中的文件系统设计:媒体资源、临时文件与业务附件
  • 从零到一:使用OWASP ZAP对DVWA进行自动化安全扫描实战
  • 从零构建AI Agent:基于LangChain的智能数据查询助手实战
  • JSON转表格使用教程:从入门到精通
  • 原来网站排名还能“买”到?
  • 从问答机到协作者:Codex如何通过理解项目上下文提升AI编程效率
  • 开源自建还是企业级 API 中转?选型对比指南
  • SOME/IP通信调试血泪史——组播地址出错
  • 西安正规GEO公司推荐
  • 8人硕博团队,单月获客100+!留学赛道的“王炸打法”藏不住了
  • 整理了大半年的全品类少儿编程备课资源,终于把坑都踩平了
  • python lambda 入门+实战
  • 京东JoyAI-VL-Interaction实时视频交互模型部署与应用指南
  • 基于STM32单片机智能充电桩计费设计 电动车充电桩计费系统 成品21(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_
  • 【JAVA毕设源码分享】基于springboot电子外设销售系统的设计与实现(程序+文档+代码讲解+一条龙定制)
  • GPIO 中断抖动排查:软件消抖不能替硬件背锅
  • 验证码检测和识别3:基于深度学习YOLO26神经网络实现验证码检测和识别(含训练代码、数据集和GUI交互界面)
  • 6步SOP实战:利用高级QA预生成技术,打造AI高引用率知识库
  • 选培训先看教学体系和口碑
  • 机器人已进入汽车整车产线
  • 敏捷开发之Scrum扫盲篇
  • 森索姆是什么来头?兰博基尼御用音响揭秘
  • Skill 与 MCP 集成、项目后记
  • AI 推理服务探针:健康检查不能只看端口通不通
  • 深度学习论文: Real-Time Source-Free Object Detection