mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-22 07:03:53 +08:00
fix:修复SM2非对称加密的各种问题
This commit is contained in:
@@ -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");
|
||||||
|
|||||||
+62
-101
@@ -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解密失敗
|
||||||
@@ -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",
|
||||||
|
|||||||
@@ -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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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() {
|
||||||
|
|||||||
Reference in New Issue
Block a user