fix:修复SM2非对称加密的各种问题

This commit is contained in:
3030332422
2025-09-25 15:50:32 +08:00
parent 7ac5113fef
commit 9b6acf1ffc
9 changed files with 128 additions and 233 deletions
@@ -82,7 +82,6 @@ public class ShiroConfig {
filterMap.put("/user/captcha", "anon"); filterMap.put("/user/captcha", "anon");
filterMap.put("/user/smsVerification", "anon"); filterMap.put("/user/smsVerification", "anon");
filterMap.put("/user/login", "anon"); filterMap.put("/user/login", "anon");
filterMap.put("/user/sm2-public-key", "anon");
filterMap.put("/user/pub-config", "anon"); filterMap.put("/user/pub-config", "anon");
filterMap.put("/user/register", "anon"); filterMap.put("/user/register", "anon");
filterMap.put("/user/retrieve-password", "anon"); filterMap.put("/user/retrieve-password", "anon");
@@ -87,47 +87,36 @@ public class LoginController {
@PostMapping("/login") @PostMapping("/login")
@Operation(summary = "登录") @Operation(summary = "登录")
public Result<TokenDTO> login(@RequestBody LoginDTO login) { public Result<TokenDTO> login(@RequestBody LoginDTO login) {
// 验证是否正确输入验证码
boolean validate = captchaService.validate(login.getCaptchaId(), login.getCaptcha(), true);
if (!validate) {
throw new RenException(ErrorCode.SMS_CAPTCHA_ERROR);
}
String username = login.getUsername();
String password = login.getPassword(); String password = login.getPassword();
// 如果用户名是SM2加密格式,则进行解密 // 获取SM2私钥
if (isSM2Encrypted(username)) { String privateKeyStr = sysParamsService.getValue(Constant.SM2_PRIVATE_KEY, true);
try { if (StringUtils.isBlank(privateKeyStr)) {
// 获取SM2私钥 throw new RenException(ErrorCode.SM2_KEY_NOT_CONFIGURED);
String privateKeyStr = sysParamsService.getValue(Constant.SM2_PRIVATE_KEY, true);
if (StringUtils.isBlank(privateKeyStr)) {
throw new RenException(ErrorCode.SM2_KEY_NOT_CONFIGURED);
}
// 使用SM2私钥解密用户名(十六进制格式)
String decryptedUsername = SM2Utils.decrypt(privateKeyStr, username);
login.setUsername(decryptedUsername);
} catch (Exception e) {
throw new RenException(ErrorCode.SM2_DECRYPT_ERROR);
}
} }
// 如果密码是SM2加密格式,则进行解密 // 使用SM2私钥解密密码
if (isSM2Encrypted(password)) { String decryptedContent;
try { try {
// 获取SM2私钥 decryptedContent = SM2Utils.decrypt(privateKeyStr, password);
String privateKeyStr = sysParamsService.getValue(Constant.SM2_PRIVATE_KEY, true); } catch (Exception e) {
if (StringUtils.isBlank(privateKeyStr)) { throw new RenException(ErrorCode.SM2_DECRYPT_ERROR);
throw new RenException(ErrorCode.SM2_KEY_NOT_CONFIGURED); }
}
// 分离验证码和密码:前5位是验证码,后面是密码
// 使用SM2私钥解密密码 if (decryptedContent.length() > 5) {
String decryptedPassword = SM2Utils.decrypt(privateKeyStr, password); String embeddedCaptcha = decryptedContent.substring(0, 5);
login.setPassword(decryptedPassword); String actualPassword = decryptedContent.substring(5);
} catch (Exception e) {
throw new RenException(ErrorCode.SM2_DECRYPT_ERROR); // 验证嵌入的验证码是否正确
boolean embeddedCaptchaValid = captchaService.validate(login.getCaptchaId(), embeddedCaptcha, true);
if (!embeddedCaptchaValid) {
throw new RenException(ErrorCode.SMS_CAPTCHA_ERROR);
} }
login.setPassword(actualPassword);
} else {
throw new RenException(ErrorCode.SM2_DECRYPT_ERROR);
} }
// 按照用户名获取用户 // 按照用户名获取用户
@@ -143,21 +132,7 @@ public class LoginController {
return sysUserTokenService.createToken(userDTO.getId()); return sysUserTokenService.createToken(userDTO.getId());
} }
/**
* 判断字符串是否为SM2加密格式
* @param str 待判断的字符串
* @return 是否为SM2加密格式
*/
private boolean isSM2Encrypted(String str) {
if (StringUtils.isBlank(str)) {
return false;
}
// 十六进制格式检测(长度较长且只包含0-9,a-f,A-F字符)
if (str.length() > 100 && str.matches("^[0-9a-fA-F]+$")) {
return true;
}
return false;
}
@PostMapping("/register") @PostMapping("/register")
@Operation(summary = "注册") @Operation(summary = "注册")
@@ -166,64 +141,54 @@ public class LoginController {
throw new RenException(ErrorCode.USER_REGISTER_DISABLED); throw new RenException(ErrorCode.USER_REGISTER_DISABLED);
} }
String username = login.getUsername();
String password = login.getPassword(); String password = login.getPassword();
// 如果用户名是SM2加密格式,则进行解 // SM2加
if (isSM2Encrypted(username)) { String privateKeyStr;
try { try {
// 获取SM2私钥 // 获取SM2私钥
String privateKeyStr = sysParamsService.getValue(Constant.SM2_PRIVATE_KEY, true); privateKeyStr = sysParamsService.getValue(Constant.SM2_PRIVATE_KEY, true);
if (StringUtils.isBlank(privateKeyStr)) { if (StringUtils.isBlank(privateKeyStr)) {
throw new RenException(ErrorCode.SM2_KEY_NOT_CONFIGURED); throw new RenException(ErrorCode.SM2_KEY_NOT_CONFIGURED);
}
// 使用SM2私钥解密用户名(十六进制格式)
String decryptedUsername = SM2Utils.decrypt(privateKeyStr, username);
login.setUsername(decryptedUsername);
} catch (Exception e) {
throw new RenException(ErrorCode.SM2_DECRYPT_ERROR);
} }
} catch (Exception e) {
throw new RenException(ErrorCode.SM2_KEY_NOT_CONFIGURED);
} }
// 如果密码是SM2加密格式,则进行解密 String decryptedContent;
if (isSM2Encrypted(password)) { try {
try { // 使用SM2私钥解密密码
// 获取SM2私钥 decryptedContent = SM2Utils.decrypt(privateKeyStr, password);
String privateKeyStr = sysParamsService.getValue(Constant.SM2_PRIVATE_KEY, true); } catch (Exception e) {
if (StringUtils.isBlank(privateKeyStr)) { throw new RenException(ErrorCode.SM2_DECRYPT_ERROR);
throw new RenException(ErrorCode.SM2_KEY_NOT_CONFIGURED); }
}
// 分离验证码和密码:前5位是验证码,后面是密码
// 使用SM2私钥解密密码 if (decryptedContent.length() > 5) {
String decryptedPassword = SM2Utils.decrypt(privateKeyStr, password); String embeddedCaptcha = decryptedContent.substring(0, 5);
login.setPassword(decryptedPassword); String actualPassword = decryptedContent.substring(5);
} catch (Exception e) {
throw new RenException(ErrorCode.SM2_DECRYPT_ERROR); // 验证嵌入的验证码是否正确
boolean embeddedCaptchaValid = captchaService.validate(login.getCaptchaId(), embeddedCaptcha, true);
if (!embeddedCaptchaValid) {
throw new RenException(ErrorCode.SMS_CAPTCHA_ERROR);
} }
login.setPassword(actualPassword);
} else {
throw new RenException(ErrorCode.SM2_DECRYPT_ERROR);
} }
// 是否开启手机注册 // 是否开启手机注册
Boolean isMobileRegister = sysParamsService Boolean isMobileRegister = sysParamsService
.getValueObject(Constant.SysMSMParam.SERVER_ENABLE_MOBILE_REGISTER.getValue(), Boolean.class); .getValueObject(Constant.SysMSMParam.SERVER_ENABLE_MOBILE_REGISTER.getValue(), Boolean.class);
boolean validate;
if (isMobileRegister) { if (isMobileRegister) {
// 验证用户是否是手机号码 // 验证用户是否是手机号码
boolean validPhone = ValidatorUtils.isValidPhone(login.getUsername()); boolean validPhone = ValidatorUtils.isValidPhone(login.getUsername());
if (!validPhone) { if (!validPhone) {
throw new RenException(ErrorCode.USERNAME_NOT_PHONE); throw new RenException(ErrorCode.USERNAME_NOT_PHONE);
} }
// 验证短信验证码是否正常
validate = captchaService.validateSMSValidateCode(login.getUsername(), login.getMobileCaptcha(), false);
if (!validate) {
throw new RenException(ErrorCode.SMS_CODE_ERROR);
}
} else {
// 验证是否正确输入验证码
validate = captchaService.validate(login.getCaptchaId(), login.getCaptcha(), true);
if (!validate) {
throw new RenException(ErrorCode.SMS_CAPTCHA_ERROR);
}
} }
// 按照用户名获取用户 // 按照用户名获取用户
@@ -304,18 +269,14 @@ public class LoginController {
config.put("beianIcpNum", sysParamsService.getValue(Constant.SysBaseParam.BEIAN_ICP_NUM.getValue(), true)); config.put("beianIcpNum", sysParamsService.getValue(Constant.SysBaseParam.BEIAN_ICP_NUM.getValue(), true));
config.put("beianGaNum", sysParamsService.getValue(Constant.SysBaseParam.BEIAN_GA_NUM.getValue(), true)); config.put("beianGaNum", sysParamsService.getValue(Constant.SysBaseParam.BEIAN_GA_NUM.getValue(), true));
config.put("name", sysParamsService.getValue(Constant.SysBaseParam.SERVER_NAME.getValue(), true)); config.put("name", sysParamsService.getValue(Constant.SysBaseParam.SERVER_NAME.getValue(), true));
return new Result<Map<String, Object>>().ok(config); // SM2公钥
}
@GetMapping("/sm2-public-key")
@Operation(summary = "获取SM2公钥")
public Result<String> getSM2PublicKey() {
// 获取SM2公钥
String publicKey = sysParamsService.getValue(Constant.SM2_PUBLIC_KEY, true); String publicKey = sysParamsService.getValue(Constant.SM2_PUBLIC_KEY, true);
if (StringUtils.isBlank(publicKey)) { if (StringUtils.isBlank(publicKey)) {
throw new RenException(ErrorCode.SM2_KEY_NOT_CONFIGURED); throw new RenException(ErrorCode.SM2_KEY_NOT_CONFIGURED);
} }
return new Result<String>().ok(publicKey); config.put("sm2PublicKey", publicKey);
return new Result<Map<String, Object>>().ok(config);
} }
} }
@@ -134,4 +134,6 @@
10123=Your MQTT secret is not secure, it needs to be at least 8 characters and must contain both uppercase and lowercase letters 10123=Your MQTT secret is not secure, it needs to be at least 8 characters and must contain both uppercase and lowercase letters
10124=Your MQTT secret is not secure, MQTT secret must contain both uppercase and lowercase letters 10124=Your MQTT secret is not secure, MQTT secret must contain both uppercase and lowercase letters
10125=Your MQTT secret contains weak password 10125=Your MQTT secret contains weak password
10128=Dictionary label is duplicated 10128=Dictionary label is duplicated
10129=SM2 key not configured
10130=SM2 decryption failed
@@ -134,4 +134,6 @@
10123=您的mqtt密钥长度不安全,字符数需要至少8个字符且必须同时包含大小写字母 10123=您的mqtt密钥长度不安全,字符数需要至少8个字符且必须同时包含大小写字母
10124=您的mqtt密钥长度不安全,mqtt密钥必须同时包含大小写字母 10124=您的mqtt密钥长度不安全,mqtt密钥必须同时包含大小写字母
10125=您的mqtt密钥包含弱密码 10125=您的mqtt密钥包含弱密码
10128=字典标签重复 10128=字典标签重复
10129=SM2密钥未配置
10130=SM2解密失败
@@ -134,4 +134,6 @@
10123=您的MQTT密鑰不安全,需至少8個字符且必須同時包含大小寫字母 10123=您的MQTT密鑰不安全,需至少8個字符且必須同時包含大小寫字母
10124=您的MQTT密鑰不安全,MQTT密鑰必須同時包含大小寫字母 10124=您的MQTT密鑰不安全,MQTT密鑰必須同時包含大小寫字母
10125=您的MQTT密鑰包含弱密碼 10125=您的MQTT密鑰包含弱密碼
10128=字典標籤重複 10128=字典標籤重複
10129=SM2密鑰未配置
10130=SM2解密失敗
-1
View File
@@ -13,7 +13,6 @@
"dotenv": "^16.5.0", "dotenv": "^16.5.0",
"element-ui": "^2.15.14", "element-ui": "^2.15.14",
"flyio": "^0.6.14", "flyio": "^0.6.14",
"jsbn": "^1.1.0",
"normalize.css": "^8.0.1", "normalize.css": "^8.0.1",
"opus-decoder": "^0.7.7", "opus-decoder": "^0.7.7",
"opus-recorder": "^8.0.5", "opus-recorder": "^8.0.5",
+9 -21
View File
@@ -154,7 +154,7 @@ export default {
}).send() }).send()
}, },
// 获取公共配置 // 获取公共配置
getPubConfig(callback) { getPubConfig(callback, failCallback) {
RequestService.sendRequest() RequestService.sendRequest()
.url(`${getServiceUrl()}/user/pub-config`) .url(`${getServiceUrl()}/user/pub-config`)
.method('GET') .method('GET')
@@ -162,10 +162,16 @@ export default {
RequestService.clearRequestTime(); RequestService.clearRequestTime();
callback(res); callback(res);
}) })
.fail((err) => {
RequestService.clearRequestTime();
if (failCallback) {
failCallback(err);
}
})
.networkFail((err) => { .networkFail((err) => {
console.error('获取公共配置失败:', err); console.error('获取公共配置失败:', err);
RequestService.reAjaxFun(() => { RequestService.reAjaxFun(() => {
this.getPubConfig(callback); this.getPubConfig(callback, failCallback);
}); });
}).send(); }).send();
}, },
@@ -193,23 +199,5 @@ export default {
}); });
}).send() }).send()
}, },
// 获取SM2公钥
getSM2PublicKey(callback, failCallback) {
RequestService.sendRequest()
.url(`${getServiceUrl()}/user/sm2-public-key`)
.method('GET')
.success((res) => {
RequestService.clearRequestTime();
callback(res);
})
.fail((err) => {
RequestService.clearRequestTime();
failCallback(err);
})
.networkFail(() => {
RequestService.reAjaxFun(() => {
this.getSM2PublicKey(callback, failCallback);
});
}).send()
}
} }
+23 -51
View File
@@ -148,7 +148,7 @@
import Api from "@/apis/api"; import Api from "@/apis/api";
import VersionFooter from "@/components/VersionFooter.vue"; import VersionFooter from "@/components/VersionFooter.vue";
import i18n, { changeLanguage } from "@/i18n"; import i18n, { changeLanguage } from "@/i18n";
import { getUUID, goToPage, showDanger, showSuccess, validateMobile, generateSm2KeyPairHex, sm2Encrypt, isBase64 } from "@/utils"; import { getUUID, goToPage, showDanger, showSuccess, validateMobile, sm2Encrypt, isBase64 } from "@/utils";
import { mapState } from "vuex"; import { mapState } from "vuex";
import Constant from "@/utils/constant"; import Constant from "@/utils/constant";
@@ -198,7 +198,6 @@ export default {
isMobileLogin: false, isMobileLogin: false,
languageDropdownVisible: false, languageDropdownVisible: false,
serverPublicKey: "", // 服务器公钥 serverPublicKey: "", // 服务器公钥
clientKeyPair: null, // 客户端密钥对
}; };
}, },
mounted() { mounted() {
@@ -209,8 +208,6 @@ export default {
}); });
// 获取服务器公钥 // 获取服务器公钥
this.getServerPublicKey(); this.getServerPublicKey();
// 生成客户端密钥对
this.generateClientKeyPair();
}, },
methods: { methods: {
// 获取服务器公钥 // 获取服务器公钥
@@ -225,30 +222,28 @@ export default {
} }
console.log('本地存储无公钥,从服务器获取...'); console.log('本地存储无公钥,从服务器获取...');
// 从服务器获取公钥 // 从公共配置接口获取公钥
Api.user.getSM2PublicKey( Api.user.getPubConfig(
(res) => { (res) => {
if (res.data && res.data.data) { if (res.data && res.data.data && res.data.data.sm2PublicKey) {
console.log('获取到服务器公钥,长度:', res.data.data.length); console.log('获取到服务器公钥,长度:', res.data.data.sm2PublicKey.length);
this.serverPublicKey = res.data.data; this.serverPublicKey = res.data.data.sm2PublicKey;
// 存储到本地 // 存储到本地
localStorage.setItem(Constant.STORAGE_KEY.PUBLIC_KEY, this.serverPublicKey); localStorage.setItem(Constant.STORAGE_KEY.PUBLIC_KEY, this.serverPublicKey);
console.log('公钥已存储到本地'); console.log('公钥已存储到本地');
} else { } else {
console.error('服务器返回数据格式异常:', res); console.error('服务器返回数据格式异常:', res);
showDanger(this.$t('sm2.failedToGetPublicKey'));
} }
}, },
(err) => { (err) => {
console.error("获取SM2公钥失败:", err); console.error("获取公共配置失败:", err);
showDanger(this.$t('sm2.failedToGetPublicKey')); showDanger(this.$t('sm2.failedToGetPublicKey'));
} }
); );
}, },
// 生成客户端密钥对
generateClientKeyPair() {
this.clientKeyPair = generateSm2KeyPairHex();
},
fetchCaptcha() { fetchCaptcha() {
if (this.$store.getters.getToken) { if (this.$store.getters.getToken) {
@@ -357,36 +352,26 @@ export default {
} }
// 加密密码 // 加密密码
let encryptedPassword = this.form.password; let encryptedPassword;
if (!this.isSM2Encrypted(this.form.password)) { try {
try { // 拼接验证码和密码
encryptedPassword = sm2Encrypt(this.serverPublicKey, this.form.password); const captchaAndPassword = this.form.captcha + this.form.password;
} catch (error) { encryptedPassword = sm2Encrypt(this.serverPublicKey, captchaAndPassword);
console.error("密码加密失败:", error); } catch (error) {
showDanger(this.$t('sm2.encryptionFailed')); console.error("密码加密失败:", error);
return; showDanger(this.$t('sm2.encryptionFailed'));
} return;
} }
// 加密用户名 const plainUsername = this.form.username;
let encryptedUsername = this.form.username;
if (!this.isSM2Encrypted(this.form.username)) {
try {
encryptedUsername = sm2Encrypt(this.serverPublicKey, this.form.username);
} catch (error) {
console.error("用户名加密失败:", error);
showDanger(this.$t('sm2.encryptionFailed'));
return;
}
}
this.form.captchaId = this.captchaUuid; this.form.captchaId = this.captchaUuid;
// 使用加密后的用户名和密码 // 加密
const loginData = { const loginData = {
...this.form, username: plainUsername,
username: encryptedUsername, password: encryptedPassword,
password: encryptedPassword captchaId: this.form.captchaId
}; };
Api.user.login( Api.user.login(
@@ -422,19 +407,6 @@ export default {
}, },
goToForgetPassword() { goToForgetPassword() {
goToPage("/retrieve-password"); goToPage("/retrieve-password");
},
/**
* 判断字符串是否为SM2加密格式(十六进制格式)
* @param {string} str 待判断的字符串
* @returns {boolean} 是否为SM2加密格式
*/
isSM2Encrypted(str) {
if (typeof str !== 'string' || str.trim() === '') {
return false;
}
// 长度大于100且只包含0-9,a-f,A-F字符
return str.length > 100 && /^[0-9a-fA-F]+$/.test(str);
} }
}, },
}; };
+25 -55
View File
@@ -121,7 +121,7 @@
<script> <script>
import Api from '@/apis/api'; import Api from '@/apis/api';
import VersionFooter from '@/components/VersionFooter.vue'; import VersionFooter from '@/components/VersionFooter.vue';
import { getUUID, goToPage, showDanger, showSuccess, validateMobile, generateSm2KeyPairHex, sm2Encrypt, isBase64 } from '@/utils'; import { getUUID, goToPage, showDanger, showSuccess, validateMobile, sm2Encrypt, isBase64 } from '@/utils';
import { mapState } from 'vuex'; import { mapState } from 'vuex';
import Constant from '@/utils/constant'; import Constant from '@/utils/constant';
@@ -159,8 +159,6 @@ export default {
countdown: 0, countdown: 0,
timer: null, timer: null,
serverPublicKey: "", // 服务器公钥 serverPublicKey: "", // 服务器公钥
clientKeyPair: null, // 客户端密钥对
isGettingPublicKey: false // 获取公钥状态
} }
}, },
mounted() { mounted() {
@@ -175,8 +173,6 @@ export default {
this.fetchCaptcha(); this.fetchCaptcha();
// 获取服务器公钥 // 获取服务器公钥
this.getServerPublicKey(); this.getServerPublicKey();
// 生成客户端密钥对
this.generateClientKeyPair();
}, },
methods: { methods: {
// 获取服务器公钥 // 获取服务器公钥
@@ -191,30 +187,28 @@ export default {
} }
console.log('本地存储无公钥,从服务器获取...'); console.log('本地存储无公钥,从服务器获取...');
// 从服务器获取公钥 // 从公共配置接口获取公钥
Api.user.getSM2PublicKey( Api.user.getPubConfig(
(res) => { (res) => {
if (res.data && res.data.data) { if (res.data && res.data.data && res.data.data.sm2PublicKey) {
console.log('获取到服务器公钥,长度:', res.data.data.length); console.log('获取到服务器公钥,长度:', res.data.data.sm2PublicKey.length);
this.serverPublicKey = res.data.data; this.serverPublicKey = res.data.data.sm2PublicKey;
// 存储到本地 // 存储到本地
localStorage.setItem(Constant.STORAGE_KEY.PUBLIC_KEY, this.serverPublicKey); localStorage.setItem(Constant.STORAGE_KEY.PUBLIC_KEY, this.serverPublicKey);
console.log('公钥已存储到本地'); console.log('公钥已存储到本地');
} else { } else {
console.error('服务器返回数据格式异常:', res); console.error('服务器返回数据格式异常:', res);
showDanger(this.$t('sm2.failedToGetPublicKey'));
} }
}, },
(err) => { (err) => {
console.error("获取SM2公钥失败:", err); console.error("获取公共配置失败:", err);
showDanger(this.$t('sm2.failedToGetPublicKey')); showDanger(this.$t('sm2.failedToGetPublicKey'));
} }
); );
}, },
// 生成客户端密钥对
generateClientKeyPair() {
this.clientKeyPair = generateSm2KeyPairHex();
},
// 复用验证码获取方法 // 复用验证码获取方法
fetchCaptcha() { fetchCaptcha() {
@@ -343,41 +337,30 @@ export default {
} }
} }
// 加密密码 // 加密
let encryptedPassword = this.form.password; let encryptedPassword;
if (!this.isSM2Encrypted(this.form.password)) { try {
try { // 拼接验证码和密码
encryptedPassword = sm2Encrypt(this.serverPublicKey, this.form.password); const captchaAndPassword = this.form.captcha + this.form.password;
} catch (error) { encryptedPassword = sm2Encrypt(this.serverPublicKey, captchaAndPassword);
console.error("密码加密失败:", error); } catch (error) {
showDanger(this.$t('sm2.encryptionFailed')); console.error("密码加密失败:", error);
return; showDanger(this.$t('sm2.encryptionFailed'));
} return;
} }
// 加密用户名 let plainUsername;
let encryptedUsername = this.form.username;
if (this.enableMobileRegister) { if (this.enableMobileRegister) {
this.form.username = this.form.areaCode + this.form.mobile; plainUsername = this.form.areaCode + this.form.mobile;
encryptedUsername = this.form.username; } else {
} plainUsername = this.form.username;
if (!this.isSM2Encrypted(encryptedUsername)) {
try {
encryptedUsername = sm2Encrypt(this.serverPublicKey, encryptedUsername);
} catch (error) {
console.error("用户名加密失败:", error);
showDanger(this.$t('sm2.encryptionFailed'));
return;
}
} }
// 准备注册数据 // 准备注册数据
const registerData = { const registerData = {
...this.form, username: plainUsername,
username: encryptedUsername,
password: encryptedPassword, password: encryptedPassword,
confirmPassword: encryptedPassword captchaId: this.form.captchaId
}; };
Api.user.register(registerData, ({ data }) => { Api.user.register(registerData, ({ data }) => {
@@ -393,19 +376,6 @@ export default {
goToLogin() { goToLogin() {
goToPage('/login') goToPage('/login')
},
/**
* 判断字符串是否为SM2加密格式(十六进制格式)
* @param {string} str 待判断的字符串
* @returns {boolean} 是否为SM2加密格式
*/
isSM2Encrypted(str) {
if (typeof str !== 'string' || str.trim() === '') {
return false;
}
// 长度大于100且只包含0-9,a-f,A-F字符
return str.length > 100 && /^[0-9a-fA-F]+$/.test(str);
} }
}, },
beforeDestroy() { beforeDestroy() {