diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 1671f3f7..98af0ded 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -49,6 +49,7 @@ jobs: script: | cd /docker docker-compose up --force-recreate --build -d continew-admin-server + docker images | grep none | awk '{print $3}' | xargs docker rmi # 部署前端 deploy-web: diff --git a/README.md b/README.md index b0e72aca..14d15996 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ContiNew-Admin 中后台管理框架 +# ContiNew Admin 中后台管理框架 [![License](https://img.shields.io/badge/License-Apache%202.0-green.svg)](https://github.com/Charles7c/continew-admin/blob/dev/LICENSE) ![SNAPSHOT](https://img.shields.io/badge/SNAPSHOT-v0.0.1-%23ff3f59.svg) @@ -7,7 +7,7 @@ ## 简介 -ContiNew-Admin (incubating) 中后台管理框架,Continue New Admin,持续以最新流行技术栈构建。当前阶段采用的技术栈:Vue3、TypeScript、Arco Design Pro Vue、Spring Boot、Undertow、Sa-Token、JWT、MariaDB、MyBatis Plus、Redis、Redisson、Hutool 等。 +ContiNew Admin 中后台管理框架(孵化中),Continue New Admin,持续以最新流行技术栈构建。当前阶段采用的技术栈:Vue3、TypeScript、Arco Design Pro Vue、Spring Boot、Undertow、Sa-Token、JWT、MariaDB、MyBatis Plus、Redis、Redisson、Hutool 等。 ## 开始 @@ -21,11 +21,11 @@ git clone https://github.com/Charles7c/continew-admin.git # 2.在 IDE(IntelliJ IDEA/Eclipse)中打开本项目 -# 3.修改配置文件中的 Redis 配置信息 +# 3.修改配置文件中的数据源配置信息、Redis 配置信息、邮件配置信息等 # [3.也可以在 IntelliJ IDEA 中直接配置程序启动环境变量(DB_HOST、DB_PORT、DB_USER、DB_PWD、DB_NAME;REDIS_HOST、REDIS_PORT、REDIS_PWD、REDIS_DB)] # 4.启动程序 -# 4.1 启动成功:访问 http://localhost:8000/,页面输出:ContiNew-Admin backend service started successfully. +# 4.1 启动成功:访问 http://localhost:8000/,页面输出:ContiNew Admin backend service started successfully. # 4.2 接口文档:http://localhost:8000/doc.html # 5.部署 @@ -72,7 +72,7 @@ yarn dev | :----------------------------------------------------------- | :----------- | :----------------------------------------------------------- | | [Vue](https://cn.vuejs.org/) | 3.2.45 | 渐进式 JavaScript 框架,易学易用,性能出色,适用场景丰富的 Web 前端框架。 | | [TypeScript](https://www.typescriptlang.org/zh/) | 4.9.4 | TypeScript 是微软开发的一个开源的编程语言,通过在 JavaScript 的基础上添加静态类型定义构建而成。 | -| [Arco Design Pro Vue](http://pro.arco.design/) | 2.5.15 | 基于 Arco Design Vue 组件库的开箱即用的中后台前端解决方案。 | +| [Arco Design Pro Vue](http://pro.arco.design/) | 2.6.0 | 基于 Arco Design Vue 组件库的开箱即用的中后台前端解决方案。 | | [Spring Boot](https://spring.io/projects/spring-boot) | 2.7.7 | 简化新 Spring 应用的初始搭建以及开发过程。 | | [Undertow](https://undertow.io/) | 2.2.22.Final | 采用 Java 开发的灵活的高性能 Web 服务器,提供包括阻塞和基于 NIO 的非堵塞机制。 | | [Sa-Token + JWT](https://sa-token.dev33.cn/) | 1.33.0 | 轻量级 Java 权限认证框架,让鉴权变得简单、优雅。 | @@ -110,10 +110,14 @@ continew-admin # 全局通用项目配置及依赖版本管理 │ │ ├─ webapi │ │ │ └─ controller │ │ │ ├─ auth # 认证相关 API + │ │ │ ├─ common # 公共相关 API(例如:验证码 API 等) │ │ │ └─ system # 系统管理相关 API │ │ └─ ContinewAdminApplication.java # 启动入口 │ └─ resources # 工程配置目录 - │ └─ db.changelog.v0.0.1 # 数据库脚本文件 + │ ├─ db.changelog # 数据库脚本文件 + │ │ └─ v0.0.1 # v0.0.1 版本数据库脚本文件 + │ └─ templates # 模板文件 + │ └─ mail # 邮件模板 ├─ continew-admin-monitor # 系统监控模块(存放系统监控模块相关功能,例如:日志管理、服务监控等) │ └─ src │ └─ main @@ -144,8 +148,7 @@ continew-admin # 全局通用项目配置及依赖版本管理 │ │ └─ cnadmin │ │ ├─ auth # 系统认证相关业务及配置 │ │ │ ├─ config # 系统认证相关配置 - │ │ │ │ ├─ satoken # Sa-Token 配置 - │ │ │ │ └─ properties # 系统认证相关配置属性 + │ │ │ │ └─ satoken # Sa-Token 配置 │ │ │ ├─ model # 系统认证相关模型 │ │ │ │ ├─ request # 系统认证相关请求对象 │ │ │ │ └─ vo # 系统认证相关 VO(View Object) @@ -197,6 +200,7 @@ continew-admin ├─ src │ ├─ api # 请求接口 │ │ ├─ auth # 认证模块 + │ │ ├─ common # 公共模块 │ │ └─ system # 系统管理模块 │ ├─ assets # 静态资源 │ │ ├─ images # 图片资源 diff --git a/continew-admin-common/pom.xml b/continew-admin-common/pom.xml index 565242f9..8a0b50c8 100644 --- a/continew-admin-common/pom.xml +++ b/continew-admin-common/pom.xml @@ -59,6 +59,17 @@ limitations under the License. + + + org.springframework.boot + spring-boot-starter-mail + + + + org.freemarker + freemarker + + org.springframework.boot @@ -126,5 +137,11 @@ limitations under the License. org.redisson redisson-spring-boot-starter + + + + com.github.whvcse + easy-captcha + \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/config/properties/CaptchaProperties.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/config/properties/CaptchaProperties.java similarity index 51% rename from continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/config/properties/CaptchaProperties.java rename to continew-admin-common/src/main/java/top/charles7c/cnadmin/common/config/properties/CaptchaProperties.java index 5123ea89..e9e40ae3 100644 --- a/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/config/properties/CaptchaProperties.java +++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/config/properties/CaptchaProperties.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package top.charles7c.cnadmin.auth.config.properties; +package top.charles7c.cnadmin.common.config.properties; import java.awt.*; @@ -43,65 +43,102 @@ import cn.hutool.core.util.StrUtil; public class CaptchaProperties { /** - * 类型 + * 图片验证码配置 */ - private CaptchaTypeEnum type; + private CaptchaImage image; /** - * 缓存键的前缀 + * 邮箱验证码配置 */ - private String keyPrefix; + private CaptchaMail mail; /** - * 过期时间 + * 图片验证码配置 */ - private Long expirationInMinutes = 2L; + @Data + public static class CaptchaImage { + /** + * 类型 + */ + private CaptchaImageTypeEnum type; - /** - * 内容长度 - */ - private int length = 4; + /** + * 内容长度 + */ + private int length; - /** - * 宽度 - */ - private int width = 111; + /** + * 过期时间 + */ + private long expirationInMinutes; - /** - * 高度 - */ - private int height = 36; + /** + * 宽度 + */ + private int width = 111; - /** - * 字体 - */ - private String fontName; + /** + * 高度 + */ + private int height = 36; - /** - * 字体大小 - */ - private int fontSize = 25; + /** + * 字体 + */ + private String fontName; - /** - * 获取验证码对象 - * - * @return 验证码对象 - */ - public Captcha getCaptcha() { - Captcha captcha = ReflectUtil.newInstance(type.getClazz(), this.width, this.height); - captcha.setLen(length); - if (StrUtil.isNotBlank(this.fontName)) { - captcha.setFont(new Font(this.fontName, Font.PLAIN, this.fontSize)); + /** + * 字体大小 + */ + private int fontSize = 25; + + /** + * 获取图片验证码对象 + * + * @return 验证码对象 + */ + public Captcha getCaptcha() { + Captcha captcha = ReflectUtil.newInstance(type.getClazz(), this.width, this.height); + captcha.setLen(length); + if (StrUtil.isNotBlank(this.fontName)) { + captcha.setFont(new Font(this.fontName, Font.PLAIN, this.fontSize)); + } + return captcha; } - return captcha; } /** - * 验证码类型枚举 + * 邮箱验证码配置 + */ + @Data + public static class CaptchaMail { + /** + * 内容长度 + */ + private int length; + + /** + * 过期时间 + */ + private long expirationInMinutes; + + /** + * 限制时间 + */ + private long limitInSeconds; + + /** + * 模板路径 + */ + private String templatePath; + } + + /** + * 图片验证码类型枚举 */ @Getter @RequiredArgsConstructor - public enum CaptchaTypeEnum { + private enum CaptchaImageTypeEnum { /** * 算术 diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/consts/CacheConstants.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/consts/CacheConstants.java index fe47bc03..6583d9df 100644 --- a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/consts/CacheConstants.java +++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/consts/CacheConstants.java @@ -33,4 +33,14 @@ public class CacheConstants { */ public static final String LOGIN_USER_CACHE_KEY = "LOGIN_USER"; + /** + * 验证码缓存键 + */ + public static final String CAPTCHA_CACHE_KEY = "CAPTCHA"; + + /** + * 限流缓存键 + */ + public static final String LIMIT_CACHE_KEY = "LIMIT"; + } diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/handler/GlobalExceptionHandler.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/handler/GlobalExceptionHandler.java index ac647e5d..b2b7a103 100644 --- a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/handler/GlobalExceptionHandler.java +++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/handler/GlobalExceptionHandler.java @@ -162,8 +162,8 @@ public class GlobalExceptionHandler { @ResponseStatus(HttpStatus.UNAUTHORIZED) @ExceptionHandler(NotLoginException.class) public R handleNotLoginException(NotLoginException e, HttpServletRequest request) { - log.error("请求地址'{}',认证失败'{}',无法访问系统资源", request.getRequestURI(), e.getMessage()); - return R.fail(HttpStatus.UNAUTHORIZED.value(), "认证失败,无法访问系统资源"); + log.error("请求地址'{}',认证失败,无法访问系统资源", request.getRequestURI(), e); + return R.fail(HttpStatus.UNAUTHORIZED.value(), "登录状态已过期,请重新登录"); } /** diff --git a/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/model/vo/CaptchaVO.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/model/vo/CaptchaVO.java similarity index 96% rename from continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/model/vo/CaptchaVO.java rename to continew-admin-common/src/main/java/top/charles7c/cnadmin/common/model/vo/CaptchaVO.java index ac406707..7b80583c 100644 --- a/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/model/vo/CaptchaVO.java +++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/model/vo/CaptchaVO.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package top.charles7c.cnadmin.auth.model.vo; +package top.charles7c.cnadmin.common.model.vo; import java.io.Serializable; diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/MailUtils.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/MailUtils.java new file mode 100644 index 00000000..50de3499 --- /dev/null +++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/MailUtils.java @@ -0,0 +1,244 @@ +/* + * 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.charles7c.cnadmin.common.util; + +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.util.Collection; +import java.util.List; + +import javax.mail.MessagingException; +import javax.mail.internet.MimeMessage; + +import lombok.AccessLevel; +import lombok.Data; +import lombok.NoArgsConstructor; + +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.mail.javamail.MimeMessageHelper; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.CharUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.spring.SpringUtil; + +import top.charles7c.cnadmin.common.util.validate.CheckUtils; + +/** + * 邮件工具类 + * + * @author Charles7c + * @since 2023/1/12 23:25 + */ +@Data +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class MailUtils { + + private static final JavaMailSender MAIL_SENDER = SpringUtil.getBean(JavaMailSender.class); + + /** + * 发送文本邮件给单个人 + * + * @param subject + * 主题 + * @param content + * 内容 + * @param to + * 收件人 + * @throws MessagingException + * / + */ + public static void sendText(String to, String subject, String content) throws MessagingException { + send(splitAddress(to), null, null, subject, content, false); + } + + /** + * 发送 HTML 邮件给单个人 + * + * @param subject + * 主题 + * @param content + * 内容 + * @param to + * 收件人 + * @throws MessagingException + * / + */ + public static void sendHtml(String to, String subject, String content) throws MessagingException { + send(splitAddress(to), null, null, subject, content, true); + } + + /** + * 发送 HTML 邮件给单个人 + * + * @param subject + * 主题 + * @param content + * 内容 + * @param to + * 收件人 + * @param files + * 附件列表 + * @throws MessagingException + * / + */ + public static void sendHtml(String to, String subject, String content, File... files) throws MessagingException { + send(splitAddress(to), null, null, subject, content, true, files); + } + + /** + * 发送 HTML 邮件给多个人 + * + * @param subject + * 主题 + * @param content + * 内容 + * @param tos + * 收件人列表 + * @param files + * 附件列表 + * @throws MessagingException + * / + */ + public static void sendHtml(Collection tos, String subject, String content, File... files) + throws MessagingException { + send(tos, null, null, subject, content, true, files); + } + + /** + * 发送 HTML 邮件给多个人 + * + * @param subject + * 主题 + * @param content + * 内容 + * @param tos + * 收件人列表 + * @param ccs + * 抄送人列表 + * @param files + * 附件列表 + * @throws MessagingException + * / + */ + public static void sendHtml(Collection tos, Collection ccs, String subject, String content, + File... files) throws MessagingException { + send(tos, ccs, null, subject, content, true, files); + } + + /** + * 发送 HTML 邮件给多个人 + * + * @param subject + * 主题 + * @param content + * 内容 + * @param tos + * 收件人列表 + * @param ccs + * 抄送人列表 + * @param bccs + * 密送人列表 + * @param files + * 附件列表 + * @throws MessagingException + * / + */ + public static void sendHtml(Collection tos, Collection ccs, Collection bccs, String subject, + String content, File... files) throws MessagingException { + send(tos, ccs, bccs, subject, content, true, files); + } + + /** + * 发送邮件给多个人 + * + * @param tos + * 收件人列表 + * @param ccs + * 抄送人列表 + * @param bccs + * 密送人列表 + * @param subject + * 主题 + * @param content + * 内容 + * @param isHtml + * 是否是 HTML + * @param files + * 附件列表 + * @throws MessagingException + * / + */ + public static void send(Collection tos, Collection ccs, Collection bccs, String subject, + String content, boolean isHtml, File... files) throws MessagingException { + CheckUtils.exIfCondition(() -> CollUtil.isEmpty(tos), "请至少指定一名收件人"); + MimeMessage mimeMessage = MAIL_SENDER.createMimeMessage(); + MimeMessageHelper messageHelper = + new MimeMessageHelper(mimeMessage, true, StandardCharsets.UTF_8.displayName()); + + // 设置基本信息 + messageHelper.setFrom(SpringUtil.getProperty("spring.mail.username")); + messageHelper.setSubject(subject); + messageHelper.setText(content, isHtml); + + // 设置收信人 + // 抄送人 + if (CollUtil.isNotEmpty(ccs)) { + messageHelper.setCc(ccs.toArray(new String[0])); + } + // 密送人 + if (CollUtil.isNotEmpty(bccs)) { + messageHelper.setBcc(bccs.toArray(new String[0])); + } + // 收件人 + messageHelper.setTo(tos.toArray(new String[0])); + + // 设置附件 + if (ArrayUtil.isNotEmpty(files)) { + for (File file : files) { + messageHelper.addAttachment(file.getName(), file); + } + } + + // 发送邮件 + MAIL_SENDER.send(mimeMessage); + } + + /** + * 将多个联系人转为列表,分隔符为逗号或者分号 + * + * @param addresses + * 多个联系人,如果为空返回null + * @return 联系人列表 + */ + private static List splitAddress(String addresses) { + if (StrUtil.isBlank(addresses)) { + return null; + } + + List result; + if (StrUtil.contains(addresses, CharUtil.COMMA)) { + result = StrUtil.splitTrim(addresses, CharUtil.COMMA); + } else if (StrUtil.contains(addresses, ';')) { + result = StrUtil.splitTrim(addresses, ';'); + } else { + result = CollUtil.newArrayList(addresses); + } + return result; + } +} diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/TemplateUtils.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/TemplateUtils.java new file mode 100644 index 00000000..5197fee9 --- /dev/null +++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/TemplateUtils.java @@ -0,0 +1,53 @@ +/* + * 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.charles7c.cnadmin.common.util; + +import java.util.Map; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import cn.hutool.extra.template.Template; +import cn.hutool.extra.template.TemplateConfig; +import cn.hutool.extra.template.TemplateEngine; +import cn.hutool.extra.template.TemplateUtil; + +/** + * 模板工具类 + * + * @author Charles7c + * @since 2023/1/13 20:37 + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class TemplateUtils { + + private static final String TEMPLATE_PARENT_PATH = "templates"; + + /** + * 将模板与绑定参数融合后返回为字符串 + * + * @param bindingMap + * 绑定的参数,此Map中的参数会替换模板中的变量 + * @return 融合后的内容 + */ + public static String render(String templatePath, Map bindingMap) { + TemplateEngine engine = + TemplateUtil.createEngine(new TemplateConfig(TEMPLATE_PARENT_PATH, TemplateConfig.ResourceMode.CLASSPATH)); + Template template = engine.getTemplate(templatePath); + return template.render(bindingMap); + } +} diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/validate/CheckUtils.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/validate/CheckUtils.java index a94b6ad9..02cfb549 100644 --- a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/validate/CheckUtils.java +++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/validate/CheckUtils.java @@ -35,18 +35,6 @@ public class CheckUtils extends Validator { private static final Class EXCEPTION_TYPE = ServiceException.class; - /** - * 如果为空,抛出异常 - * - * @param obj - * 被检测的对象 - * @param message - * 错误信息 - */ - public static void exIfNull(Object obj, String message) { - exIfNull(obj, message, EXCEPTION_TYPE); - } - /** * 如果为空,抛出异常 * @@ -59,6 +47,18 @@ public class CheckUtils extends Validator { exIfBlank(str, message, EXCEPTION_TYPE); } + /** + * 如果不为空,抛出异常 + * + * @param str + * 被检测的字符串 + * @param message + * 错误信息 + */ + public static void exIfNotBlank(CharSequence str, String message) { + exIfNotBlank(str, message, EXCEPTION_TYPE); + } + /** * 如果相同,抛出异常 * @@ -87,6 +87,58 @@ public class CheckUtils extends Validator { exIfNotEqual(obj1, obj2, message, EXCEPTION_TYPE); } + /** + * 如果相同,抛出异常(不区分大小写) + * + * @param str1 + * 要比较的字符串1 + * @param str2 + * 要比较的字符串2 + * @param message + * 错误信息 + */ + public static void exIfEqualIgnoreCase(CharSequence str1, CharSequence str2, String message) { + exIfEqualIgnoreCase(str1, str2, message, EXCEPTION_TYPE); + } + + /** + * 如果不相同,抛出异常(不区分大小写) + * + * @param str1 + * 要比较的字符串1 + * @param str2 + * 要比较的字符串2 + * @param message + * 错误信息 + */ + public static void exIfNotEqualIgnoreCase(CharSequence str1, CharSequence str2, String message) { + exIfNotEqualIgnoreCase(str1, str2, message, EXCEPTION_TYPE); + } + + /** + * 如果为空,抛出异常 + * + * @param obj + * 被检测的对象 + * @param message + * 错误信息 + */ + public static void exIfNull(Object obj, String message) { + exIfNull(obj, message, EXCEPTION_TYPE); + } + + /** + * 如果不为空,抛出异常 + * + * @param obj + * 被检测的对象 + * @param message + * 错误信息 + */ + public static void exIfNotNull(Object obj, String message) { + exIfNotNull(obj, message, EXCEPTION_TYPE); + } + /** * 如果条件成立,抛出异常 * diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/validate/ValidationUtils.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/validate/ValidationUtils.java index 9451405c..7f26d3c4 100644 --- a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/validate/ValidationUtils.java +++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/validate/ValidationUtils.java @@ -35,18 +35,6 @@ public class ValidationUtils extends Validator { private static final Class EXCEPTION_TYPE = BadRequestException.class; - /** - * 如果为空,抛出异常 - * - * @param obj - * 被检测的对象 - * @param message - * 错误信息 - */ - public static void exIfNull(Object obj, String message) { - exIfNull(obj, message, EXCEPTION_TYPE); - } - /** * 如果为空,抛出异常 * @@ -59,6 +47,18 @@ public class ValidationUtils extends Validator { exIfBlank(str, message, EXCEPTION_TYPE); } + /** + * 如果不为空,抛出异常 + * + * @param str + * 被检测的字符串 + * @param message + * 错误信息 + */ + public static void exIfNotBlank(CharSequence str, String message) { + exIfNotBlank(str, message, EXCEPTION_TYPE); + } + /** * 如果相同,抛出异常 * @@ -87,6 +87,58 @@ public class ValidationUtils extends Validator { exIfNotEqual(obj1, obj2, message, EXCEPTION_TYPE); } + /** + * 如果相同,抛出异常(不区分大小写) + * + * @param str1 + * 要比较的字符串1 + * @param str2 + * 要比较的字符串2 + * @param message + * 错误信息 + */ + public static void exIfEqualIgnoreCase(CharSequence str1, CharSequence str2, String message) { + exIfEqualIgnoreCase(str1, str2, message, EXCEPTION_TYPE); + } + + /** + * 如果不相同,抛出异常(不区分大小写) + * + * @param str1 + * 要比较的字符串1 + * @param str2 + * 要比较的字符串2 + * @param message + * 错误信息 + */ + public static void exIfNotEqualIgnoreCase(CharSequence str1, CharSequence str2, String message) { + exIfNotEqualIgnoreCase(str1, str2, message, EXCEPTION_TYPE); + } + + /** + * 如果为空,抛出异常 + * + * @param obj + * 被检测的对象 + * @param message + * 错误信息 + */ + public static void exIfNull(Object obj, String message) { + exIfNull(obj, message, EXCEPTION_TYPE); + } + + /** + * 如果不为空,抛出异常 + * + * @param obj + * 被检测的对象 + * @param message + * 错误信息 + */ + public static void exIfNotNull(Object obj, String message) { + exIfNotNull(obj, message, EXCEPTION_TYPE); + } + /** * 如果条件成立,抛出异常 * diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/validate/Validator.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/validate/Validator.java index 613cec33..6ee21521 100644 --- a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/validate/Validator.java +++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/validate/Validator.java @@ -25,6 +25,8 @@ import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.StrUtil; /** + * 校验器 + * * @author Charles7c * @since 2023/1/2 22:12 */ @@ -32,23 +34,6 @@ import cn.hutool.core.util.StrUtil; @NoArgsConstructor(access = AccessLevel.PROTECTED) public class Validator { - /** - * 如果为空,抛出异常 - * - * @param obj - * 被检测的对象 - * @param message - * 错误信息 - * @param exceptionType - * 异常类型 - */ - protected static void exIfNull(Object obj, String message, Class exceptionType) { - if (obj == null) { - log.error(message); - throw ReflectUtil.newInstance(exceptionType, message); - } - } - /** * 如果为空,抛出异常 * @@ -59,11 +44,23 @@ public class Validator { * @param exceptionType * 异常类型 */ - public static void exIfBlank(CharSequence str, String message, Class exceptionType) { - if (StrUtil.isBlank(str)) { - log.error(message); - throw ReflectUtil.newInstance(exceptionType, message); - } + protected static void exIfBlank(CharSequence str, String message, Class exceptionType) { + exIfCondition(() -> StrUtil.isBlank(str), message, exceptionType); + } + + /** + * 如果不为空,抛出异常 + * + * @param str + * 被检测的字符串 + * @param message + * 错误信息 + * @param exceptionType + * 异常类型 + */ + protected static void exIfNotBlank(CharSequence str, String message, + Class exceptionType) { + exIfCondition(() -> StrUtil.isNotBlank(str), message, exceptionType); } /** @@ -78,12 +75,9 @@ public class Validator { * @param exceptionType * 异常类型 */ - public static void exIfEqual(Object obj1, Object obj2, String message, + protected static void exIfEqual(Object obj1, Object obj2, String message, Class exceptionType) { - if (ObjectUtil.equals(obj1, obj2)) { - log.error(message); - throw ReflectUtil.newInstance(exceptionType, message); - } + exIfCondition(() -> ObjectUtil.equal(obj1, obj2), message, exceptionType); } /** @@ -98,12 +92,71 @@ public class Validator { * @param exceptionType * 异常类型 */ - public static void exIfNotEqual(Object obj1, Object obj2, String message, + protected static void exIfNotEqual(Object obj1, Object obj2, String message, Class exceptionType) { - if (ObjectUtil.notEqual(obj1, obj2)) { - log.error(message); - throw ReflectUtil.newInstance(exceptionType, message); - } + exIfCondition(() -> ObjectUtil.notEqual(obj1, obj2), message, exceptionType); + } + + /** + * 如果相同,抛出异常(不区分大小写) + * + * @param str1 + * 要比较的字符串1 + * @param str2 + * 要比较的字符串2 + * @param message + * 错误信息 + * @param exceptionType + * 异常类型 + */ + protected static void exIfEqualIgnoreCase(CharSequence str1, CharSequence str2, String message, + Class exceptionType) { + exIfCondition(() -> StrUtil.equalsIgnoreCase(str1, str2), message, exceptionType); + } + + /** + * 如果不相同,抛出异常(不区分大小写) + * + * @param str1 + * 要比较的字符串1 + * @param str2 + * 要比较的字符串2 + * @param message + * 错误信息 + * @param exceptionType + * 异常类型 + */ + protected static void exIfNotEqualIgnoreCase(CharSequence str1, CharSequence str2, String message, + Class exceptionType) { + exIfCondition(() -> !StrUtil.equalsIgnoreCase(str1, str2), message, exceptionType); + } + + /** + * 如果为空,抛出异常 + * + * @param obj + * 被检测的对象 + * @param message + * 错误信息 + * @param exceptionType + * 异常类型 + */ + protected static void exIfNull(Object obj, String message, Class exceptionType) { + exIfCondition(() -> obj == null, message, exceptionType); + } + + /** + * 如果不为空,抛出异常 + * + * @param obj + * 被检测的对象 + * @param message + * 错误信息 + * @param exceptionType + * 异常类型 + */ + protected static void exIfNotNull(Object obj, String message, Class exceptionType) { + exIfCondition(() -> obj != null, message, exceptionType); } /** @@ -116,7 +169,7 @@ public class Validator { * @param exceptionType * 异常类型 */ - public static void exIfCondition(java.util.function.BooleanSupplier conditionSupplier, String message, + protected static void exIfCondition(java.util.function.BooleanSupplier conditionSupplier, String message, Class exceptionType) { if (conditionSupplier != null && conditionSupplier.getAsBoolean()) { log.error(message); diff --git a/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/interceptor/LogInterceptor.java b/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/interceptor/LogInterceptor.java index 7aaec9f2..d777f27b 100644 --- a/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/interceptor/LogInterceptor.java +++ b/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/interceptor/LogInterceptor.java @@ -28,6 +28,7 @@ import lombok.extern.slf4j.Slf4j; import io.swagger.v3.oas.annotations.Operation; import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.lang.NonNull; import org.springframework.stereotype.Component; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerInterceptor; @@ -65,20 +66,21 @@ import top.charles7c.cnadmin.monitor.model.entity.SysLog; public class LogInterceptor implements HandlerInterceptor { private final LogProperties operationLogProperties; + private static final String ENCRYPT_SYMBOL = "****************"; @Override - public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { - if (!checkIsNeedRecord(handler, request)) { - return true; + public boolean preHandle(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, + @NonNull Object handler) { + if (checkIsNeedRecord(handler, request)) { + // 记录操作时间 + this.logCreateTime(); } - - // 记录操作时间 - this.logCreateTime(); return true; } @Override - public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception e) { + public void afterCompletion(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, + @NonNull Object handler, Exception e) { // 记录请求耗时及异常信息 SysLog sysLog = this.logElapsedTimeAndException(); if (sysLog == null) { @@ -203,6 +205,7 @@ public class LogInterceptor implements HandlerInterceptor { * 待脱敏数据 * @return 脱敏后的 JSON 字符串数据 */ + @SuppressWarnings("unchecked") private String desensitize(Map waitDesensitizeData) { String desensitizeDataStr = JSONUtil.toJsonStr(waitDesensitizeData); try { @@ -211,9 +214,9 @@ public class LogInterceptor implements HandlerInterceptor { } for (String desensitizeProperty : operationLogProperties.getDesensitize()) { - waitDesensitizeData.computeIfPresent(desensitizeProperty, (k, v) -> "****************"); - waitDesensitizeData.computeIfPresent(desensitizeProperty.toLowerCase(), (k, v) -> "****************"); - waitDesensitizeData.computeIfPresent(desensitizeProperty.toUpperCase(), (k, v) -> "****************"); + waitDesensitizeData.computeIfPresent(desensitizeProperty, (k, v) -> ENCRYPT_SYMBOL); + waitDesensitizeData.computeIfPresent(desensitizeProperty.toLowerCase(), (k, v) -> ENCRYPT_SYMBOL); + waitDesensitizeData.computeIfPresent(desensitizeProperty.toUpperCase(), (k, v) -> ENCRYPT_SYMBOL); } return JSONUtil.toJsonStr(waitDesensitizeData); } catch (Exception ignored) { diff --git a/continew-admin-system/pom.xml b/continew-admin-system/pom.xml index 116d21d4..f33e7e55 100644 --- a/continew-admin-system/pom.xml +++ b/continew-admin-system/pom.xml @@ -32,12 +32,6 @@ limitations under the License. 系统管理模块(存放系统管理模块相关功能,例如:部门管理、角色管理、用户管理等) - - - com.github.whvcse - easy-captcha - - top.charles7c diff --git a/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/service/impl/LoginServiceImpl.java b/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/service/impl/LoginServiceImpl.java index 21d14d40..e3211a46 100644 --- a/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/service/impl/LoginServiceImpl.java +++ b/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/service/impl/LoginServiceImpl.java @@ -52,7 +52,7 @@ public class LoginServiceImpl implements LoginService { // 校验 ValidationUtils.exIfNull(sysUser, "用户名或密码错误"); Long userId = sysUser.getUserId(); - ValidationUtils.exIfNotEqual(sysUser.getPassword(), SecureUtils.md5Salt(password, userId.toString()), + ValidationUtils.exIfNotEqual(SecureUtils.md5Salt(password, userId.toString()), sysUser.getPassword(), "用户名或密码错误"); ValidationUtils.exIfEqual(DisEnableStatusEnum.DISABLE, sysUser.getStatus(), "此账号已被禁用,如有疑问,请联系管理员"); diff --git a/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/model/request/UpdateBasicInfoRequest.java b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/model/request/UpdateBasicInfoRequest.java index 9bb5569e..d746b828 100644 --- a/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/model/request/UpdateBasicInfoRequest.java +++ b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/model/request/UpdateBasicInfoRequest.java @@ -52,6 +52,6 @@ public class UpdateBasicInfoRequest implements Serializable { * 性别(0未知 1男 2女) */ @Schema(description = "性别(0未知 1男 2女)", type = "Integer", allowableValues = {"0", "1", "2"}) - @NotNull(message = "非法性别") + @NotNull(message = "性别非法") private GenderEnum gender; } diff --git a/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/model/request/UpdateEmailRequest.java b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/model/request/UpdateEmailRequest.java new file mode 100644 index 00000000..ee0b1132 --- /dev/null +++ b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/model/request/UpdateEmailRequest.java @@ -0,0 +1,66 @@ +/* + * 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.charles7c.cnadmin.system.model.request; + +import java.io.Serializable; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Pattern; + +import lombok.Data; + +import io.swagger.v3.oas.annotations.media.Schema; + +import org.hibernate.validator.constraints.Length; + +import cn.hutool.core.lang.RegexPool; + +/** + * 修改邮箱信息 + * + * @author Charles7c + * @since 2023/1/12 20:18 + */ +@Data +@Schema(description = "修改邮箱信息") +public class UpdateEmailRequest implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 新邮箱 + */ + @Schema(description = "新邮箱") + @NotBlank(message = "新邮箱不能为空") + @Pattern(regexp = RegexPool.EMAIL, message = "邮箱格式错误") + private String newEmail; + + /** + * 验证码 + */ + @Schema(description = "验证码") + @NotBlank(message = "验证码不能为空") + @Length(max = 6, message = "验证码非法") + private String captcha; + + /** + * 当前密码(加密后) + */ + @Schema(description = "当前密码(加密后)") + @NotBlank(message = "当前密码不能为空") + private String currentPassword; +} diff --git a/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/UserService.java b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/UserService.java index ce4afa9c..35062b6a 100644 --- a/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/UserService.java +++ b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/UserService.java @@ -67,4 +67,16 @@ public interface UserService { * 用户 ID */ void updatePassword(String oldPassword, String newPassword, Long userId); + + /** + * 修改邮箱 + * + * @param newEmail + * 新邮箱 + * @param currentPassword + * 当前密码 + * @param userId + * 用户ID + */ + void updateEmail(String newEmail, String currentPassword, Long userId); } diff --git a/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/impl/UserServiceImpl.java b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/impl/UserServiceImpl.java index 5aabfca5..1600a032 100644 --- a/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/impl/UserServiceImpl.java +++ b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/impl/UserServiceImpl.java @@ -104,7 +104,7 @@ public class UserServiceImpl implements UserService { @Transactional(rollbackFor = Exception.class) public void updatePassword(String oldPassword, String newPassword, Long userId) { SysUser sysUser = this.getById(userId); - ValidationUtils.exIfNotEqual(sysUser.getPassword(), SecureUtils.md5Salt(oldPassword, userId.toString()), + ValidationUtils.exIfNotEqual(SecureUtils.md5Salt(oldPassword, userId.toString()), sysUser.getPassword(), "当前密码错误"); // 更新密码和密码重置时间 @@ -120,6 +120,27 @@ public class UserServiceImpl implements UserService { LoginHelper.updateLoginUser(loginUser); } + @Override + @Transactional(rollbackFor = Exception.class) + public void updateEmail(String newEmail, String currentPassword, Long userId) { + // 校验 + SysUser sysUser = this.getById(userId); + ValidationUtils.exIfNotEqual(SecureUtils.md5Salt(currentPassword, userId.toString()), sysUser.getPassword(), + "当前密码错误"); + Long count = userMapper.selectCount(Wrappers.lambdaQuery().eq(SysUser::getEmail, newEmail)); + ValidationUtils.exIfCondition(() -> count > 0, "邮箱已绑定其他账号,请更换其他邮箱"); + ValidationUtils.exIfEqual(newEmail, sysUser.getEmail(), "新邮箱不能与当前邮箱相同"); + + // 更新邮箱 + userMapper.update(null, + new LambdaUpdateWrapper().set(SysUser::getEmail, newEmail).eq(SysUser::getUserId, userId)); + + // 更新登录用户信息 + LoginUser loginUser = LoginHelper.getLoginUser(); + loginUser.setEmail(newEmail); + LoginHelper.updateLoginUser(loginUser); + } + /** * 根据 ID 查询 * diff --git a/continew-admin-ui/package.json b/continew-admin-ui/package.json index b4d5de7f..76dec468 100644 --- a/continew-admin-ui/package.json +++ b/continew-admin-ui/package.json @@ -1,6 +1,6 @@ { "name": "continew-admin-ui", - "description": "ContiNew-Admin (incubating) 中后台管理框架,Continue New Admin,持续以最新流行技术栈构建。", + "description": "ContiNew Admin 中后台管理框架(孵化中),Continue New Admin,持续以最新流行技术栈构建。", "version": "0.0.1-SNAPSHOT", "private": true, "author": "Charles7c", diff --git a/continew-admin-ui/src/api/auth/login.ts b/continew-admin-ui/src/api/auth/login.ts index 86e2cc05..4a9533d8 100644 --- a/continew-admin-ui/src/api/auth/login.ts +++ b/continew-admin-ui/src/api/auth/login.ts @@ -2,14 +2,6 @@ import axios from 'axios'; import type { RouteRecordNormalized } from 'vue-router'; import { UserState } from '@/store/modules/login/types'; -export interface ImageCaptchaRes { - uuid: string; - img: string; -} -export function getImageCaptcha() { - return axios.get('/captcha/img'); -} - export interface LoginReq { username: string; password: string; diff --git a/continew-admin-ui/src/api/common/captcha.ts b/continew-admin-ui/src/api/common/captcha.ts new file mode 100644 index 00000000..5ae9c560 --- /dev/null +++ b/continew-admin-ui/src/api/common/captcha.ts @@ -0,0 +1,22 @@ +import axios from 'axios'; +import qs from 'query-string'; + +export interface ImageCaptchaRes { + uuid: string; + img: string; +} +export function getImageCaptcha() { + return axios.get('/common/captcha/img'); +} + +export interface MailCaptchaReq { + email: string; +} +export function getMailCaptcha(params: MailCaptchaReq) { + return axios.get('/common/captcha/mail', { + params, + paramsSerializer: (obj) => { + return qs.stringify(obj); + }, + }); +} diff --git a/continew-admin-ui/src/api/system/user-center.ts b/continew-admin-ui/src/api/system/user-center.ts index 4e68ddf9..e4af5473 100644 --- a/continew-admin-ui/src/api/system/user-center.ts +++ b/continew-admin-ui/src/api/system/user-center.ts @@ -27,4 +27,13 @@ export interface UpdatePasswordReq { } export function updatePassword(req: UpdatePasswordReq) { return axios.patch('/system/user/center/password', req); +} + +export interface UpdateEmailReq { + newEmail: string; + captcha: string; + currentPassword: string; +} +export function updateEmail(req: UpdateEmailReq) { + return axios.patch('/system/user/center/email', req); } \ No newline at end of file diff --git a/continew-admin-ui/src/components/footer/index.vue b/continew-admin-ui/src/components/footer/index.vue index 02a70984..215e8aff 100644 --- a/continew-admin-ui/src/components/footer/index.vue +++ b/continew-admin-ui/src/components/footer/index.vue @@ -2,6 +2,8 @@ {{ `Copyright © 2022-${new Date().getFullYear()} Charles7c` }}  ⋅  + {{ $t('title') }} +  ⋅  津ICP备2022005864号-2 diff --git a/continew-admin-ui/src/components/navbar/index.vue b/continew-admin-ui/src/components/navbar/index.vue index 7077f234..120385fb 100644 --- a/continew-admin-ui/src/components/navbar/index.vue +++ b/continew-admin-ui/src/components/navbar/index.vue @@ -190,7 +190,7 @@ import useLocale from '@/hooks/locale'; import useUser from '@/hooks/user'; import Menu from '@/components/menu/index.vue'; - import getAvatar from "@/utils/avatar"; + import getAvatar from '@/utils/avatar'; import MessageBox from '../message-box/index.vue'; const appStore = useAppStore(); diff --git a/continew-admin-ui/src/hooks/axios.d.ts b/continew-admin-ui/src/hooks/axios.d.ts index 0a982b49..64e4b08a 100644 --- a/continew-admin-ui/src/hooks/axios.d.ts +++ b/continew-admin-ui/src/hooks/axios.d.ts @@ -1,4 +1,4 @@ -import axios, { Axios, AxiosResponse, AxiosRequestConfig } from "axios"; +import axios, { Axios, AxiosResponse, AxiosRequestConfig } from 'axios'; declare module "axios" { interface AxiosResponse { diff --git a/continew-admin-ui/src/hooks/user.ts b/continew-admin-ui/src/hooks/user.ts index 84fa75f8..11939604 100644 --- a/continew-admin-ui/src/hooks/user.ts +++ b/continew-admin-ui/src/hooks/user.ts @@ -1,5 +1,5 @@ import { useRouter } from 'vue-router'; -import { useI18n } from "vue-i18n"; +import { useI18n } from 'vue-i18n'; import { Message } from '@arco-design/web-vue'; import { useLoginStore } from '@/store'; diff --git a/continew-admin-ui/src/store/modules/login/index.ts b/continew-admin-ui/src/store/modules/login/index.ts index 30ffd0fb..41c77d0c 100644 --- a/continew-admin-ui/src/store/modules/login/index.ts +++ b/continew-admin-ui/src/store/modules/login/index.ts @@ -1,11 +1,11 @@ import { defineStore } from 'pinia'; import { - getImageCaptcha as getCaptcha, login as userLogin, logout as userLogout, getUserInfo, LoginReq, } from '@/api/auth/login'; +import { getImageCaptcha as getCaptcha } from '@/api/common/captcha'; import { setToken, clearToken } from '@/utils/auth'; import { removeRouteListener } from '@/utils/route-listener'; import { UserState } from './types'; diff --git a/continew-admin-ui/src/views/dashboard/monitor/components/studio.vue b/continew-admin-ui/src/views/dashboard/monitor/components/studio.vue index a356bdcc..31f683c1 100644 --- a/continew-admin-ui/src/views/dashboard/monitor/components/studio.vue +++ b/continew-admin-ui/src/views/dashboard/monitor/components/studio.vue @@ -29,7 +29,7 @@ diff --git a/continew-admin-ui/src/views/login/components/login-form.vue b/continew-admin-ui/src/views/login/components/login-form.vue index 632f7a6e..ab1899be 100644 --- a/continew-admin-ui/src/views/login/components/login-form.vue +++ b/continew-admin-ui/src/views/login/components/login-form.vue @@ -37,7 +37,7 @@ :placeholder="$t('login.form.placeholder.password')" size="large" allow-clear - max-length="50" + max-length="32" > - + diff --git a/continew-admin-ui/src/views/system/user/center/components/security-settings/update-pwd.vue b/continew-admin-ui/src/views/system/user/center/components/security-settings/update-pwd.vue index 1ac27dbe..61bf017b 100644 --- a/continew-admin-ui/src/views/system/user/center/components/security-settings/update-pwd.vue +++ b/continew-admin-ui/src/views/system/user/center/components/security-settings/update-pwd.vue @@ -22,12 +22,14 @@ - - + + @@ -52,7 +54,7 @@ :placeholder="$t('userCenter.securitySettings.updatePwd.form.placeholder.newPassword')" size="large" allow-clear - max-length="50" + max-length="32" > @@ -66,7 +68,7 @@ :placeholder="$t('userCenter.securitySettings.updatePwd.form.placeholder.rePassword')" size="large" allow-clear - max-length="50" + max-length="32" > @@ -75,14 +77,14 @@