Merge pull request #2224 from xinnan-tech/hot-fix

update:mqtt密钥复杂度校验
This commit is contained in:
欣南科技
2025-09-13 10:19:37 +08:00
committed by GitHub
6 changed files with 76 additions and 41 deletions
+5 -1
View File
@@ -68,7 +68,11 @@ MQTT_SIGNATURE_KEY=test # MQTT签名密钥
``` ```
请注意`PUBLIC_IP`配置,确保其与实际公网IP一致,如果有域名就填域名。 请注意`PUBLIC_IP`配置,确保其与实际公网IP一致,如果有域名就填域名。
`MQTT_SIGNATURE_KEY` 是用于MQTT连接认证的密钥,最好设置成复杂一点的,这个密钥稍后还会用到。 `MQTT_SIGNATURE_KEY` 是用于MQTT连接认证的密钥,最好设置成复杂一点的,最好是设置成8个字符以上且同时包含大小写字母,这个密钥稍后还会用到。
- 注意不要用简单的密码,比如`123456`、`test`等。
- 注意不要用简单的密码,比如`123456`、`test`等。
- 注意不要用简单的密码,比如`123456`、`test`等。
6. 启动MQTT网关 6. 启动MQTT网关
``` ```
@@ -126,6 +126,11 @@ public interface Constant {
*/ */
String SERVER_VOICE_PRINT = "server.voice_print"; String SERVER_VOICE_PRINT = "server.voice_print";
/**
* mqtt密钥
*/
String SERVER_MQTT_SECRET = "server.mqtt_signature_key";
/** /**
* 无记忆 * 无记忆
*/ */
@@ -107,9 +107,12 @@ public class SysParamsController {
// 验证MCP地址 // 验证MCP地址
validateMcpUrl(dto.getParamCode(), dto.getParamValue()); validateMcpUrl(dto.getParamCode(), dto.getParamValue());
// // 验证声纹地址
validateVoicePrint(dto.getParamCode(), dto.getParamValue()); validateVoicePrint(dto.getParamCode(), dto.getParamValue());
// 校验mqtt密钥长度
validateMqttSecretLength(dto.getParamCode(), dto.getParamValue());
sysParamsService.update(dto); sysParamsService.update(dto);
configService.getConfig(false); configService.getConfig(false);
return new Result<Void>(); return new Result<Void>();
@@ -231,6 +234,7 @@ public class SysParamsController {
throw new RenException("MCP接口验证失败:" + e.getMessage()); throw new RenException("MCP接口验证失败:" + e.getMessage());
} }
} }
// 验证声纹接口地址是否正常 // 验证声纹接口地址是否正常
private void validateVoicePrint(String paramCode, String url) { private void validateVoicePrint(String paramCode, String url) {
if (!paramCode.equals(Constant.SERVER_VOICE_PRINT)) { if (!paramCode.equals(Constant.SERVER_VOICE_PRINT)) {
@@ -264,4 +268,21 @@ public class SysParamsController {
throw new RenException("声纹接口验证失败:" + e.getMessage()); throw new RenException("声纹接口验证失败:" + e.getMessage());
} }
} }
// 校验mqtt密钥长度和复杂度
private void validateMqttSecretLength(String paramCode, String secret) {
if (!paramCode.equals(Constant.SERVER_MQTT_SECRET)) {
return;
}
if (StringUtils.isBlank(secret) || secret.equals("null")) {
throw new RenException("mqtt密钥不能为空");
}
if (secret.length() < 8) {
throw new RenException("您的mqtt密钥长度不安全,字符数需要至少8个字符且必须同时包含大小写字母");
}
// 检查是否同时包含大小写字母
if (!secret.matches(".*[a-z].*") || !secret.matches(".*[A-Z].*")) {
throw new RenException("您的mqtt密钥长度不安全,mqtt密钥必须同时包含大小写字母");
}
}
} }
+5 -1
View File
@@ -97,7 +97,7 @@ export default {
}).send() }).send()
}, },
// 修改 // 修改
updateParam(data, callback) { updateParam(data, callback, failCallback) {
RequestService.sendRequest() RequestService.sendRequest()
.url(`${getServiceUrl()}/admin/params`) .url(`${getServiceUrl()}/admin/params`)
.method('PUT') .method('PUT')
@@ -106,6 +106,10 @@ export default {
RequestService.clearRequestTime() RequestService.clearRequestTime()
callback(res) callback(res)
}) })
.fail((err) => {
RequestService.clearRequestTime()
failCallback(err)
})
.networkFail((err) => { .networkFail((err) => {
console.error('更新参数失败:', err) console.error('更新参数失败:', err)
RequestService.reAjaxFun(() => { RequestService.reAjaxFun(() => {
+19 -24
View File
@@ -1,51 +1,43 @@
<template> <template>
<el-dialog :title="title" <el-dialog :title="title" :visible.sync="visible" width="520px" class="param-dialog-wrapper" :append-to-body="true"
:visible.sync="visible" :close-on-click-modal="false" :key="dialogKey" custom-class="custom-param-dialog" :show-close="false">
width="520px"
class="param-dialog-wrapper"
:append-to-body="true"
:close-on-click-modal="false"
:key="dialogKey"
custom-class="custom-param-dialog"
:show-close="false"
>
<div class="dialog-container"> <div class="dialog-container">
<div class="dialog-header"> <div class="dialog-header">
<h2 class="dialog-title">{{ title }}</h2> <h2 class="dialog-title">{{ title }}</h2>
<button class="custom-close-btn" @click="cancel"> <button class="custom-close-btn" @click="cancel">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13 1L1 13M1 1L13 13" stroke="currentColor" stroke-width="2" stroke-linecap="round"/> <path d="M13 1L1 13M1 1L13 13" stroke="currentColor" stroke-width="2" stroke-linecap="round" />
</svg> </svg>
</button> </button>
</div> </div>
<el-form :model="form" :rules="rules" ref="form" label-width="auto" label-position="left" class="param-form"> <el-form :model="form" :rules="rules" ref="form" label-width="auto" label-position="left" class="param-form">
<el-form-item :label="$t('paramDialog.paramCode')" prop="paramCode" class="form-item"> <el-form-item :label="$t('paramDialog.paramCode')" prop="paramCode" class="form-item">
<el-input v-model="form.paramCode" :placeholder="$t('paramDialog.paramCodePlaceholder')" class="custom-input"></el-input> <el-input v-model="form.paramCode" :placeholder="$t('paramDialog.paramCodePlaceholder')"
class="custom-input"></el-input>
</el-form-item> </el-form-item>
<el-form-item :label="$t('paramDialog.paramValue')" prop="paramValue" class="form-item"> <el-form-item :label="$t('paramDialog.paramValue')" prop="paramValue" class="form-item">
<el-input v-model="form.paramValue" :placeholder="$t('paramDialog.paramValuePlaceholder')" class="custom-input"></el-input> <el-input v-model="form.paramValue" :placeholder="$t('paramDialog.paramValuePlaceholder')"
class="custom-input"></el-input>
</el-form-item> </el-form-item>
<el-form-item :label="$t('paramDialog.valueType')" prop="valueType" class="form-item"> <el-form-item :label="$t('paramDialog.valueType')" prop="valueType" class="form-item">
<el-select v-model="form.valueType" :placeholder="$t('paramDialog.valueTypePlaceholder')" class="custom-select"> <el-select v-model="form.valueType" :placeholder="$t('paramDialog.valueTypePlaceholder')"
<el-option v-for="item in valueTypeOptions" :key="item.value" :label="$t(`paramDialog.${item.value}Type`)" :value="item.value"/> class="custom-select">
<el-option v-for="item in valueTypeOptions" :key="item.value" :label="$t(`paramDialog.${item.value}Type`)"
:value="item.value" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item :label="$t('paramDialog.remark')" prop="remark" class="form-item remark-item"> <el-form-item :label="$t('paramDialog.remark')" prop="remark" class="form-item remark-item">
<el-input type="textarea" v-model="form.remark" :placeholder="$t('paramDialog.remarkPlaceholder')" :rows="3" class="custom-textarea"></el-input> <el-input type="textarea" v-model="form.remark" :placeholder="$t('paramDialog.remarkPlaceholder')" :rows="3"
class="custom-textarea"></el-input>
</el-form-item> </el-form-item>
</el-form> </el-form>
<div class="dialog-footer"> <div class="dialog-footer">
<el-button <el-button type="primary" @click="submit" class="save-btn" :loading="saving" :disabled="saving">
type="primary"
@click="submit"
class="save-btn"
:loading="saving"
:disabled="saving">
{{ $t('paramDialog.save') }} {{ $t('paramDialog.save') }}
</el-button> </el-button>
<el-button @click="cancel" class="cancel-btn"> <el-button @click="cancel" class="cancel-btn">
@@ -108,14 +100,17 @@ export default {
if (valid) { if (valid) {
this.saving = true; // 开始加载 this.saving = true; // 开始加载
this.$emit('submit', this.form); this.$emit('submit', this.form);
// 在父组件处理完成后,通过watch visible的变化来重置saving状态
} }
}); });
}, },
cancel() { cancel() {
this.saving = false; // 取消时重置状态 this.saving = false; // 取消时重置状态
this.$emit('cancel'); this.$emit('cancel');
},
// 提供给父组件调用以重置saving状态
resetSaving() {
this.saving = false;
} }
}, },
watch: { watch: {
+20 -14
View File
@@ -93,8 +93,8 @@
</div> </div>
<!-- 新增/编辑参数对话框 --> <!-- 新增/编辑参数对话框 -->
<param-dialog :title="dialogTitle" :visible.sync="dialogVisible" :form="paramForm" @submit="handleSubmit" <param-dialog ref="paramDialog" :title="dialogTitle" :visible.sync="dialogVisible" :form="paramForm"
@cancel="dialogVisible = false" /> @submit="handleSubmit" @cancel="dialogVisible = false" />
<el-footer> <el-footer>
<version-footer /> <version-footer />
</el-footer> </el-footer>
@@ -224,18 +224,20 @@ export default {
if (form.id) { if (form.id) {
// 更新参数 // 更新参数
Api.admin.updateParam(form, ({ data }) => { Api.admin.updateParam(form, ({ data }) => {
if (data.code === 0) { this.dialogVisible = false;
this.dialogVisible = false; this.fetchParams();
this.fetchParams(); this.$message.success({
this.$message.success({ message: this.$t('paramManagement.updateSuccess'),
message: this.$t('paramManagement.updateSuccess'), showClose: true
showClose: true });
}); }, ({ data }) => {
} else { this.$message.error({
this.$message.error({ message: data.msg || this.$t('paramManagement.updateFailed'),
message: data.msg || this.$t('paramManagement.updateFailed'), showClose: true
showClose: true });
}); // 调用ParamDialog的resetSaving方法重置保存状态
if (this.$refs.paramDialog && typeof this.$refs.paramDialog.resetSaving === 'function') {
this.$refs.paramDialog.resetSaving();
} }
}); });
} else { } else {
@@ -253,6 +255,10 @@ export default {
message: data.msg || this.$t('paramManagement.addFailed'), message: data.msg || this.$t('paramManagement.addFailed'),
showClose: true showClose: true
}); });
// 调用ParamDialog的resetSaving方法重置保存状态
if (this.$refs.paramDialog && typeof this.$refs.paramDialog.resetSaving === 'function') {
this.$refs.paramDialog.resetSaving();
}
} }
}); });
} }