feat: 新增账号密码策略

This commit is contained in:
tangwei 2026-08-11 16:22:16 +08:00
parent cca46d159e
commit bcf8279d3d
6 changed files with 464 additions and 0 deletions

View File

@ -77,6 +77,7 @@ public class SecurityConfig {
// .requestMatchers("/threedroamb/**").permitAll()
// .requestMatchers("/overview/**").permitAll()
.requestMatchers("/wt/**").permitAll()
.requestMatchers("/system/acctPasswordPolicy/**").permitAll()
.requestMatchers("/fb/**").permitAll()
// .requestMatchers("/base/**").permitAll()
// .requestMatchers("/zq/**").permitAll()

View File

@ -0,0 +1,139 @@
package com.yfd.platform.system.controller;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yfd.platform.common.DataSourceRequest;
import com.yfd.platform.config.ResponseResult;
import com.yfd.platform.system.domain.SysAcctPasswordPolicy;
import com.yfd.platform.system.service.ISysAcctPasswordPolicyService;
import com.yfd.platform.system.service.IUserService;
import com.yfd.platform.utils.DataSourceRequestUtil;
import com.yfd.platform.utils.SecurityUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import java.util.Date;
import java.util.List;
import java.util.Set;
/**
* <p>
* 账号密码策略 前端控制器
* </p>
*/
@Slf4j
@RestController
@RequestMapping("/system/acctPasswordPolicy")
@Tag(name = "账号密码策略管理")
public class SysAcctPasswordPolicyController {
@Resource
private ISysAcctPasswordPolicyService policyService;
@Resource
private IUserService userService;
@GetMapping("/current")
@Operation(summary = "获取当前生效的密码策略")
public ResponseResult current() {
SysAcctPasswordPolicy policy = policyService.getCurrentPolicy();
return policy == null
? ResponseResult.error("未配置密码策略")
: ResponseResult.successData(policy);
}
@GetMapping("/getById")
@Operation(summary = "根据ID查询密码策略")
public ResponseResult getById(@RequestParam String id) {
SysAcctPasswordPolicy policy = policyService.getById(id);
return policy == null
? ResponseResult.error("策略不存在")
: ResponseResult.successData(policy);
}
@PostMapping("/queryPageList")
@Operation(summary = "分页查询密码策略列表")
public ResponseResult queryPageList(@RequestBody DataSourceRequest request) {
Page<SysAcctPasswordPolicy> page = DataSourceRequestUtil.executeQuery(request, SysAcctPasswordPolicy.class, policyService);
return ResponseResult.successData(page);
}
@PostMapping("/list")
@Operation(summary = "查询密码策略列表(不分页)")
public ResponseResult list(@RequestBody DataSourceRequest request) {
List<SysAcctPasswordPolicy> list = DataSourceRequestUtil.executeList(request, SysAcctPasswordPolicy.class, policyService);
return ResponseResult.successData(list);
}
@PostMapping("/add")
@Operation(summary = "新增密码策略")
public ResponseResult add(@RequestBody SysAcctPasswordPolicy policy) {
if (StrUtil.isBlank(policy.getName())) {
return ResponseResult.error("策略名称不能为空");
}
policy.setId(IdUtil.fastSimpleUUID());
// policy.setRecordUser(SecurityUtils.getUserId());
policy.setRecordUser("admin");
policy.setRecordTime(new Date());
policy.setIsDeleted(0);
boolean ok = policyService.save(policy);
return ok ? ResponseResult.success("新增成功") : ResponseResult.error("新增失败");
}
@PostMapping("/update")
@Operation(summary = "修改密码策略")
public ResponseResult update(@RequestBody SysAcctPasswordPolicy policy) {
if (StrUtil.isBlank(policy.getId())) {
return ResponseResult.error("ID不能为空");
}
SysAcctPasswordPolicy exist = policyService.getById(policy.getId());
if (exist == null) {
return ResponseResult.error("策略不存在");
}
policy.setModifyUser(userService.getUsername());
policy.setModifyTime(new Date());
boolean ok = policyService.updateById(policy);
return ok ? ResponseResult.success("修改成功") : ResponseResult.error("修改失败");
}
@PostMapping("/delete")
@Operation(summary = "删除密码策略(软删除)")
public ResponseResult delete(@RequestBody List<String> ids) {
if (ids == null || ids.isEmpty()) {
return ResponseResult.error("ID不能为空");
}
Date now = new Date();
String username = userService.getUsername();
for (String id : ids) {
SysAcctPasswordPolicy policy = policyService.getById(id);
if (policy != null) {
policy.setIsDeleted(1);
policy.setDeleteUser(username);
policy.setDeleteTime(now);
policyService.updateById(policy);
}
}
return ResponseResult.success("删除成功");
}
@PostMapping("/validatePassword")
@Operation(summary = "根据当前策略校验密码复杂度")
public ResponseResult validatePassword(@RequestParam String password,
@RequestParam(required = false) String username) {
SysAcctPasswordPolicy policy = policyService.getCurrentPolicy();
if (policy == null) {
return ResponseResult.error("未配置密码策略");
}
String errMsg = policyService.validatePassword(policy, password, username);
if (errMsg != null) {
return ResponseResult.error(errMsg);
}
return ResponseResult.success("密码校验通过");
}
}

View File

@ -0,0 +1,189 @@
package com.yfd.platform.system.domain;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.util.Date;
/**
* <p>
* 账号密码策略表
* </p>
*/
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("SYS_ACCT_PASSWORD_POLICY")
public class SysAcctPasswordPolicy implements Serializable {
private static final long serialVersionUID = 1L;
/** 主键ID */
@TableId(type = IdType.ASSIGN_UUID)
private String id;
/** 密码策略标识 */
private String code;
/** 策略名称 */
private String name;
/** 用户名敏感性校验 */
private Integer usernameSensitiveValid;
/** 密码敏感性校验 */
private Integer passwordSensitiveValid;
/** 启用短信重置密码 */
private Integer enableSmsResetPwd;
/** 启用邮箱重置密码 */
private Integer enableEmailResetPwd;
/** 启用输入密码 */
private Integer enableInputPassword;
/** 启用默认密码 */
private Integer enableDefaultPassword;
/** 默认密码 */
private String defaultPassword;
/** 密码最小长度 */
private Integer minLength;
/** 密码最大长度 */
private Integer maxLength;
/** 最大密码错误次数 */
private Integer maxErrorNumber;
/** 密码必须包含字符类型 */
private String passwordCharsRequire;
/** 密码数字数量要求 */
private Integer digitsCount;
/** 密码小写字母数量要求 */
private Integer lowercaseCount;
/** 密码大写字母数量要求 */
private Integer uppercaseCount;
/** 特殊字符数量要求 */
private Integer specialCharCount;
/** 密码不可与用户名相同 */
private Integer notUsername;
/** 密码正则表达式 */
private String regularExpression;
/** 不可使用最近N次密码 */
private Integer notRecentCount;
/** 启用密码策略 */
private Integer enablePasswordPolicy;
/** 启用安全策略 */
private Integer enableSecurityPolicy;
/** 启用账户锁定 */
private Integer enableLock;
/** 锁定时长(秒) */
private Integer lockedExpireTime;
/** 启用验证码 */
private Integer enableCaptcha;
/** 验证码类型 */
private String captureType;
/** 触发验证码的错误次数 */
private Integer maxCheckCaptcha;
/** Web端多端登录 */
private Integer enableWebMultipleLogin;
/** 移动端多端登录 */
private Integer enableAppMultipleLogin;
/** 密码更新频率(天) */
private Integer passwordUpdateRate;
/** 规则变更时强制修改密码 */
private Integer forceModifyPwdRuleModify;
/** 密码修改提醒周期(天) */
private Integer passwordReminderPeriod;
/** 首次登录强制修改密码 */
private Integer forceModifyPassword;
/** 允许账号登录 */
private Integer enableAccountLogin;
/** 修改密码后需重新登录 */
private Integer loginAgain;
/** 允许短信登录 */
private Integer enableSmsLogin;
/** 启用随机密码 */
private Integer enableRandomPassword;
/** 启用数据加密 */
private Integer enableDataSecurity;
/** 是否全局策略 */
private Integer isBelongGlobal;
/** 是否双重验证 */
private Integer isTwoFactorAuth;
/** 创建人 */
@TableField(fill = FieldFill.INSERT)
private String recordUser;
/** 创建时间 */
@TableField(fill = FieldFill.INSERT)
private Date recordTime;
/** 修改人 */
@TableField(fill = FieldFill.UPDATE)
private String modifyUser;
/** 修改时间 */
@TableField(fill = FieldFill.UPDATE)
private Date modifyTime;
/** 是否已删除 0=未删除 1=已删除 */
@TableLogic
private Integer isDeleted;
/** 删除人 */
private String deleteUser;
/** 删除时间 */
private Date deleteTime;
/** 过滤内容 */
private String filterContent;
/** 排序 */
private Integer orderIndex;
/** 启用验证码类型 */
private String enableCaptchaType;
/** 启用企业微信登录 */
private Integer enableEnterpriseWechatLogin;
/** 启用钉钉登录 */
private Integer enableDingdingLogin;
/** 启用微信登录 */
private Integer enableWechatLogin;
}

View File

@ -0,0 +1,14 @@
package com.yfd.platform.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yfd.platform.system.domain.SysAcctPasswordPolicy;
import org.apache.ibatis.annotations.Mapper;
/**
* <p>
* 账号密码策略表 Mapper 接口
* </p>
*/
@Mapper
public interface SysAcctPasswordPolicyMapper extends BaseMapper<SysAcctPasswordPolicy> {
}

View File

@ -0,0 +1,26 @@
package com.yfd.platform.system.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.yfd.platform.system.domain.SysAcctPasswordPolicy;
/**
* <p>
* 账号密码策略表 服务类
* </p>
*/
public interface ISysAcctPasswordPolicyService extends IService<SysAcctPasswordPolicy> {
/**
* 获取当前生效的策略取排序最小且未删除的那条
*/
SysAcctPasswordPolicy getCurrentPolicy();
/**
* 根据策略校验密码复杂度
*
* @param password 待校验的密码
* @param username 关联的用户名用于NOT_USERNAME校验
* @return null=校验通过否则返回错误提示
*/
String validatePassword(SysAcctPasswordPolicy policy, String password, String username);
}

View File

@ -0,0 +1,95 @@
package com.yfd.platform.system.service.impl;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yfd.platform.system.domain.SysAcctPasswordPolicy;
import com.yfd.platform.system.mapper.SysAcctPasswordPolicyMapper;
import com.yfd.platform.system.service.ISysAcctPasswordPolicyService;
import org.springframework.stereotype.Service;
import java.util.regex.Pattern;
/**
* <p>
* 账号密码策略表 服务实现类
* </p>
*/
@Service
public class SysAcctPasswordPolicyServiceImpl extends ServiceImpl<SysAcctPasswordPolicyMapper, SysAcctPasswordPolicy>
implements ISysAcctPasswordPolicyService {
@Override
public SysAcctPasswordPolicy getCurrentPolicy() {
LambdaQueryWrapper<SysAcctPasswordPolicy> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SysAcctPasswordPolicy::getIsDeleted, 0)
.eq(SysAcctPasswordPolicy::getEnablePasswordPolicy, 1)
.orderByAsc(SysAcctPasswordPolicy::getOrderIndex)
.last("LIMIT 1");
return getOne(wrapper);
}
@Override
public String validatePassword(SysAcctPasswordPolicy policy, String password, String username) {
if (policy == null || StrUtil.isBlank(password)) {
return "密码不能为空";
}
// 1. 密码长度校验
if (policy.getMinLength() != null && password.length() < policy.getMinLength()) {
return "密码长度不能少于" + policy.getMinLength() + "";
}
if (policy.getMaxLength() != null && password.length() > policy.getMaxLength()) {
return "密码长度不能超过" + policy.getMaxLength() + "";
}
// 2. 密码与用户名不可相同
if (isEnabled(policy.getNotUsername()) && StrUtil.isNotBlank(username) && username.equals(password)) {
return "密码不能与用户名相同";
}
// 3. 字符类型数量校验
int digits = 0, lowercase = 0, uppercase = 0, special = 0;
for (char c : password.toCharArray()) {
if (Character.isDigit(c)) {
digits++;
} else if (Character.isLowerCase(c)) {
lowercase++;
} else if (Character.isUpperCase(c)) {
uppercase++;
} else {
special++;
}
}
if (policy.getDigitsCount() != null && digits < policy.getDigitsCount()) {
return "密码至少包含" + policy.getDigitsCount() + "个数字";
}
if (policy.getLowercaseCount() != null && lowercase < policy.getLowercaseCount()) {
return "密码至少包含" + policy.getLowercaseCount() + "个小写字母";
}
if (policy.getUppercaseCount() != null && uppercase < policy.getUppercaseCount()) {
return "密码至少包含" + policy.getUppercaseCount() + "个大写字母";
}
if (policy.getSpecialCharCount() != null && special < policy.getSpecialCharCount()) {
return "密码至少包含" + policy.getSpecialCharCount() + "个特殊字符";
}
// 4. 正则表达式校验
if (StrUtil.isNotBlank(policy.getRegularExpression())) {
try {
if (!Pattern.matches(policy.getRegularExpression(), password)) {
return "密码格式不符合要求";
}
} catch (Exception e) {
// 正则不合法则跳过
}
}
return null;
}
private boolean isEnabled(Integer value) {
return value != null && value == 1;
}
}