加密方案
加密方案
场景:针对脚本中需要使用密码等数据的时候,比如数据库备份脚本,可以通过使用环境变量的方式进行加密
1. 环境变量的方式
您可以使用配置环境变量的方法,避免在服务中显式地配置密码,从而降低泄漏风险。
环境变量是操作系统中用于存储有关系统环境的信息的变量。您可以通过环境变量来配置密码,这样即使您的代码库被公开,密码也不会泄漏
1.1 Linux系统
当您使用Linux系统(如Ubuntu、CentOS等)中的命令行添加DashScope的API-KEY为环境变量时,可以选择在当前会话添加临时性环境变量,或对当前用户添加永久性环境变量。
添加临时性环境变量
如果您仅想在当前会话中添加并使用临时性环境变量,您可以运行以下命令:
# 用您的 DashScope API-KEY 代替 YOUR_DASHSCOPE_API_KEY export DASHSCOPE_API_KEY="YOUR_DASHSCOPE_API_KEY"您可以在当前会话运行以下命令检查环境变量是否生效:
echo $DASHSCOPE_API_KEY对当前用户添加永久性环境变量
如果您想对当前用户添加永久性环境变量,使得在该用户的新会话中也可以使用该环境变量,您可以把以下命令语句复制并添加到~/.bashrc文件中:
# 用您的 DashScope API-KEY 代替 YOUR_DASHSCOPE_API_KEY export DASHSCOPE_API_KEY="YOUR_DASHSCOPE_API_KEY"或直接运行以下命令将上述命令语句添加到~/.bashrc中:
# 用您的 DashScope API-KEY 代替 YOUR_DASHSCOPE_API_KEY echo "export DASHSCOPE_API_KEY='YOUR_DASHSCOPE_API_KEY'" >> ~/.bashrc添加完成后,您可以运行以下命令使得环境变量生效:
source ~/.bashrc您可以新建立一个会话,运行以下命令检查环境变量是否生效:
echo $DASHSCOPE_API_KEY
1.2 Linux系统
在Windows系统中,您可以使用CMD或PowerShell(推荐)运行命令。
1.2.1 通过CMD命令
当您使用CMD中的命令行添加DashScope的API-KEY为环境变量时,可以选择在当前会话添加临时性环境变量,或对当前用户添加永久性环境变量。
添加临时性环境变量
如果您仅想在当前会话中添加并使用临时性环境变量,您可以运行以下命令:
# 用您的 DashScope API-KEY 代替 YOUR_DASHSCOPE_API_KEY set DASHSCOPE_API_KEY="YOUR_DASHSCOPE_API_KEY"您可以在当前会话运行以下命令检查环境变量是否生效:
echo %DASHSCOPE_API_KEY%对当前用户添加永久性环境变量
当您在CMD中需要为当前用户添加永久性环境变量时,您可以运行以下命令:
# 用您的 DashScope API-KEY 代替 YOUR_DASHSCOPE_API_KEY setx DASHSCOPE_API_KEY "YOUR_DASHSCOPE_API_KEY"您可以新建立一个会话,运行以下命令检查环境变量是否生效:
echo %DASHSCOPE_API_KEY%
1.2.1 通过PowerShell命令
当您使用PowerShell中的命令行添加DashScope的API-KEY为环境变量时,可以选择在当前会话添加临时性环境变量,或对当前用户添加永久性环境变量。
添加临时性环境变量
如果您仅想在当前会话中添加并使用临时性环境变量,您可以运行以下命令:
# 用您的 DashScope API-KEY 代替 YOUR_DASHSCOPE_API_KEY $env:DASHSCOPE_API_KEY = "YOUR_DASHSCOPE_API_KEY"您可以在当前会话运行以下命令检查环境变量是否生效:
echo $env:DASHSCOPE_API_KEY对当前用户添加永久性环境变量
如果您在PowerShell中需要为当前用户添加永久性环境变量,您可以运行以下命令:
# 用您的 DashScope API-KEY 代替 YOUR_DASHSCOPE_API_KEY [Environment]::SetEnvironmentVariable("DASHSCOPE_API_KEY", "YOUR_DASHSCOPE_API_KEY", [EnvironmentVariableTarget]::User)您可以新建立一个会话,运行以下命令检查环境变量是否生效:
echo $env:DASHSCOPE_API_KEY
2. 前端实现
2.1 简易的方式实现
// 替换算法加密函数
function encryptText(text, shift) {
let result = "";
for (let i = 0; i < text.length; i++) {
let charCode = text.charCodeAt(i);
if (charCode >= 65 && charCode <= 90) {
result += String.fromCharCode(((charCode - 65 + shift) % 26) + 65);
} else if (charCode >= 97 && charCode <= 122) {
result += String.fromCharCode(((charCode - 97 + shift) % 26) + 97);
} else {
result += text[i];
}
}
return result;
}
// 替换算法解密函数
function decryptText(text, shift) {
return encryptText(text, 26 - shift);
}
// 加密
function encrypt(data) {
return encryptText(data, 3); // 使用偏移量为3进行加密
}
// 解密
function decrypt(decryptData) {
return decryptText(decryptData, 3); // 使用偏移量为3进行解密
}
// 示例:生成密钥并加密数据
(() => {
try {
const orgData = 'lihuan@1234';
console.log("原始数据:", orgData);
const encrypted = encrypt(orgData);
console.log("加密结果:", encrypted);
const decryptedData = decrypt(encrypted);
console.log("解密后的原始数据:", decryptedData);
} catch (error) {
// 错误处理
console.log(error);
}
})();
使用crypto库
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Title</title>
<!-- import CSS -->
<link rel="stylesheet" href="https://cdn.staticfile.org/element-ui/2.15.9/theme-chalk/index.css" />
</head>
<body>
<div>
<div>{{info}}</div>
<input type="button" value="按钮" @click="onClick()" />
<!-- 其他 Vue 组件和代码 -->
</div>
<!-- import JavaScript -->
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="https://cdn.staticfile.org/element-ui/2.15.9/index.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/crypto-js.js"></script>
<script src="encryption.js"></script>
<script>
let v = new Vue({
el: "body>div",
data: {
info: "事件绑定"
},
mounted() {
// this.getData()
},
methods: {
onClick() {
alert("按钮点击了!");
// 在这里调用加密函数
const orgData = "lihuan@1234";
let key = "lishihuan";
console.log("原始数据:", orgData);
const encrypted = encryptText(orgData, key);
console.log("加密结果:", encrypted);
const decryptedData = decryptText(encrypted, key);
console.log("解密后的原始数据:", decryptedData);
}
}
});
</script>
</body>
</html>
通过 URL 的方式来引入 CryptoJS 库
目前测试无法在其他页面调用
// 异步加载 CryptoJS 库
function loadCryptoJS(url, callback) {
var script = document.createElement("script");
script.type = "text/javascript";
if (script.readyState) {
// For IE
script.onreadystatechange = function () {
if (script.readyState === "loaded" || script.readyState === "complete") {
script.onreadystatechange = null;
callback();
}
};
} else {
// For other browsers
script.onload = function () {
callback();
};
}
script.src = url;
document.getElementsByTagName("head")[0].appendChild(script);
}
// 加载 CryptoJS 库
loadCryptoJS(
"https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/crypto-js.js",
function () {
console.log("CryptoJS library loaded.");
// 加密函数
window.encryptText = function (text, key) {
return CryptoJS.AES.encrypt(text, key).toString();
};
// 解密函数
window.decryptText = function (ciphertext, key) {
let bytes = CryptoJS.AES.decrypt(ciphertext, key);
return bytes.toString(CryptoJS.enc.Utf8);
};
console.log("Functions exposed globally.");
// 待加密数据
const orgData = "lihuan@1234";
// 密钥
let key = "lishihuan";
console.log("原始数据:", orgData);
const encrypted = encryptText(orgData, key);
console.log("加密结果:", encrypted);
const decryptedData = decryptText(encrypted, key);
console.log("解密后的原始数据:", decryptedData);
}
);
后端实现
- 方式一
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.68</version>
</dependency>
package com.example.demo.utils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
import java.util.Base64;
/**
* @author lishihuan
*/
public class EncryptionUtils {
public static void main(String[] args) {
Security.addProvider(new BouncyCastleProvider());
try {
// 生成密钥
String secretKey = "1234567890123456";
// 加密字符串
String originalString = "lishihuan";
String encryptedString = encrypt(originalString, secretKey);
// 解密字符串
String decryptedString = decrypt(encryptedString, secretKey);
System.out.println("原始字符串: " + originalString);
System.out.println("加密后的字符串: " + encryptedString);
System.out.println("解密后的字符串: " + decryptedString);
} catch (Exception e) {
e.printStackTrace();
}
}
public static SecretKey generateAESKey(String key) {
byte[] keyBytes = key.getBytes();
return new SecretKeySpec(keyBytes, "AES");
}
public static String encrypt(String plainText, String key) throws Exception {
SecretKey secretKey = generateAESKey(key);
Cipher cipher = Cipher.getInstance("AES", "BC");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] encryptedBytes = cipher.doFinal(plainText.getBytes());
return Base64.getEncoder().encodeToString(encryptedBytes);
}
public static String decrypt(String encryptedString, String key) throws Exception {
SecretKey secretKey = generateAESKey(key);
Cipher cipher = Cipher.getInstance("AES", "BC");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] encryptedBytes = Base64.getDecoder().decode(encryptedString);
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
return new String(decryptedBytes);
}
}
调用
Security.addProvider(new BouncyCastleProvider()); // 如果不加则出现 java.security.NoSuchProviderException: no such provider: BC
System.out.println(EncryptionUtils.decrypt("c2l5MSAfD+Yrs7vVhWq52TFvLxlToxS75NkSKiTSODurjUgb6mq9d3JuM2vEYM5J", "1234567890123456"));
- 方式二
package com.example.demo.utils;
import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
public class EncryptionUtils {
public static Key generateKey() throws Exception {
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128);
return keyGenerator.generateKey();
}
public static String encrypt(String plainText, Key key) throws Exception {
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encryptedBytes = cipher.doFinal(plainText.getBytes());
return byteArrayToHexString(encryptedBytes);
}
public static String decrypt(String encryptedText, Key key) throws Exception {
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] encryptedBytes = hexStringToByteArray(encryptedText);
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
return new String(decryptedBytes);
}
public static String byteArrayToHexString(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
public static byte[] hexStringToByteArray(String hexString) {
int len = hexString.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4)
+ Character.digit(hexString.charAt(i + 1), 16));
}
return data;
}
public static void main(String[] args) throws Exception {
String originalString = "sk-56767e1cbae3489ca2cc41bfdc2d4287";
// 生成密钥
Key key = generateKey();
System.out.println("生成密钥: " + key);
// 加密
String encryptedString = encrypt(originalString, key);
System.out.println("加密后的字符串: " + encryptedString);
// 解密
String decryptedString = decrypt(encryptedString, key);
System.out.println("解密后的字符串: " + decryptedString);
}
}
Base64 编码【可逆,简单】
如果需求是: “轻量级加密”或“可逆的简单混淆”,
不要求高安全性,只是防止一眼看出内容。那我们可以使用Base64 编码,它在 JavaScript、Java 和 SQL(比如 MySQL)中都可以很好地编码和解码。
- 不是真正的加密,但能有效隐藏原文
- 无法通过简单观察识别内容
- 无复杂运算,适合大数据量处理
- 完整支持 UTF-8 字符(包括中文)
- 支持特殊符号和数字
JavaScript 示例:
btoa 和 atob 只能处理 Latin1(单字节)字符集,也就是普通的英文字符。
- 如果你要处理包含中文或 emoji 的字符串,需要转成 UTF-8 后再编码。
// 编码
const encoded = btoa("HelloWorld");
console.log(encoded); // "SGVsbG9Xb3JsZA=="
// 解码
const decoded = atob(encoded);
console.log(decoded); // "HelloWorld"
//========================================# 针对中文 #========================================
function utf8ToBase64(str) {
return btoa(unescape(encodeURIComponent(str)));
}
function base64ToUtf8(base64) {
return decodeURIComponent(escape(atob(base64)));
}
const original = "你好,世界!";
const encoded = utf8ToBase64(original);
console.log("Encoded:", encoded);
const decoded = base64ToUtf8(encoded);
console.log("Decoded:", decoded);
Java 示例:
import java.util.Base64;
import java.nio.charset.StandardCharsets;
public class Base64Example {
public static void main(String[] args) {
String original = "你好,世界!";
// 编码(指定 UTF-8)
String encoded = Base64.getEncoder().encodeToString(original.getBytes(StandardCharsets.UTF_8));
System.out.println("Encoded: " + encoded);
// 解码(指定 UTF-8)
byte[] decodedBytes = Base64.getDecoder().decode(encoded);
String decoded = new String(decodedBytes, StandardCharsets.UTF_8);
System.out.println("Decoded: " + decoded);
}
}
MySQL 示例:
sql复制-- 编码
SELECT TO_BASE64('HelloWorld'); -- 返回:SGVsbG9Xb3JsZA==
-- 解码
SELECT FROM_BASE64('SGVsbG9Xb3JsZA=='); -- 返回:HelloWorld
🔒 如果你希望稍微“更难看懂一点”:
你可以在 Base64 的基础上再做一点点变形,比如:
- 替换字符(如
=换成~,/换成_等) - 前后加点干扰字符(如加个前缀/后缀再去解)
例如:
const original = "HelloWorld";
const encoded = btoa(original).replace(/=/g, '~').replace(/\//g, '_');
console.log(encoded); // 变形后的Base64
// 解码前替换回去
const cleaned = encoded.replace(/~/g, '=').replace(/_/g, '/');
const decoded = atob(cleaned);
console.log(decoded); // HelloWorld
二次 Base64 + 干扰字符插入
✅ 目标:
- 非明文传输(防止敏感数据被直观看出)
- 支持三端解码(JS、Java、MySQL)
- 加入干扰字符,防止一眼看出可还原
- 保持简单、易实现、轻量无依赖
🧠 加密逻辑说明:
最终加密串 = 插入干扰字符 ( base64( base64(原始字符串) ) )
例如原始字符串:Hello123
步骤1:base64 编码 => SGVsbG8xMjM=
步骤2:再次 base64 => U0dWc2JHODFNak09
步骤3:插入干扰 => 插入 2 个固定或随机字符,比如在第5位插入"X1",=> U0dWX1NiRzAxTWpNPT0=
✅ 前端 JavaScript 实现
function doubleBase64WithNoise(str) {
const b64 = btoa(unescape(encodeURIComponent(str)));
const doubleB64 = btoa(b64);
// 插入干扰(固定插入也可以随机):
return doubleB64.slice(0, 5) + "X1" + doubleB64.slice(5);
}
function decodeDoubleBase64WithNoise(noisedStr) {
// 去除干扰
const cleaned = noisedStr.slice(0, 5) + noisedStr.slice(7);
const once = atob(cleaned);
return decodeURIComponent(escape(atob(once)));
}
const encrypted = doubleBase64WithNoise("你好Hello123");
console.log("加密:", encrypted);
const decrypted = decodeDoubleBase64WithNoise(encrypted);
console.log("解密:", decrypted);
✅ Java 实现
import java.util.Base64;
import java.nio.charset.StandardCharsets;
public class Encoder {
public static String encode(String input) {
String base64_1 = Base64.getEncoder().encodeToString(input.getBytes(StandardCharsets.UTF_8));
String base64_2 = Base64.getEncoder().encodeToString(base64_1.getBytes(StandardCharsets.UTF_8));
return base64_2.substring(0, 5) + "X1" + base64_2.substring(5); // 插入干扰
}
public static String decode(String input) {
String cleaned = input.substring(0, 5) + input.substring(7); // 去除干扰
byte[] decoded1 = Base64.getDecoder().decode(cleaned);
byte[] decoded2 = Base64.getDecoder().decode(new String(decoded1, StandardCharsets.UTF_8));
return new String(decoded2, StandardCharsets.UTF_8);
}
public static void main(String[] args) {
String encoded = encode("你好Hello123");
System.out.println("加密: " + encoded);
String decoded = decode(encoded);
System.out.println("解密: " + decoded);
}
}
✅ MySQL 实现
自定义函数:
-- 加密函数:Base64两次 + 插入干扰字符(第6位插入'X1')
DELIMITER $$
CREATE FUNCTION double_base64_with_noise(input TEXT) RETURNS TEXT
DETERMINISTIC
BEGIN
DECLARE b64_1 TEXT;
DECLARE b64_2 TEXT;
DECLARE result TEXT;
SET b64_1 = TO_BASE64(input);
SET b64_2 = TO_BASE64(b64_1);
SET result = CONCAT(LEFT(b64_2, 5), 'X1', SUBSTRING(b64_2, 6));
RETURN result;
END$$
DELIMITER ;
解密函数:
DELIMITER $$
CREATE FUNCTION decode_double_base64_with_noise(input TEXT) RETURNS TEXT
DETERMINISTIC
BEGIN
DECLARE cleaned TEXT;
DECLARE step1 TEXT;
DECLARE result TEXT;
SET cleaned = CONCAT(LEFT(input, 5), SUBSTRING(input, 8)); -- 去除干扰
SET step1 = FROM_BASE64(cleaned);
SET result = FROM_BASE64(step1);
RETURN result;
END$$
DELIMITER ;
✅ 使用示例(MySQL):
SELECT double_base64_with_noise('你好Hello123'); -- 加密
SELECT decode_double_base64_with_noise('U0dWX1NiRzAxTWpNPT0='); -- 解密
✅ 总结
| 环节 | 方案 | 特点 |
|---|---|---|
| 加密 | Base64 → Base64 → 插入干扰字符 | 无外部依赖、快速 |
| 解密 | 去干扰 → Base64 → Base64 | 完整还原 |
| 安全性 | 防止明文 + 不容易一眼看出 | 非对称安全,不适合高保密场景 |
| 支持 | JS / Java / MySQL | ✅ 三端兼容 |
Base64 编码+加盐
✅ 简单解释「加盐」
“加盐”指在原始内容的基础上添加一段额外的、固定或可控的信息,让加密结果更难猜。
在我们的场景中,可以这样实现:
✳️ 加盐策略示例(统一三端):
| 步骤 | 说明 |
|---|---|
| 1 | 原始字符串进行 Base64 编码 |
| 2 | 在特定位置(如中间或前后)插入一段固定“盐值”字符串 |
| 3 | 作为最终加密结果传输 |
| 4 | 解密时移除盐值,再 Base64 解码 |
✳️ 盐值策略建议:
| 类型 | 示例 | 是否固定 | 推荐用途 |
|---|---|---|---|
| 固定盐值 | "s@lt123" | 是 | 最简单 |
| 动态前缀/后缀 | "timestamp" + base64 | 否 | 对抗重放攻击(复杂) |
| 插入特定位置 | "ABC" + base64.slice(0,5) + "XYZ" + base64.slice(5) | 是 | 易实现,适合你当前需求 |
🧪 JavaScript 示例(加盐版)
const SALT = 's@lt123';
function encryptWithSalt(str) {
const base64 = btoa(unescape(encodeURIComponent(str)));
const salted = base64.slice(0, 4) + SALT + base64.slice(4); // 插入盐
return salted;
}
function decryptWithSalt(saltedStr) {
const base64 = saltedStr.replace(SALT, '');
return decodeURIComponent(escape(atob(base64)));
}
const origin = "你好123abcABC";
const encrypted = encryptWithSalt(origin);
const decrypted = decryptWithSalt(encrypted);
console.log("加密后:", encrypted);
console.log("解密后:", decrypted);
☕ Java 对应实现
public class SaltedBase64 {
private static final String SALT = "s@lt123";
public static String encrypt(String input) {
String base64 = java.util.Base64.getEncoder().encodeToString(input.getBytes(java.nio.charset.StandardCharsets.UTF_8));
return base64.substring(0, 4) + SALT + base64.substring(4);
}
public static String decrypt(String input) {
String unsalted = input.replace(SALT, "");
byte[] decoded = java.util.Base64.getDecoder().decode(unsalted);
return new String(decoded, java.nio.charset.StandardCharsets.UTF_8);
}
}
🛢️ MySQL 对应方案(仅限固定盐值)
-- 加密(Base64 + 加盐插入位置)
SELECT
CONCAT(SUBSTRING(base64, 1, 4), 's@lt123', SUBSTRING(base64, 5)) AS encrypted
FROM
(SELECT TO_BASE64('你好123abcABC') AS base64) t;
-- 解密(去除盐值 + 解码)
SELECT
FROM_BASE64(REPLACE('加密后的值', 's@lt123', '')) AS decrypted;
🔹 复杂一点也可以写成函数或触发器,但这就是最小实现成本的方式。
✅ 加盐 vs 字符替换对比
| 技术 | 易用性 | 安全性提升 | 跨端支持 | 解码复杂度 |
|---|---|---|---|---|
| 替换字符 | 非常简单 | 一点点 | ✅ | 简单 |
| 加盐 | 稍复杂 | 明显提升 | ✅ | 中等(需去盐) |
✅ 总结建议
| 如果你 | 推荐方案 |
|---|---|
| 要求极简 | Base64 + 替换(适合 URL 场景) |
| 想再多混淆一点 | Base64 + 固定加盐(插入中间) ✅推荐 |
| 安全性要求高 | 那就不能再用 Base64,应切换到 AES 等非对称加密 |
Base64+替换字符串+干扰
📌 1. 设计目标
- ✅ 轻量级混淆:基于 Base64 编码,通过字符替换与插入干扰字符实现可逆加密。
- ✅ 跨平台兼容:前端 JavaScript、后端 Java、数据库 MySQL 均可加解密互通。
- ✅ 避免一眼识破:打破标准 Base64 特征,提升可读性混淆效果。
🔁 2. 加解密流程图
💻 3. 平台实现
🌐 JavaScript 实现
const CryptoConfig = {
CHAR_MAP: {
'A': 'q', 'B': 'w', 'C': 'e', 'D': 'r',
'+': '-', '/': '_', '=': '.'
},
SALT: {
length: 2,
position: 4,
generate: () => Math.random().toString(36).slice(2, 4)
}
};
function pseudoEncrypt(str) {
if (!str) return '';
const base64 = btoa(unescape(encodeURIComponent(str)));
const replaced = base64.replace(/[A-Z+\/=]/g, c => CryptoConfig.CHAR_MAP[c] || c);
const salt = CryptoConfig.SALT.generate();
return replaced.slice(0, CryptoConfig.SALT.position) + salt + replaced.slice(CryptoConfig.SALT.position);
}
function pseudoDecrypt(encryptedStr) {
if (!encryptedStr) return '';
const { position, length } = CryptoConfig.SALT;
const cleaned = encryptedStr.slice(0, position) + encryptedStr.slice(position + length);
const reverseMap = Object.fromEntries(Object.entries(CryptoConfig.CHAR_MAP).map(([k, v]) => [v, k]));
const restored = cleaned.replace(/[qwer\-_\.]/g, c => reverseMap[c] || c);
return decodeURIComponent(escape(atob(restored)));
}
☕ Java 实现
import java.util.*;
import java.util.stream.Collectors;
import java.nio.charset.StandardCharsets;
public class PseudoCrypto {
private static final Map<Character, Character> CHAR_MAP = Map.of(
'A', 'q', 'B', 'w', 'C', 'e', 'D', 'r',
'+', '-', '/', '_', '=', '.'
);
private static final Map<Character, Character> REVERSE_MAP = CHAR_MAP.entrySet()
.stream().collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));
private static final int SALT_POSITION = 4;
private static final int SALT_LENGTH = 2;
public static String encrypt(String input) {
if (input == null || input.isEmpty()) return "";
String base64 = Base64.getEncoder().encodeToString(input.getBytes(StandardCharsets.UTF_8));
String replaced = base64.chars()
.mapToObj(c -> String.valueOf(CHAR_MAP.getOrDefault((char) c, (char) c)))
.collect(Collectors.joining());
String salt = generateSalt();
return new StringBuilder(replaced).insert(SALT_POSITION, salt).toString();
}
public static String decrypt(String encrypted) {
if (encrypted == null || encrypted.isEmpty()) return "";
String cleaned = encrypted.substring(0, SALT_POSITION) + encrypted.substring(SALT_POSITION + SALT_LENGTH);
String restored = cleaned.chars()
.mapToObj(c -> String.valueOf(REVERSE_MAP.getOrDefault((char) c, (char) c)))
.collect(Collectors.joining());
return new String(Base64.getDecoder().decode(restored), StandardCharsets.UTF_8);
}
private static String generateSalt() {
Random r = new Random();
return r.ints(2, 0, 36)
.mapToObj(i -> String.valueOf(i < 10 ? (char) ('0' + i) : (char) ('a' + i - 10)))
.collect(Collectors.joining());
}
}
🛢️ MySQL 实现
-- 需要 MySQL 5.6+
DELIMITER $$
CREATE FUNCTION fn_pseudo_encrypt(input TEXT)
RETURNS TEXT
DETERMINISTIC
BEGIN
DECLARE base64 TEXT;
DECLARE replaced TEXT;
DECLARE salt CHAR(2);
IF input IS NULL THEN RETURN NULL; END IF;
SET base64 = TO_BASE64(CAST(input AS BINARY));
SET replaced = base64;
SET replaced = REPLACE(replaced, 'A', 'q');
SET replaced = REPLACE(replaced, 'B', 'w');
SET replaced = REPLACE(replaced, 'C', 'e');
SET replaced = REPLACE(replaced, 'D', 'r');
SET replaced = REPLACE(replaced, '+', '-');
SET replaced = REPLACE(replaced, '/', '_');
SET replaced = REPLACE(replaced, '=', '.');
SET salt = LOWER(HEX(RANDOM_BYTES(1))); -- 2位16进制随机盐
RETURN CONCAT(SUBSTRING(replaced, 1, 4), salt, SUBSTRING(replaced, 5));
END$$
CREATE FUNCTION fn_pseudo_decrypt(encrypted TEXT)
RETURNS TEXT
DETERMINISTIC
BEGIN
DECLARE cleaned TEXT;
DECLARE restored TEXT;
IF encrypted IS NULL THEN RETURN NULL; END IF;
SET cleaned = CONCAT(SUBSTRING(encrypted, 1, 4), SUBSTRING(encrypted, 7));
SET restored = cleaned;
SET restored = REPLACE(restored, 'q', 'A');
SET restored = REPLACE(restored, 'w', 'B');
SET restored = REPLACE(restored, 'e', 'C');
SET restored = REPLACE(restored, 'r', 'D');
SET restored = REPLACE(restored, '-', '+');
SET restored = REPLACE(restored, '_', '/');
SET restored = REPLACE(restored, '.', '=');
RETURN CAST(FROM_BASE64(restored) AS CHAR);
END$$
DELIMITER ;
✅ 4. 跨平台一致性保障表
| 项目 | JavaScript | Java | MySQL |
|---|---|---|---|
| Base64 编码 | btoa (UTF-8) | Base64.getEncoder() | TO_BASE64() |
| 字符替换表 | {A→q, B→w, ...} | Map<Character, Character> | 多次 REPLACE() |
| 干扰字符 | Math.random() 2位 | Random().ints(2,0,36) | HEX(RANDOM_BYTES(1)) |
| 插入位置 | index=4 | index=4 | SUBSTRING(1,4) + salt |
🧪 5. 示例场景
📦 前端 → 后端解密
// JavaScript
const encrypted = pseudoEncrypt("/api/用户信息");
fetch(`/api?path=${encodeURIComponent(encrypted)}`);
// Java 控制器
@GetMapping("/api")
public String getPath(@RequestParam String path) {
String realPath = PseudoCrypto.decrypt(path);
return "你请求的路径是:" + realPath;
}
🗄️ MySQL 存储与查询
-- 加密插入
INSERT INTO user_data (path) VALUES (fn_pseudo_encrypt('/api/private'));
-- 查询解密
SELECT fn_pseudo_decrypt(path) AS real_path FROM user_data;
🔐 6. 安全说明
| 项目 | 描述 |
|---|---|
| 🔓 不是真正加密 | 本方案为可逆混淆算法,不适用于敏感数据保护 |
| 🔑 可视为“变体密钥” | CHAR_MAP 相当于“算法密钥”,可定期变更 |
| ⚡ 轻量无依赖 | 无需外部库,适合 URL、表单字段、数据库字段混淆 |
| 🧠 不建议用于传输密码、Token | 真正敏感信息请使用 AES、RSA 等标准加密 |
📎 附:测试用例示例
const str = "/demo/测试文件123.xlsx";
const enc = pseudoEncrypt(str);
const dec = pseudoDecrypt(enc);
console.log({ str, enc, dec }); // dec 应该与 str 完全一致
SELECT fn_pseudo_encrypt('/demo/测试文件123.xlsx') AS enc,
fn_pseudo_decrypt(fn_pseudo_encrypt('/demo/测试文件123.xlsx')) AS dec;
前端url加密显示
考虑到有些页面需要带参数 路由跳转,防止用户篡改
通过自定义
Vue Router的stringifyQuery和parseQuery方法,实现URL参数的自动加密与解密