跳到主要内容

三级等保-密码相关

密码功能概述

JeecgBoot的登录系统实现了完整的三级等保密码要求,包括:

  • 密码长度8-20位,必须包含大小写字母、数字和特殊符号、特殊符号(如:@#$%^&*()_+-=)等三种类型
  • 定期更换机制(默认6个月周期,5次内不重复)
  • 密码加密保存默认:使用 md5 + salt(加盐) 存储

三级等保 密码相关的核心代码在后端类 : SecurityPasswordService

密码复杂度验证

后端通过 SecurityPasswordService 类实现验证。使用正则表达式确保密码包含混合字符类型,"无需校验"时可被配置服务禁用。

public static final String PASSWORD_PATTERN = "^(?![a-zA-Z]+$)(?![A-Z0-9]+$)(?![A-Z\\W_!@#$%^&*`~()-+=]+$)(?![a-z0-9]+$)(?![a-z\\W_!@#$%^&*`~()-+=]+$)(?![0-9\\W_!@#$%^&*`~()-+=]+$)[a-zA-Z0-9\\W_!@#$%^&*`~()-+=]*$";
public ResponseDTO<String> validatePassComplexity(String password) {
...
// 无需校验
if (!level3ProtectConfigService.isPasswordComplexityEnabled()) {
return ResponseDTO.ok();
}
// 密码复杂度
if (!password.matches(PASSWORD_PATTERN)) {
return ResponseDTO.userErrorParam(PASSWORD_FORMAT_MSG);
}
return ResponseDTO.ok();
}

密码定期更换

系统使用 t_password_log 表记录密码修改历史。登录时检查上次修改时间,超期则弹出强制修改窗口。前端通过Pinia中的 needUpdatePwdFlag 字段跟踪修改状态。

  • 表结构
CREATE TABLE `t_password_log` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
`user_id` bigint NOT NULL COMMENT '用户id',
`user_type` tinyint NOT NULL COMMENT '用户类型',
`old_password` varchar(255) COLLATE utf8mb4_general_ci NOT NULL COMMENT '旧密码',
`new_password` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '新密码',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`) USING BTREE,
KEY `user_and_type_index` (`user_id`,`user_type`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='密码修改记录';
  • 前端:是否需要修改密码的状态记录在 pinia user state中 needUpdatePwdFlag字段
export const useUserStore = defineStore({
id: 'userStore',
state: () => ({
...
//是否需要修改密码
needUpdatePwdFlag: false,
  • 前端:强制修改密码弹窗在 /layout/index.vue文件中引入 强制修改密码弹窗 RegularChangePasswordModal.vue
/layout/index.vue

<template>
<!--左侧菜单 模式-->
<SideLayout v-if="layout === LAYOUT_ENUM.SIDE.value" />
<!--左侧展开菜单 模式-->
<SideExpandLayout v-if="layout === LAYOUT_ENUM.SIDE_EXPAND.value" />
<!--顶部菜单 模式-->
<TopLayout v-if="layout === LAYOUT_ENUM.TOP.value" />
<!--定期修改密码-->
<RegularChangePasswordModal />
</template>
  • regular-change-password-modal.vue
<template>
<a-modal :open="visible" width="620px" :footer="null" :bodyStyle="{ height: '420px' }" title="" :closable="false" :maskClosable="true">
<a-alert style="width: 550px" message="根据《网络安全法》和《数据安全法》要求,需要定期修改密码保障数据安全!" type="warning" show-icon />
<Password @on-success="refresh" />
</a-modal>
</template>
<script setup>
import { computed } from 'vue';
import Password from '/@/views/system/account/components/password/index.vue';
import { useUserStore } from '/@/store/modules/system/user.js';
import { loginApi } from '/@/api/system/login-api.js';
import { smartSentry } from '/@/lib/smart-sentry.js';
import { SmartLoading } from '/@/components/framework/smart-loading/index.js';

//修改密码弹窗
const visible = computed(() => {
return useUserStore().$state.needUpdatePwdFlag;
});

//刷新
async function refresh() {
try {
SmartLoading.show();
//获取登录用户信息
const res = await loginApi.getLoginInfo();
//更新用户信息到pinia
useUserStore().setUserLoginInfo(res.data);
} catch (e) {
smartSentry.captureError(e);
} finally {
SmartLoading.hide();
}
}
</script>

登录请求加密

前端在发送登录请求前使用 lib/encrypt.js 进行SM4或AES加密,后端由 ApiEncryptService 负责解密。

 try {
SmartLoading.show();
// 密码加密
let encryptPasswordForm = Object.assign({}, loginForm, {
password: encryptData(loginForm.password),
});
const res = await loginApi.login(encryptPasswordForm);
// 登录成功
stopRefreshCaptchaInterval();
localSave(LocalStorageKeyConst.USER_TOKEN, res.data.token ? res.data.token : '');
message.success('登录成功');
//更新用户信息到pinia
useUserStore().setUserLoginInfo(res.data);
//构建系统的路由
buildRoutes();
router.push('/home');
} catch (e) {
...
...
}

密码加盐处理

具体实现位于 SecurityPasswordServicegetEncryptPwd 方法中。