diff --git a/continew-admin-common/src/main/java/top/continew/admin/common/constant/CacheConstants.java b/continew-admin-common/src/main/java/top/continew/admin/common/constant/CacheConstants.java index 79d869d6..074dbaa9 100644 --- a/continew-admin-common/src/main/java/top/continew/admin/common/constant/CacheConstants.java +++ b/continew-admin-common/src/main/java/top/continew/admin/common/constant/CacheConstants.java @@ -66,6 +66,11 @@ public class CacheConstants { */ public static final String DASHBOARD_KEY_PREFIX = "DASHBOARD" + DELIMITER; + /** + * 用户密码错误次数缓存键前缀 + */ + public static final String USER_PASSWORD_ERROR_KEY_PREFIX = USER_KEY_PREFIX + "PASSWORD_ERROR" + DELIMITER; + private CacheConstants() { } } diff --git a/continew-admin-common/src/main/java/top/continew/admin/common/constant/RegexConstants.java b/continew-admin-common/src/main/java/top/continew/admin/common/constant/RegexConstants.java index f11cb5bd..b73e5e56 100644 --- a/continew-admin-common/src/main/java/top/continew/admin/common/constant/RegexConstants.java +++ b/continew-admin-common/src/main/java/top/continew/admin/common/constant/RegexConstants.java @@ -25,27 +25,32 @@ public class RegexConstants { /** - * 用户名正则(长度为 4 到 64 位,可以包含字母、数字,下划线,以字母开头) + * 用户名正则(用户名长度为 4-64 个字符,支持大小写字母、数字、下划线,以字母开头) */ public static final String USERNAME = "^[a-zA-Z][a-zA-Z0-9_]{3,64}$"; /** - * 密码正则(长度为 6 到 32 位,可以包含字母、数字、下划线,特殊字符,同时包含字母和数字) + * 密码正则模板(密码长度为 min-max 个字符,支持大小写字母、数字、特殊字符,至少包含字母和数字) */ - public static final String PASSWORD = "^(?=.*\\d)(?=.*[a-z]).{6,32}$"; + public static final String PASSWORD_TEMPLATE = "^(?=.*\\d)(?=.*[a-z]).{%s,%s}$"; /** - * 密码正则严格版(长度为 8 到 32 位,包含至少1个大写字母、1个小写字母、1个数字,1个特殊字符) + * 密码正则(密码长度为 8-32 个字符,支持大小写字母、数字、特殊字符,至少包含字母和数字) */ - public static final String PASSWORD_STRICT = "^\\S*(?=\\S{8,32})(?=\\S*\\d)(?=\\S*[A-Z])(?=\\S*[a-z])(?=\\S*[!@#$%^&*? ])\\S*$"; + public static final String PASSWORD = "^(?=.*\\d)(?=.*[a-z]).{8,32}$"; /** - * 通用编码正则(长度为 2 到 30 位,可以包含字母、数字,下划线,以字母开头) + * 特殊字符正则 + */ + public static final String SPECIAL_CHARACTER = "[-_`~!@#$%^&*()+=|{}':;',\\\\[\\\\].<>/?~!@#¥%……&*()——+|{}【】‘;:”“’。,、?]|\\n|\\r|\\t"; + + /** + * 通用编码正则(长度为 2-30 个字符,支持大小写字母、数字、下划线,以字母开头) */ public static final String GENERAL_CODE = "^[a-zA-Z][a-zA-Z0-9_]{1,29}$"; /** - * 通用名称正则(长度为 2 到 30 位,可以包含中文、字母、数字、下划线,短横线) + * 通用名称正则(长度为 2-30 个字符,支持中文、字母、数字、下划线,短横线) */ public static final String GENERAL_NAME = "^[\\u4e00-\\u9fa5a-zA-Z0-9_-]{2,30}$"; diff --git a/continew-admin-common/src/main/java/top/continew/admin/common/constant/SysConstants.java b/continew-admin-common/src/main/java/top/continew/admin/common/constant/SysConstants.java index 3f82f348..a07381ea 100644 --- a/continew-admin-common/src/main/java/top/continew/admin/common/constant/SysConstants.java +++ b/continew-admin-common/src/main/java/top/continew/admin/common/constant/SysConstants.java @@ -24,6 +24,16 @@ */ public class SysConstants { + /** + * 否 + */ + public static final Integer NO = 0; + + /** + * 是 + */ + public static final Integer YES = 1; + /** * 管理员角色编码 */ diff --git a/continew-admin-system/src/main/java/top/continew/admin/auth/service/LoginService.java b/continew-admin-system/src/main/java/top/continew/admin/auth/service/LoginService.java index 8a46565d..34a81359 100644 --- a/continew-admin-system/src/main/java/top/continew/admin/auth/service/LoginService.java +++ b/continew-admin-system/src/main/java/top/continew/admin/auth/service/LoginService.java @@ -16,6 +16,7 @@ package top.continew.admin.auth.service; +import jakarta.servlet.http.HttpServletRequest; import me.zhyd.oauth.model.AuthUser; import top.continew.admin.auth.model.resp.RouteResp; @@ -34,9 +35,10 @@ public interface LoginService { * * @param username 用户名 * @param password 密码 + * @param request 请求对象 * @return 令牌 */ - String accountLogin(String username, String password); + String accountLogin(String username, String password, HttpServletRequest request); /** * 手机号登录 diff --git a/continew-admin-system/src/main/java/top/continew/admin/auth/service/impl/LoginServiceImpl.java b/continew-admin-system/src/main/java/top/continew/admin/auth/service/impl/LoginServiceImpl.java index 00be3150..220a6ac9 100644 --- a/continew-admin-system/src/main/java/top/continew/admin/auth/service/impl/LoginServiceImpl.java +++ b/continew-admin-system/src/main/java/top/continew/admin/auth/service/impl/LoginServiceImpl.java @@ -21,7 +21,9 @@ import cn.hutool.core.lang.tree.Tree; import cn.hutool.core.lang.tree.TreeNodeConfig; import cn.hutool.core.util.*; +import cn.hutool.extra.servlet.JakartaServletUtil; import cn.hutool.json.JSONUtil; +import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import me.zhyd.oauth.model.AuthUser; import org.springframework.security.crypto.password.PasswordEncoder; @@ -39,7 +41,7 @@ import top.continew.admin.common.model.dto.LoginUser; import top.continew.admin.common.util.helper.LoginHelper; import top.continew.admin.system.enums.MessageTemplateEnum; -import top.continew.admin.system.enums.OptionCodeEnum; +import top.continew.admin.system.enums.PasswordPolicyEnum; import top.continew.admin.system.model.entity.DeptDO; import top.continew.admin.system.model.entity.RoleDO; import top.continew.admin.system.model.entity.UserDO; @@ -80,36 +82,15 @@ public class LoginServiceImpl implements LoginService { private final OptionService optionService; @Override - public String accountLogin(String username, String password) { + public String accountLogin(String username, String password, HttpServletRequest request) { UserDO user = userService.getByUsername(username); boolean isError = ObjectUtil.isNull(user) || !passwordEncoder.matches(password, user.getPassword()); - isPasswordLocked(username, isError); + this.checkUserLocked(username, request, isError); CheckUtils.throwIf(isError, "用户名或密码错误"); this.checkUserStatus(user); return this.login(user); } - /** - * 检测用户是否被密码锁定 - * - * @param username 用户名 - */ - private void isPasswordLocked(String username, boolean isError) { - // 不锁定账户 - int maxErrorCount = optionService.getValueByCode2Int(OptionCodeEnum.PASSWORD_ERROR_COUNT); - if (maxErrorCount <= 0) { - return; - } - int lockMinutes = optionService.getValueByCode2Int(OptionCodeEnum.PASSWORD_LOCK_MINUTES); - String key = CacheConstants.USER_KEY_PREFIX + "PASSWORD-ERROR:" + username; - long currentErrorCount = 0; - if (isError) { - currentErrorCount = RedisUtils.incr(key); - RedisUtils.expire(key, Duration.ofMinutes(lockMinutes)); - } - CheckUtils.throwIf(currentErrorCount > maxErrorCount, "密码错误已达 {} 次,账户锁定 {} 分钟", maxErrorCount, lockMinutes); - } - @Override public String phoneLogin(String phone) { UserDO user = userService.getByPhone(phone); @@ -229,6 +210,36 @@ private void checkUserStatus(UserDO user) { CheckUtils.throwIfEqual(DisEnableStatusEnum.DISABLE, dept.getStatus(), "此账号所属部门已被禁用,如有疑问,请联系管理员"); } + /** + * 检测用户是否已被锁定 + * + * @param username 用户名 + * @param request 请求对象 + * @param isError 是否登录错误 + */ + private void checkUserLocked(String username, HttpServletRequest request, boolean isError) { + // 不锁定 + int maxErrorCount = optionService.getValueByCode2Int(PasswordPolicyEnum.PASSWORD_ERROR_LOCK_COUNT.name()); + if (maxErrorCount <= SysConstants.NO) { + return; + } + // 检测是否已被锁定 + String key = CacheConstants.USER_PASSWORD_ERROR_KEY_PREFIX + RedisUtils.formatKey(username, JakartaServletUtil + .getClientIP(request)); + int lockMinutes = optionService.getValueByCode2Int(PasswordPolicyEnum.PASSWORD_ERROR_LOCK_MINUTES.name()); + Integer currentErrorCount = ObjectUtil.defaultIfNull(RedisUtils.get(key), 0); + CheckUtils.throwIf(currentErrorCount >= maxErrorCount, "账号锁定 {} 分钟,请稍后再试", lockMinutes); + // 登录成功清除计数 + if (!isError) { + RedisUtils.delete(key); + return; + } + // 登录失败递增计数 + currentErrorCount++; + RedisUtils.set(key, currentErrorCount, Duration.ofMinutes(lockMinutes)); + CheckUtils.throwIf(currentErrorCount >= maxErrorCount, "密码错误已达 {} 次,账号锁定 {} 分钟", maxErrorCount, lockMinutes); + } + /** * 发送系统消息 * @@ -237,8 +248,8 @@ private void checkUserStatus(UserDO user) { private void sendSystemMsg(UserDO user) { MessageReq req = new MessageReq(); MessageTemplateEnum socialRegister = MessageTemplateEnum.SOCIAL_REGISTER; - req.setTitle(StrUtil.format(socialRegister.getTitle(), projectProperties.getName())); - req.setContent(StrUtil.format(socialRegister.getContent(), user.getNickname())); + req.setTitle(socialRegister.getTitle().formatted(projectProperties.getName())); + req.setContent(socialRegister.getContent().formatted(user.getNickname())); req.setType(MessageTypeEnum.SYSTEM); messageService.add(req, CollUtil.toList(user.getId())); } diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/enums/MessageTemplateEnum.java b/continew-admin-system/src/main/java/top/continew/admin/system/enums/MessageTemplateEnum.java index 3ceb34c2..ef61af58 100644 --- a/continew-admin-system/src/main/java/top/continew/admin/system/enums/MessageTemplateEnum.java +++ b/continew-admin-system/src/main/java/top/continew/admin/system/enums/MessageTemplateEnum.java @@ -32,7 +32,7 @@ public enum MessageTemplateEnum { /** * 第三方登录 */ - SOCIAL_REGISTER("欢迎注册 {}", "尊敬的 {},欢迎注册使用,请及时配置您的密码。"); + SOCIAL_REGISTER("欢迎注册 %s", "尊敬的 %s,欢迎注册使用,请及时配置您的密码。"); private final String title; private final String content; diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/enums/OptionCodeEnum.java b/continew-admin-system/src/main/java/top/continew/admin/system/enums/OptionCodeEnum.java deleted file mode 100644 index 4233799c..00000000 --- a/continew-admin-system/src/main/java/top/continew/admin/system/enums/OptionCodeEnum.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package top.continew.admin.system.enums; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -/** - * 参数枚举 - * - * @author Kils - * @since 2024/05/09 11:25 - */ -@Getter -@RequiredArgsConstructor -public enum OptionCodeEnum { - - /** - * 密码是否允许包含正反序帐户名 - */ - PASSWORD_CONTAIN_NAME("password_contain_name", "密码不允许包含正反序帐户名"), - /** - * 密码错误锁定帐户次数 - */ - PASSWORD_ERROR_COUNT("password_error_count", "密码错误锁定帐户次数"), - /** - * 密码有效期 - */ - PASSWORD_EXPIRATION_DAYS("password_expiration_days", "密码有效期"), - /** - * 密码是否允许包含正反序帐户名 - */ - PASSWORD_LOCK_MINUTES("password_lock_minutes", "密码错误锁定帐户的时间"), - /** - * 密码最小长度 - */ - PASSWORD_MIN_LENGTH("password_min_length", "密码最小长度"), - /** - * 密码是否必须包含特殊字符 - */ - PASSWORD_SPECIAL_CHAR("password_special_char", "密码是否必须包含特殊字符"), - /** - * 修改密码最短间隔 - */ - PASSWORD_UPDATE_INTERVAL("password_update_interval", "修改密码最短间隔"); - - private final String value; - private final String description; -} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/enums/PasswordPolicyEnum.java b/continew-admin-system/src/main/java/top/continew/admin/system/enums/PasswordPolicyEnum.java new file mode 100644 index 00000000..0764d64a --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/enums/PasswordPolicyEnum.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.admin.system.enums; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import top.continew.admin.common.constant.SysConstants; + +/** + * 密码策略枚举 + * + * @author Kils + * @author Charles7c + * @since 2024/5/9 11:25 + */ +@Getter +@RequiredArgsConstructor +public enum PasswordPolicyEnum { + + /** + * 登录密码错误锁定账号的次数 + */ + PASSWORD_ERROR_LOCK_COUNT(null, SysConstants.NO, 10), + + /** + * 登录密码错误锁定账号的时间(min) + */ + PASSWORD_ERROR_LOCK_MINUTES(null, 1, 1440), + + /** + * 密码到期提前提示(天) + */ + PASSWORD_EXPIRATION_WARNING_DAYS(null, SysConstants.NO, Integer.MAX_VALUE), + + /** + * 密码有效期(天) + */ + PASSWORD_EXPIRATION_DAYS(null, SysConstants.NO, 999), + + /** + * 密码重复使用规则 + */ + PASSWORD_REUSE_POLICY("不允许使用最近 %s 次的历史密码", 3, 32), + + /** + * 密码最小长度 + */ + PASSWORD_MIN_LENGTH("密码最小长度为 %s 个字符", 8, 32), + + /** + * 密码是否允许包含正反序账号名 + */ + PASSWORD_ALLOW_CONTAIN_USERNAME("密码不允许包含正反序账号名", SysConstants.NO, SysConstants.YES), + + /** + * 密码是否必须包含特殊字符 + */ + PASSWORD_CONTAIN_SPECIAL_CHARACTERS("密码必须包含特殊字符", SysConstants.NO, SysConstants.YES),; + + private final String description; + private final Integer min; + private final Integer max; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/req/DeptReq.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/DeptReq.java index cda0dd95..3a93009d 100644 --- a/continew-admin-system/src/main/java/top/continew/admin/system/model/req/DeptReq.java +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/DeptReq.java @@ -54,7 +54,7 @@ public class DeptReq extends BaseReq { */ @Schema(description = "名称", example = "测试部") @NotBlank(message = "名称不能为空") - @Pattern(regexp = RegexConstants.GENERAL_NAME, message = "名称长度为 2 到 30 位,可以包含中文、字母、数字、下划线,短横线") + @Pattern(regexp = RegexConstants.GENERAL_NAME, message = "名称长度为 2-30 个字符,支持中文、字母、数字、下划线,短横线") private String name; /** diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/req/DictReq.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/DictReq.java index d30faa09..37a54f43 100644 --- a/continew-admin-system/src/main/java/top/continew/admin/system/model/req/DictReq.java +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/DictReq.java @@ -44,7 +44,7 @@ public class DictReq extends BaseReq { */ @Schema(description = "名称", example = "公告类型") @NotBlank(message = "名称不能为空") - @Pattern(regexp = RegexConstants.GENERAL_NAME, message = "名称长度为 2 到 30 位,可以包含中文、字母、数字、下划线,短横线") + @Pattern(regexp = RegexConstants.GENERAL_NAME, message = "名称长度为 2-30 个字符,支持中文、字母、数字、下划线,短横线") private String name; /** @@ -52,7 +52,7 @@ public class DictReq extends BaseReq { */ @Schema(description = "编码", example = "notice_type") @NotBlank(message = "编码不能为空") - @Pattern(regexp = RegexConstants.GENERAL_CODE, message = "编码长度为 2 到 30 位,可以包含字母、数字,下划线,以字母开头") + @Pattern(regexp = RegexConstants.GENERAL_CODE, message = "编码长度为 2-30 个字符,支持大小写字母、数字、下划线,以字母开头") private String code; /** diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/req/RoleReq.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/RoleReq.java index 41c39515..d23ede21 100644 --- a/continew-admin-system/src/main/java/top/continew/admin/system/model/req/RoleReq.java +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/RoleReq.java @@ -48,7 +48,7 @@ public class RoleReq extends BaseReq { */ @Schema(description = "名称", example = "测试人员") @NotBlank(message = "名称不能为空") - @Pattern(regexp = RegexConstants.GENERAL_NAME, message = "名称长度为 2 到 30 位,可以包含中文、字母、数字、下划线,短横线") + @Pattern(regexp = RegexConstants.GENERAL_NAME, message = "名称长度为 2-30 个字符,支持中文、字母、数字、下划线,短横线") private String name; /** @@ -56,7 +56,7 @@ public class RoleReq extends BaseReq { */ @Schema(description = "编码", example = "test") @NotBlank(message = "编码不能为空") - @Pattern(regexp = RegexConstants.GENERAL_CODE, message = "编码长度为 2 到 30 位,可以包含字母、数字,下划线,以字母开头") + @Pattern(regexp = RegexConstants.GENERAL_CODE, message = "编码长度为 2-30 个字符,支持大小写字母、数字、下划线,以字母开头") private String code; /** diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/req/StorageReq.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/StorageReq.java index 9e685438..2dca57b5 100644 --- a/continew-admin-system/src/main/java/top/continew/admin/system/model/req/StorageReq.java +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/StorageReq.java @@ -55,7 +55,7 @@ public class StorageReq extends BaseReq { */ @Schema(description = "编码", example = "local") @NotBlank(message = "编码不能为空") - @Pattern(regexp = RegexConstants.GENERAL_CODE, message = "编码长度为 2 到 30 位,可以包含字母、数字,下划线,以字母开头") + @Pattern(regexp = RegexConstants.GENERAL_CODE, message = "编码长度为 2-30 个字符,支持大小写字母、数字、下划线,以字母开头") private String code; /** diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/req/UserBasicInfoUpdateReq.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/UserBasicInfoUpdateReq.java index c026269b..318a4314 100644 --- a/continew-admin-system/src/main/java/top/continew/admin/system/model/req/UserBasicInfoUpdateReq.java +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/UserBasicInfoUpdateReq.java @@ -45,7 +45,7 @@ public class UserBasicInfoUpdateReq implements Serializable { */ @Schema(description = "昵称", example = "张三") @NotBlank(message = "昵称不能为空") - @Pattern(regexp = RegexConstants.GENERAL_NAME, message = "昵称长度为 2 到 30 位,可以包含中文、字母、数字、下划线,短横线") + @Pattern(regexp = RegexConstants.GENERAL_NAME, message = "昵称长度为 2-30 个字符,支持中文、字母、数字、下划线,短横线") private String nickname; /** diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/req/UserReq.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/UserReq.java index 602af6ef..509cfb87 100644 --- a/continew-admin-system/src/main/java/top/continew/admin/system/model/req/UserReq.java +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/UserReq.java @@ -51,7 +51,7 @@ public class UserReq extends BaseReq { */ @Schema(description = "用户名", example = "zhangsan") @NotBlank(message = "用户名不能为空") - @Pattern(regexp = RegexConstants.USERNAME, message = "用户名长度为 4 到 64 位,可以包含字母、数字,下划线,以字母开头") + @Pattern(regexp = RegexConstants.USERNAME, message = "用户名长度为 4-64 个字符,支持大小写字母、数字、下划线,以字母开头") private String username; /** @@ -59,7 +59,7 @@ public class UserReq extends BaseReq { */ @Schema(description = "昵称", example = "张三") @NotBlank(message = "昵称不能为空") - @Pattern(regexp = RegexConstants.GENERAL_NAME, message = "昵称长度为 2 到 30 位,可以包含中文、字母、数字、下划线,短横线") + @Pattern(regexp = RegexConstants.GENERAL_NAME, message = "昵称长度为 2-30 个字符,支持中文、字母、数字、下划线,短横线") private String nickname; /** diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/service/OptionService.java b/continew-admin-system/src/main/java/top/continew/admin/system/service/OptionService.java index fcc1c47d..a0aec51d 100644 --- a/continew-admin-system/src/main/java/top/continew/admin/system/service/OptionService.java +++ b/continew-admin-system/src/main/java/top/continew/admin/system/service/OptionService.java @@ -16,15 +16,14 @@ package top.continew.admin.system.service; -import java.util.List; -import java.util.function.Function; - -import top.continew.admin.system.enums.OptionCodeEnum; import top.continew.admin.system.model.query.OptionQuery; import top.continew.admin.system.model.req.OptionReq; import top.continew.admin.system.model.req.OptionResetValueReq; import top.continew.admin.system.model.resp.OptionResp; +import java.util.List; +import java.util.function.Function; + /** * 参数业务接口 * @@ -56,19 +55,19 @@ public interface OptionService { void resetValue(OptionResetValueReq req); /** - * 根据code获取int参数值 - * - * @param code code - * @return 参数值 + * 根据编码查询参数值 + * + * @param code 编码 + * @return 参数值(自动转换为 int 类型) */ - int getValueByCode2Int(OptionCodeEnum code); + int getValueByCode2Int(String code); /** - * 根据code获取参数值 - * - * @param code code - * @param mapper 类型转换 ex:value -> Integer.parseInt(value) + * 根据编码查询参数值 + * + * @param code 编码 + * @param mapper 转换方法 e.g.:value -> Integer.parseInt(value) * @return 参数值 */ - T getValueByCode(OptionCodeEnum code, Function mapper); -} \ No newline at end of file + T getValueByCode(String code, Function mapper); +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/OptionServiceImpl.java b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/OptionServiceImpl.java index a9304f9b..fef23e5d 100644 --- a/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/OptionServiceImpl.java +++ b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/OptionServiceImpl.java @@ -17,14 +17,10 @@ package top.continew.admin.system.service.impl; import cn.hutool.core.bean.BeanUtil; -import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.StrUtil; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.core.toolkit.Wrappers; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import top.continew.admin.common.constant.CacheConstants; -import top.continew.admin.system.enums.OptionCodeEnum; import top.continew.admin.system.mapper.OptionMapper; import top.continew.admin.system.model.entity.OptionDO; import top.continew.admin.system.model.query.OptionQuery; @@ -70,25 +66,24 @@ public void resetValue(OptionResetValueReq req) { } @Override - public int getValueByCode2Int(OptionCodeEnum code) { + public int getValueByCode2Int(String code) { return this.getValueByCode(code, Integer::parseInt); } @Override - public T getValueByCode(OptionCodeEnum code, Function mapper) { - String value = RedisUtils.get(CacheConstants.OPTION_KEY_PREFIX + code.getValue()); + public T getValueByCode(String code, Function mapper) { + String value = RedisUtils.get(CacheConstants.OPTION_KEY_PREFIX + code); if (StrUtil.isNotBlank(value)) { return mapper.apply(value); } - LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery() - .eq(OptionDO::getCode, code.getValue()) - .select(OptionDO::getValue, OptionDO::getDefaultValue); - OptionDO optionDO = baseMapper.selectOne(queryWrapper); - CheckUtils.throwIf(ObjUtil.isEmpty(optionDO), "配置 [{}] 不存在", code); - value = StrUtil.nullToDefault(optionDO.getValue(), optionDO.getDefaultValue()); - CheckUtils.throwIf(StrUtil.isBlank(value), "配置 [{}] 不存在", code); - RedisUtils.set(CacheConstants.OPTION_KEY_PREFIX + code.getValue(), value); + OptionDO option = baseMapper.lambdaQuery() + .eq(OptionDO::getCode, code) + .select(OptionDO::getValue, OptionDO::getDefaultValue) + .one(); + CheckUtils.throwIfNull(option, "参数 [{}] 不存在", code); + value = StrUtil.nullToDefault(option.getValue(), option.getDefaultValue()); + CheckUtils.throwIfBlank(value, "参数 [{}] 数据错误", code); + RedisUtils.set(CacheConstants.OPTION_KEY_PREFIX + code, value); return mapper.apply(value); } - } \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/UserServiceImpl.java b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/UserServiceImpl.java index 6c2c371f..cfd72d0b 100644 --- a/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/UserServiceImpl.java +++ b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/UserServiceImpl.java @@ -18,7 +18,6 @@ import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.date.LocalDateTimeUtil; import cn.hutool.core.io.file.FileNameUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ReUtil; @@ -41,6 +40,7 @@ import top.continew.admin.auth.service.OnlineUserService; import top.continew.admin.common.constant.CacheConstants; import top.continew.admin.common.constant.RegexConstants; +import top.continew.admin.common.constant.SysConstants; import top.continew.admin.common.enums.DisEnableStatusEnum; import top.continew.admin.common.util.helper.LoginHelper; import top.continew.admin.system.mapper.UserMapper; @@ -63,10 +63,13 @@ import top.continew.starter.extension.crud.service.impl.BaseServiceImpl; import java.time.LocalDateTime; -import java.util.*; +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Optional; import java.util.stream.Collectors; -import static top.continew.admin.system.enums.OptionCodeEnum.*; +import static top.continew.admin.system.enums.PasswordPolicyEnum.*; /** * 用户业务实现 @@ -197,7 +200,7 @@ public void updatePassword(String oldPassword, String newPassword, Long id) { CheckUtils.throwIf(!passwordEncoder.matches(oldPassword, password), "当前密码错误"); } // 校验密码合法性 - checkPassword(newPassword, user); + this.checkPassword(newPassword, user); // 更新密码和密码重置时间 user.setPassword(newPassword); user.setPwdResetTime(LocalDateTime.now()); @@ -205,52 +208,11 @@ public void updatePassword(String oldPassword, String newPassword, Long id) { onlineUserService.cleanByUserId(user.getId()); } - /** - * 检测修改密码合法性 - * - * @param password 密码 - * @param user 用户 - */ - private void checkPassword(String password, UserDO user) { - // 密码最小长度 - int passwordMinLength = optionService.getValueByCode2Int(PASSWORD_MIN_LENGTH); - ValidationUtils.throwIf(StrUtil.length(password) < passwordMinLength, PASSWORD_MIN_LENGTH - .getDescription() + "为 {}", passwordMinLength); - // 密码是否允许包含正反序用户名 - int passwordContainName = optionService.getValueByCode2Int(PASSWORD_CONTAIN_NAME); - if (passwordContainName == 1) { - String username = user.getUsername(); - ValidationUtils.throwIf(StrUtil.containsIgnoreCase(password, username) || StrUtil - .containsIgnoreCase(password, StrUtil.reverse(username)), PASSWORD_CONTAIN_NAME.getDescription()); - } - // 密码是否必须包含特殊字符 - int passwordSpecialChar = optionService.getValueByCode2Int(PASSWORD_SPECIAL_CHAR); - String match = RegexConstants.PASSWORD; - String desc = "密码长度为 6 到 32 位,可以包含字母、数字、下划线,特殊字符,同时包含字母和数字"; - if (passwordSpecialChar == 1) { - match = RegexConstants.PASSWORD_STRICT; - desc = "密码长度为 8 到 32 位,包含至少1个大写字母、1个小写字母、1个数字,1个特殊字符"; - } - ValidationUtils.throwIf(!ReUtil.isMatch(match, password), desc); - // 密码修改间隔 - if (ObjectUtil.isNull(user.getPwdResetTime())) { - return; - } - int passwordUpdateInterval = optionService.getValueByCode2Int(PASSWORD_UPDATE_INTERVAL); - if (passwordUpdateInterval <= 0) { - return; - } - LocalDateTime lastResetTime = user.getPwdResetTime(); - LocalDateTime limitUpdateTime = lastResetTime.plusMinutes(passwordUpdateInterval); - ValidationUtils.throwIf(LocalDateTime.now().isBefore(limitUpdateTime), "上次修改于:{},下次可修改时间:{}", LocalDateTimeUtil - .formatNormal(lastResetTime), LocalDateTimeUtil.formatNormal(limitUpdateTime)); - } - @Override public Boolean isPasswordExpired(LocalDateTime pwdResetTime) { // 永久有效 - int passwordExpirationDays = optionService.getValueByCode2Int(PASSWORD_EXPIRATION_DAYS); - if (passwordExpirationDays <= 0) { + int passwordExpirationDays = optionService.getValueByCode2Int(PASSWORD_EXPIRATION_DAYS.name()); + if (passwordExpirationDays <= SysConstants.NO) { return false; } // 初始密码也提示修改 @@ -375,6 +337,33 @@ protected void afterAdd(UserReq req, UserDO user) { userRoleService.add(req.getRoleIds(), userId); } + /** + * 检测密码合法性 + * + * @param password 密码 + * @param user 用户信息 + */ + private void checkPassword(String password, UserDO user) { + // 密码最小长度 + int passwordMinLength = optionService.getValueByCode2Int(PASSWORD_MIN_LENGTH.name()); + ValidationUtils.throwIf(StrUtil.length(password) < passwordMinLength, PASSWORD_MIN_LENGTH.getDescription() + .formatted(passwordMinLength)); + // 密码是否允许包含正反序账号名 + int passwordAllowContainUsername = optionService.getValueByCode2Int(PASSWORD_ALLOW_CONTAIN_USERNAME.name()); + if (passwordAllowContainUsername == SysConstants.NO) { + String username = user.getUsername(); + ValidationUtils.throwIf(StrUtil.containsAnyIgnoreCase(password, username, StrUtil + .reverse(username)), PASSWORD_ALLOW_CONTAIN_USERNAME.getDescription()); + } + int passwordMaxLength = PASSWORD_MIN_LENGTH.getMax(); + ValidationUtils.throwIf(!ReUtil.isMatch(RegexConstants.PASSWORD_TEMPLATE + .formatted(passwordMinLength, passwordMaxLength), password), "密码长度为 {}-{} 个字符,支持大小写字母、数字、特殊字符,至少包含字母和数字", passwordMinLength, passwordMaxLength); + // 密码是否必须包含特殊字符 + int passwordContainSpecialChar = optionService.getValueByCode2Int(PASSWORD_CONTAIN_SPECIAL_CHARACTERS.name()); + ValidationUtils.throwIf(passwordContainSpecialChar == SysConstants.YES && !ReUtil + .isMatch(RegexConstants.SPECIAL_CHARACTER, password), PASSWORD_CONTAIN_SPECIAL_CHARACTERS.getDescription()); + } + /** * 名称是否存在 * diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/webapi/auth/AuthController.java b/continew-admin-webapi/src/main/java/top/continew/admin/webapi/auth/AuthController.java index 631f46e4..1076c0db 100644 --- a/continew-admin-webapi/src/main/java/top/continew/admin/webapi/auth/AuthController.java +++ b/continew-admin-webapi/src/main/java/top/continew/admin/webapi/auth/AuthController.java @@ -23,6 +23,7 @@ import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.enums.ParameterIn; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -68,7 +69,7 @@ public class AuthController { @SaIgnore @Operation(summary = "账号登录", description = "根据账号和密码进行登录认证") @PostMapping("/account") - public R accountLogin(@Validated @RequestBody AccountLoginReq loginReq) { + public R accountLogin(@Validated @RequestBody AccountLoginReq loginReq, HttpServletRequest request) { String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + loginReq.getUuid(); String captcha = RedisUtils.get(captchaKey); ValidationUtils.throwIfBlank(captcha, CAPTCHA_EXPIRED); @@ -77,35 +78,35 @@ public R accountLogin(@Validated @RequestBody AccountLoginReq loginRe // 用户登录 String rawPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(loginReq.getPassword())); ValidationUtils.throwIfBlank(rawPassword, "密码解密失败"); - String token = loginService.accountLogin(loginReq.getUsername(), rawPassword); + String token = loginService.accountLogin(loginReq.getUsername(), rawPassword, request); return R.ok(LoginResp.builder().token(token).build()); } @SaIgnore - @Operation(summary = "邮箱登录", description = "根据邮箱和验证码进行登录认证") - @PostMapping("/email") - public R emailLogin(@Validated @RequestBody EmailLoginReq loginReq) { - String email = loginReq.getEmail(); - String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + email; + @Operation(summary = "手机号登录", description = "根据手机号和验证码进行登录认证") + @PostMapping("/phone") + public R phoneLogin(@Validated @RequestBody PhoneLoginReq loginReq) { + String phone = loginReq.getPhone(); + String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + phone; String captcha = RedisUtils.get(captchaKey); ValidationUtils.throwIfBlank(captcha, CAPTCHA_EXPIRED); ValidationUtils.throwIfNotEqualIgnoreCase(loginReq.getCaptcha(), captcha, CAPTCHA_ERROR); RedisUtils.delete(captchaKey); - String token = loginService.emailLogin(email); + String token = loginService.phoneLogin(phone); return R.ok(LoginResp.builder().token(token).build()); } @SaIgnore - @Operation(summary = "手机号登录", description = "根据手机号和验证码进行登录认证") - @PostMapping("/phone") - public R phoneLogin(@Validated @RequestBody PhoneLoginReq loginReq) { - String phone = loginReq.getPhone(); - String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + phone; + @Operation(summary = "邮箱登录", description = "根据邮箱和验证码进行登录认证") + @PostMapping("/email") + public R emailLogin(@Validated @RequestBody EmailLoginReq loginReq) { + String email = loginReq.getEmail(); + String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + email; String captcha = RedisUtils.get(captchaKey); ValidationUtils.throwIfBlank(captcha, CAPTCHA_EXPIRED); ValidationUtils.throwIfNotEqualIgnoreCase(loginReq.getCaptcha(), captcha, CAPTCHA_ERROR); RedisUtils.delete(captchaKey); - String token = loginService.phoneLogin(phone); + String token = loginService.emailLogin(email); return R.ok(LoginResp.builder().token(token).build()); } diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/webapi/system/UserController.java b/continew-admin-webapi/src/main/java/top/continew/admin/webapi/system/UserController.java index 250396a1..09229009 100644 --- a/continew-admin-webapi/src/main/java/top/continew/admin/webapi/system/UserController.java +++ b/continew-admin-webapi/src/main/java/top/continew/admin/webapi/system/UserController.java @@ -61,7 +61,7 @@ public R add(@Validated(ValidateGroup.Crud.Add.class) @RequestBody UserReq String rawPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(req.getPassword())); ValidationUtils.throwIfNull(rawPassword, "密码解密失败"); ValidationUtils.throwIf(!ReUtil - .isMatch(RegexConstants.PASSWORD, rawPassword), "密码长度为 6 到 32 位,可以包含字母、数字、下划线,特殊字符,同时包含字母和数字"); + .isMatch(RegexConstants.PASSWORD, rawPassword), "密码长度为 8-32 个字符,支持大小写字母、数字、特殊字符,至少包含字母和数字"); req.setPassword(rawPassword); return super.add(req); } @@ -74,7 +74,7 @@ public R resetPassword(@Validated @RequestBody UserPasswordResetReq req, @ String rawNewPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(req.getNewPassword())); ValidationUtils.throwIfNull(rawNewPassword, "新密码解密失败"); ValidationUtils.throwIf(!ReUtil - .isMatch(RegexConstants.PASSWORD, rawNewPassword), "密码长度为 6 到 32 位,可以包含字母、数字、下划线,特殊字符,同时包含字母和数字"); + .isMatch(RegexConstants.PASSWORD, rawNewPassword), "密码长度为 8-32 个字符,支持大小写字母、数字、特殊字符,至少包含字母和数字"); req.setNewPassword(rawNewPassword); baseService.resetPassword(req, id); return R.ok("重置密码成功"); diff --git a/continew-admin-webapi/src/main/resources/db/changelog/mysql/continew-admin_data.sql b/continew-admin-webapi/src/main/resources/db/changelog/mysql/continew-admin_data.sql index e95fe2f7..d09ab452 100644 --- a/continew-admin-webapi/src/main/resources/db/changelog/mysql/continew-admin_data.sql +++ b/continew-admin-webapi/src/main/resources/db/changelog/mysql/continew-admin_data.sql @@ -1,6 +1,6 @@ -- liquibase formatted sql --- changeset Charles7c:2.5.0 +-- changeset Charles7c:1 -- comment 初始化表数据 -- 初始化默认菜单 INSERT INTO `sys_menu` @@ -109,19 +109,20 @@ VALUES INSERT INTO `sys_option` (`name`, `code`, `value`, `default_value`, `description`, `update_user`, `update_time`) VALUES -('系统标题', 'site_title', NULL, 'ContiNew Admin', '用于显示登录页面的系统标题。', NULL, NULL), -('版权信息', 'site_copyright', NULL, - 'Copyright © 2022-present Charles7c  ContiNew Admin  津ICP备2022005864号-2', +('系统标题', 'SITE_TITLE', NULL, 'ContiNew Admin', '用于显示登录页面的系统标题。', NULL, NULL), +('版权信息', 'SITE_COPYRIGHT', NULL, + 'Copyright © 2022-present Charles7c  ContiNew Admin  津ICP备2022005864号-3', '用于显示登录页面的底部版权信息。', NULL, NULL), -('系统LOGO(16*16)', 'site_favicon', NULL, '/favicon.ico', '用于显示浏览器地址栏的系统LOGO。', NULL, NULL), -('系统LOGO(33*33)', 'site_logo', NULL, '/logo.svg', '用于显示登录页面的系统LOGO。', NULL, NULL), -('密码是否允许包含正反序帐户名', 'password_contain_name', NULL, '0', '', NULL, NULL), -('密码错误锁定帐户次数', 'password_error_count', NULL, '5', '0表示不限制。', NULL, NULL), -('密码有效期', 'password_expiration_days', NULL, '0', '取值范围为0-999,0表示永久有效。', NULL, NULL), -('密码错误锁定帐户的时间', 'password_lock_minutes', NULL, '5', '0表示不解锁。', NULL, NULL), -('密码最小长度', 'password_min_length', NULL, '8', '取值范围为8-32。', NULL, NULL), -('密码是否必须包含特殊字符', 'password_special_char', NULL, '0', '', NULL, NULL), -('修改密码最短间隔', 'password_update_interval', NULL, '5', '取值范围为0-9999,0表示不限制。', NULL, NULL); +('系统LOGO(16*16)', 'SITE_FAVICON', NULL, '/favicon.ico', '用于显示浏览器地址栏的系统LOGO。', NULL, NULL), +('系统LOGO(33*33)', 'SITE_LOGO', NULL, '/logo.svg', '用于显示登录页面的系统LOGO。', NULL, NULL), +('登录密码错误锁定账号的次数', 'PASSWORD_ERROR_LOCK_COUNT', NULL, '5', '取值范围为 0-10(0 表示不锁定)。', NULL, NULL), +('登录密码错误锁定账号的时间(min)', 'PASSWORD_ERROR_LOCK_MINUTES', NULL, '5', '取值范围为 1-1440(一天)。', NULL, NULL), +('密码到期提前提示(天)', 'PASSWORD_EXPIRATION_WARNING_DAYS', NULL, '0', '密码到期 N 天前进行提示(0 表示不提示)。', NULL, NULL), +('密码有效期(天)', 'PASSWORD_EXPIRATION_DAYS', NULL, '0', '取值范围为 0-999(0 表示永久有效)。', NULL, NULL), +('密码重复使用规则', 'PASSWORD_REUSE_POLICY', NULL, '5', '不允许使用最近 N 次密码,取值范围为 3-32。', NULL, NULL), +('密码最小长度', 'PASSWORD_MIN_LENGTH', NULL, '8', '取值范围为 8-32。', NULL, NULL), +('密码是否允许包含正反序账号名', 'PASSWORD_ALLOW_CONTAIN_USERNAME', NULL, '0', '', NULL, NULL), +('密码是否必须包含特殊字符', 'PASSWORD_CONTAIN_SPECIAL_CHARACTERS', NULL, '0', '', NULL, NULL); -- 初始化默认字典 INSERT INTO `sys_dict` diff --git a/continew-admin-webapi/src/main/resources/db/changelog/mysql/continew-admin_table.sql b/continew-admin-webapi/src/main/resources/db/changelog/mysql/continew-admin_table.sql index e9233e3b..523a55da 100644 --- a/continew-admin-webapi/src/main/resources/db/changelog/mysql/continew-admin_table.sql +++ b/continew-admin-webapi/src/main/resources/db/changelog/mysql/continew-admin_table.sql @@ -1,6 +1,6 @@ -- liquibase formatted sql --- changeset Charles7c:2.5.0 +-- changeset Charles7c:1 -- comment 初始化表结构 CREATE TABLE IF NOT EXISTS `sys_menu` ( `id` bigint(20) NOT NULL COMMENT 'ID', @@ -256,19 +256,19 @@ CREATE TABLE IF NOT EXISTS `sys_storage` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='存储表'; CREATE TABLE IF NOT EXISTS `sys_file` ( - `id` bigint(20) NOT NULL COMMENT 'ID', - `name` varchar(255) NOT NULL COMMENT '名称', - `size` bigint(20) NOT NULL COMMENT '大小(字节)', - `url` varchar(512) NOT NULL COMMENT 'URL', - `extension` varchar(100) DEFAULT NULL COMMENT '扩展名', - `type` tinyint(1) UNSIGNED NOT NULL DEFAULT 1 COMMENT '类型(1:其他;2:图片;3:文档;4:视频;5:音频)', - `storage_id` bigint(20) NOT NULL COMMENT '存储ID', - `create_user` bigint(20) NOT NULL COMMENT '创建人', - `create_time` datetime NOT NULL COMMENT '创建时间', - `update_user` bigint(20) NOT NULL COMMENT '修改人', - `update_time` datetime NOT NULL COMMENT '修改时间', - `thumbnail_size` bigint(20) NULL DEFAULT NULL COMMENT '缩略图大小(字节)', - `thumbnail_url` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '缩略图URL', + `id` bigint(20) NOT NULL COMMENT 'ID', + `name` varchar(255) NOT NULL COMMENT '名称', + `size` bigint(20) NOT NULL COMMENT '大小(字节)', + `url` varchar(512) NOT NULL COMMENT 'URL', + `extension` varchar(100) DEFAULT NULL COMMENT '扩展名', + `thumbnail_size` bigint(20) DEFAULT NULL COMMENT '缩略图大小(字节)', + `thumbnail_url` varchar(512) DEFAULT NULL COMMENT '缩略图URL', + `type` tinyint(1) UNSIGNED NOT NULL DEFAULT 1 COMMENT '类型(1:其他;2:图片;3:文档;4:视频;5:音频)', + `storage_id` bigint(20) NOT NULL COMMENT '存储ID', + `create_user` bigint(20) NOT NULL COMMENT '创建人', + `create_time` datetime NOT NULL COMMENT '创建时间', + `update_user` bigint(20) NOT NULL COMMENT '修改人', + `update_time` datetime NOT NULL COMMENT '修改时间', PRIMARY KEY (`id`) USING BTREE, INDEX `idx_url`(`url`) USING BTREE, INDEX `idx_type`(`type`) USING BTREE, diff --git a/continew-admin-webapi/src/main/resources/db/changelog/postgresql/continew-admin_data.sql b/continew-admin-webapi/src/main/resources/db/changelog/postgresql/continew-admin_data.sql index 1acac866..79d04e2d 100644 --- a/continew-admin-webapi/src/main/resources/db/changelog/postgresql/continew-admin_data.sql +++ b/continew-admin-webapi/src/main/resources/db/changelog/postgresql/continew-admin_data.sql @@ -1,6 +1,6 @@ -- liquibase formatted sql --- changeset Charles7c:2.5.0 +-- changeset Charles7c:1 -- comment 初始化表数据 -- 初始化默认菜单 INSERT INTO "sys_menu" @@ -109,19 +109,20 @@ VALUES INSERT INTO "sys_option" ("name", "code", "value", "default_value", "description", "update_user", "update_time") VALUES -('系统标题', 'site_title', NULL, 'ContiNew Admin', '用于显示登录页面的系统标题。', NULL, NULL), -('版权信息', 'site_copyright', NULL, - 'Copyright © 2022-present Charles7c  ContiNew Admin  津ICP备2022005864号-2', +('系统标题', 'SITE_TITLE', NULL, 'ContiNew Admin', '用于显示登录页面的系统标题。', NULL, NULL), +('版权信息', 'SITE_COPYRIGHT', NULL, + 'Copyright © 2022-present Charles7c  ContiNew Admin  津ICP备2022005864号-3', '用于显示登录页面的底部版权信息。', NULL, NULL), -('系统LOGO(16*16)', 'site_favicon', NULL, '/favicon.ico', '用于显示浏览器地址栏的系统LOGO。', NULL, NULL), -('系统LOGO(33*33)', 'site_logo', NULL, '/logo.svg', '用于显示登录页面的系统LOGO。', NULL, NULL), -('密码是否允许包含正反序帐户名', 'password_contain_name', NULL, '0', '', NULL, NULL), -('密码错误锁定帐户次数', 'password_error_count', NULL, '5', '0表示不限制。', NULL, NULL), -('密码有效期', 'password_expiration_days', NULL, '0', '取值范围为0-999,0表示永久有效。', NULL, NULL), -('密码错误锁定帐户的时间', 'password_lock_minutes', NULL, '5', '0表示不解锁。', NULL, NULL), -('密码最小长度', 'password_min_length', NULL, '8', '取值范围为8-32。', NULL, NULL), -('密码是否必须包含特殊字符', 'password_special_char', NULL, '0', '', NULL, NULL), -('修改密码最短间隔', 'password_update_interval', NULL, '5', '取值范围为0-9999,0表示不限制。', NULL, NULL); +('系统LOGO(16*16)', 'SITE_FAVICON', NULL, '/favicon.ico', '用于显示浏览器地址栏的系统LOGO。', NULL, NULL), +('系统LOGO(33*33)', 'SITE_LOGO', NULL, '/logo.svg', '用于显示登录页面的系统LOGO。', NULL, NULL), +('登录密码错误锁定账号的次数', 'PASSWORD_ERROR_LOCK_COUNT', NULL, '5', '取值范围为 0-10(0 表示不锁定)。', NULL, NULL), +('登录密码错误锁定账号的时间(min)', 'PASSWORD_ERROR_LOCK_MINUTES', NULL, '5', '取值范围为 1-1440(一天)。', NULL, NULL), +('密码到期提前提示(天)', 'PASSWORD_EXPIRATION_WARNING_DAYS', NULL, '0', '密码到期 N 天前进行提示(0 表示不提示)。', NULL, NULL), +('密码有效期(天)', 'PASSWORD_EXPIRATION_DAYS', NULL, '0', '取值范围为 0-999(0 表示永久有效)。', NULL, NULL), +('密码重复使用规则', 'PASSWORD_REUSE_POLICY', NULL, '5', '不允许使用最近 N 次密码,取值范围为 3-32。', NULL, NULL), +('密码最小长度', 'PASSWORD_MIN_LENGTH', NULL, '8', '取值范围为 8-32。', NULL, NULL), +('密码是否允许包含正反序账号名', 'PASSWORD_ALLOW_CONTAIN_USERNAME', NULL, '0', '', NULL, NULL), +('密码是否必须包含特殊字符', 'PASSWORD_CONTAIN_SPECIAL_CHARACTERS', NULL, '0', '', NULL, NULL); -- 初始化默认字典 INSERT INTO "sys_dict" diff --git a/continew-admin-webapi/src/main/resources/db/changelog/postgresql/continew-admin_table.sql b/continew-admin-webapi/src/main/resources/db/changelog/postgresql/continew-admin_table.sql index 388eebd9..72ddcc53 100644 --- a/continew-admin-webapi/src/main/resources/db/changelog/postgresql/continew-admin_table.sql +++ b/continew-admin-webapi/src/main/resources/db/changelog/postgresql/continew-admin_table.sql @@ -1,6 +1,6 @@ -- liquibase formatted sql --- changeset Charles7c:2.5.0 +-- changeset Charles7c:1 -- comment 初始化表结构 CREATE TABLE IF NOT EXISTS "sys_menu" ( "id" int8 NOT NULL, @@ -430,39 +430,39 @@ COMMENT ON COLUMN "sys_storage"."update_time" IS '修改时间'; COMMENT ON TABLE "sys_storage" IS '存储表'; CREATE TABLE IF NOT EXISTS "sys_file" ( - "id" int8 NOT NULL, - "name" varchar(255) NOT NULL, - "size" int8 NOT NULL, - "url" varchar(512) NOT NULL, - "extension" varchar(100) DEFAULT NULL, - "type" int2 NOT NULL DEFAULT 1, - "storage_id" int8 NOT NULL, - "create_user" int8 NOT NULL, - "create_time" timestamp NOT NULL, - "update_user" int8 NOT NULL, - "update_time" timestamp NOT NULL, - "thumbnail_size" int8 DEFAULT NULL, - "thumbnail_url" varchar(512) DEFAULT NULL, + "id" int8 NOT NULL, + "name" varchar(255) NOT NULL, + "size" int8 NOT NULL, + "url" varchar(512) NOT NULL, + "extension" varchar(100) DEFAULT NULL, + "thumbnail_size" int8 DEFAULT NULL, + "thumbnail_url" varchar(512) DEFAULT NULL, + "type" int2 NOT NULL DEFAULT 1, + "storage_id" int8 NOT NULL, + "create_user" int8 NOT NULL, + "create_time" timestamp NOT NULL, + "update_user" int8 NOT NULL, + "update_time" timestamp NOT NULL, PRIMARY KEY ("id") ); CREATE INDEX "idx_file_url" ON "sys_file" ("url"); CREATE INDEX "idx_file_type" ON "sys_file" ("type"); CREATE INDEX "idx_file_create_user" ON "sys_file" ("create_user"); CREATE INDEX "idx_file_update_user" ON "sys_file" ("update_user"); -COMMENT ON COLUMN "sys_file"."id" IS 'ID'; -COMMENT ON COLUMN "sys_file"."name" IS '名称'; -COMMENT ON COLUMN "sys_file"."size" IS '大小(字节)'; -COMMENT ON COLUMN "sys_file"."url" IS 'URL'; -COMMENT ON COLUMN "sys_file"."extension" IS '扩展名'; -COMMENT ON COLUMN "sys_file"."type" IS '类型(1:其他;2:图片;3:文档;4:视频;5:音频)'; -COMMENT ON COLUMN "sys_file"."storage_id" IS '存储ID'; -COMMENT ON COLUMN "sys_file"."create_user" IS '创建人'; -COMMENT ON COLUMN "sys_file"."create_time" IS '创建时间'; -COMMENT ON COLUMN "sys_file"."update_user" IS '修改人'; -COMMENT ON COLUMN "sys_file"."update_time" IS '修改时间'; +COMMENT ON COLUMN "sys_file"."id" IS 'ID'; +COMMENT ON COLUMN "sys_file"."name" IS '名称'; +COMMENT ON COLUMN "sys_file"."size" IS '大小(字节)'; +COMMENT ON COLUMN "sys_file"."url" IS 'URL'; +COMMENT ON COLUMN "sys_file"."extension" IS '扩展名'; COMMENT ON COLUMN "sys_file"."thumbnail_size" IS '缩略图大小(字节)'; -COMMENT ON COLUMN "sys_file"."thumbnail_url" IS '缩略图URL'; -COMMENT ON TABLE "sys_file" IS '文件表'; +COMMENT ON COLUMN "sys_file"."thumbnail_url" IS '缩略图URL'; +COMMENT ON COLUMN "sys_file"."type" IS '类型(1:其他;2:图片;3:文档;4:视频;5:音频)'; +COMMENT ON COLUMN "sys_file"."storage_id" IS '存储ID'; +COMMENT ON COLUMN "sys_file"."create_user" IS '创建人'; +COMMENT ON COLUMN "sys_file"."create_time" IS '创建时间'; +COMMENT ON COLUMN "sys_file"."update_user" IS '修改人'; +COMMENT ON COLUMN "sys_file"."update_time" IS '修改时间'; +COMMENT ON TABLE "sys_file" IS '文件表'; CREATE TABLE IF NOT EXISTS "gen_config" ( "table_name" varchar(64) NOT NULL,