From 85c60873302df05faf323f8b22685d61f37ba58f Mon Sep 17 00:00:00 2001 From: zhuoda Date: Tue, 3 Sep 2024 22:36:38 +0800 Subject: [PATCH] =?UTF-8?q?v3.6.0=20=E4=B8=89=E7=BA=A7=E7=AD=89=E4=BF=9D?= =?UTF-8?q?=E9=87=8D=E7=A3=85=E6=9B=B4=E6=96=B0=EF=BC=9A1=E3=80=81?= =?UTF-8?q?=E3=80=90=E6=96=B0=E5=A2=9E=E3=80=91=E5=8F=8C=E5=9B=A0=E5=AD=90?= =?UTF-8?q?=E6=96=B9=E5=BC=8F=E7=99=BB=E5=BD=95=EF=BC=9B2=E3=80=81?= =?UTF-8?q?=E3=80=90=E6=96=B0=E5=A2=9E=E3=80=91=E5=AE=9A=E6=9C=9F=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E5=AF=86=E7=A0=81;3=E3=80=81=E3=80=90=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91=E6=9C=80=E5=A4=A7=E6=B4=BB=E8=B7=83=E6=97=B6?= =?UTF-8?q?=E9=97=B4;4=E3=80=81=E3=80=90=E6=96=B0=E5=A2=9E=E3=80=91?= =?UTF-8?q?=E6=95=8F=E6=84=9F=E6=95=B0=E6=8D=AE=E8=84=B1=E6=95=8F;5?= =?UTF-8?q?=E3=80=81=E3=80=90=E6=96=B0=E5=A2=9E=E3=80=91=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E9=94=81=E5=AE=9A=E9=85=8D=E7=BD=AE;6=E3=80=81=E3=80=90?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E3=80=91=E5=AF=86=E7=A0=81=E5=A4=8D=E6=9D=82?= =?UTF-8?q?=E5=BA=A6=E9=85=8D=E7=BD=AE;7=E3=80=81=E3=80=90=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91=E4=B8=89=E7=BA=A7=E7=AD=89=E4=BF=9D=E5=8F=AF?= =?UTF-8?q?=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- smart-admin-api/pom.xml | 45 ++- .../admin/interceptor/AdminInterceptor.java | 8 - .../goods/domain/form/GoodsQueryForm.java | 3 + .../business/goods/service/GoodsService.java | 2 +- .../business/oa/notice/dao/NoticeDao.java | 10 + .../notice/service/NoticeEmployeeService.java | 11 +- .../manager/DepartmentCacheManager.java | 38 +- .../department/service/DepartmentService.java | 2 +- .../controller/EmployeeController.java | 17 +- .../system/employee/dao/EmployeeDao.java | 2 +- .../domain/entity/EmployeeEntity.java | 5 + .../employee/domain/form/EmployeeAddForm.java | 3 + .../form/EmployeeUpdatePasswordForm.java | 2 - .../system/employee/domain/vo/EmployeeVO.java | 3 + .../employee/manager/EmployeeManager.java | 10 +- .../employee/service/EmployeeService.java | 66 ++-- .../login/controller/LoginController.java | 26 +- .../module/system/login/domain/LoginForm.java | 3 + .../system/login/domain/LoginResultVO.java | 3 + .../system/login/service/LoginService.java | 163 +++++++- .../system/role/dao/RoleEmployeeDao.java | 10 +- .../domain/form/RoleEmployeeUpdateForm.java | 4 +- .../role/manager/RoleEmployeeManager.java | 17 +- .../role/service/RoleEmployeeService.java | 23 +- .../system/role/service/RoleService.java | 7 +- .../AdminDataMaskingDemoController.java | 88 +++++ .../support/AdminProtectController.java | 35 +- .../src/main/resources/dev/application.yaml | 25 +- .../mapper/business/goods/GoodsMapper.xml | 2 +- .../mapper/business/notice/NoticeMapper.xml | 69 ---- .../{NoticeDao.xml => NoticeMapper.xml} | 6 + .../mapper/system/employee/EmployeeMapper.xml | 2 +- .../mapper/system/role/RoleEmployeeMapper.xml | 8 + .../src/main/resources/pre/application.yaml | 25 +- .../src/main/resources/prod/application.yaml | 25 +- .../src/main/resources/test/application.yaml | 27 +- smart-admin-api/sa-base/pom.xml | 30 +- .../deserializer/DictValueVoDeserializer.java | 8 +- .../serializer/DataMaskingSerializer.java | 59 +++ .../sa/base/common/util/SmartPageUtil.java | 5 +- .../sa/base/config/DataSourceConfig.java | 4 + .../lab1024/sa/base/config/RedisConfig.java | 4 +- .../lab1024/sa/base/config/SwaggerConfig.java | 4 +- .../sa/base/constant/RedisKeyConst.java | 2 + .../sa/base/constant/SwaggerTagConst.java | 2 + .../base/handler/MybatisPlusFillHandler.java | 40 ++ .../support/captcha/CaptchaService.java | 4 +- .../constant/CodeFrontComponentEnum.java | 2 +- .../domain/form/CodeGeneratorConfigForm.java | 1 - .../model/CodeInsertAndUpdateField.java | 4 +- .../service/CodeGeneratorTemplateService.java | 16 +- .../CodeGenerateBaseVariableService.java | 45 +-- .../variable/backend/MenuVariableService.java | 27 ++ .../domain/AddFormVariableService.java | 4 +- .../backend/domain/EntityVariableService.java | 13 +- .../backend/domain/MapperVariableService.java | 65 ++-- .../domain/QueryFormVariableService.java | 24 +- .../domain/UpdateFormVariableService.java | 7 +- .../backend/domain/VOVariableService.java | 14 +- .../variable/front/FormVariableService.java | 9 +- .../variable/front/ListVariableService.java | 13 +- .../module/support/config/ConfigKeyEnum.java | 1 + .../module/support/config/ConfigService.java | 9 +- .../support/datamasking/DataMasking.java | 27 ++ .../datamasking/DataMaskingTypeEnum.java | 40 ++ .../datamasking/SmartDataMaskingUtil.java | 216 +++++++++++ .../support/file/service/FileService.java | 20 +- .../support/job/core/SmartJobExecutor.java | 2 + .../base/module/support/mail/MailService.java | 179 +++++++++ .../module/support/mail/MailTemplateDao.java | 22 ++ .../mail/constant/MailTemplateCodeEnum.java | 19 + .../mail/constant/MailTemplateTypeEnum.java | 30 ++ .../mail/domain/MailTemplateEntity.java | 51 +++ .../domain/OperateLogQueryForm.java | 6 + .../module/support/reload/ReloadCommand.java | 1 - .../securityprotect/dao/PasswordLogDao.java | 34 ++ .../domain/Level3ProtectConfigForm.java | 58 +++ .../domain/PasswordLogEntity.java | 43 +++ .../service/Level3ProtectConfigService.java | 207 ++++++++++ .../service/ProtectPasswordService.java | 99 ----- .../service/SecurityFileService.java | 52 +++ ...Service.java => SecurityLoginService.java} | 84 ++--- .../service/SecurityPasswordService.java | 149 ++++++++ .../java/constant/enum.java.vm | 1 + .../java/controller/Controller.java.vm | 8 +- .../java/dao/Dao.java.vm | 3 +- .../java/domain/entity/Entity.java.vm | 16 +- .../java/domain/form/QueryForm.java.vm | 5 +- .../java/domain/vo/VO.java.vm | 10 +- .../java/mapper/Mapper.xml.vm | 36 +- .../java/service/Service.java.vm | 4 +- .../java/sql/Menu.sql.vm | 22 ++ .../code-generator-template/js/form.vue.vm | 250 ++++++------ .../code-generator-template/js/list.vue.vm | 56 ++- .../src/main/resources/dev/sa-base.yaml | 26 +- .../mapper/support/OperateLogMapper.xml | 6 + .../mapper/support/PasswordLogMapper.xml | 28 ++ .../src/main/resources/pre/sa-base.yaml | 36 +- .../src/main/resources/prod/sa-base.yaml | 32 +- .../src/main/resources/test/sa-base.yaml | 31 +- .../.env.development | 2 +- .../javascript-ant-design-vue3/.env.test | 2 +- .../src/api/support/data-masking-api.js | 17 + .../src/api/support/level3-protect-api.js | 24 ++ .../src/api/system/employee-api.js | 14 +- .../src/api/system/login-api.js | 14 + .../components/framework/wangeditor/index.vue | 1 - .../support/dict-key-select/index.vue | 6 +- .../components/support/dict-select/index.vue | 37 +- .../support/table-operator/index.vue | 14 +- .../employee-table-select-modal/index.vue | 17 +- .../src/constants/local-storage-key-const.js | 4 +- .../src/constants/regular-const.js | 1 + .../regular-change-password-modal.vue | 47 +++ .../header-user-space/header-avatar.vue | 2 - .../header-message-detail-modal.vue | 47 +++ .../header-user-space/header-message.vue | 346 ++++++++++------- .../header-reset-password-modal/index.vue | 14 +- .../header-user-space/header-setting.vue | 4 +- .../components/header-user-space/index.vue | 18 - .../src/layout/components/page-tag/index.vue | 6 +- .../src/layout/index.vue | 15 +- .../src/lib/axios.js | 5 +- .../src/router/index.js | 16 +- .../src/store/modules/system/user.js | 31 +- .../src/utils/local-util.js | 4 + .../erp/goods/components/goods-form-modal.vue | 15 +- .../views/business/erp/goods/goods-list.vue | 2 +- .../code-generator/code-generator-util.js | 11 +- ...ode-generator-table-config-form-delete.vue | 5 +- ...code-generator-table-config-form-field.vue | 11 +- ...enerator-table-config-form-query-field.vue | 58 ++- .../form/code-generator-table-config-form.vue | 18 +- .../preview/code-generator-preview-modal.vue | 36 +- .../src/views/support/file/file-list.vue | 2 +- .../level3protect/data-masking-list.vue | 137 +++++++ .../level3-protect-config-index.vue | 254 +++++++++++++ .../support/operate-log/operate-log-list.vue | 10 +- .../account/components/center/index.vue | 3 + .../account/components/message/index.vue | 4 +- .../account/components/password/index.vue | 60 ++- .../components/employee-form-modal/index.vue | 4 + .../components/employee-list/index.vue | 16 +- .../src/views/system/home/ad-modal.vue | 92 +++++ .../home/components/default-home-card.vue | 1 - .../home/components/to-be-done-card.vue | 158 -------- .../to-be-done-card/home-to-be-done.vue | 177 +++++++++ .../to-be-done-card/to-be-done-modal.vue | 65 ++++ .../src/views/system/home/index.vue | 18 +- .../src/views/system/login/login.less | 8 +- .../src/views/system/login/login.vue | 85 ++++- .../src/views/system/login2/login.less | 7 +- .../src/views/system/login2/login.vue | 85 ++++- .../src/views/system/login3/login.less | 8 +- .../src/views/system/login3/login.vue | 83 +++- .../menu/components/menu-operate-modal.vue | 4 +- .../components/role-employee-list/index.vue | 2 +- .../src/api/support/file/file-api.js | 4 +- .../src/views/support/file/file-list.vue | 2 +- smart_admin_v3.sql | 355 +++++++++--------- 160 files changed, 4091 insertions(+), 1537 deletions(-) create mode 100644 smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/support/AdminDataMaskingDemoController.java delete mode 100644 smart-admin-api/sa-admin/src/main/resources/mapper/business/notice/NoticeMapper.xml rename smart-admin-api/sa-admin/src/main/resources/mapper/business/oa/notice/{NoticeDao.xml => NoticeMapper.xml} (97%) create mode 100644 smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/common/json/serializer/DataMaskingSerializer.java create mode 100644 smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/handler/MybatisPlusFillHandler.java create mode 100644 smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/service/variable/backend/MenuVariableService.java create mode 100644 smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/datamasking/DataMasking.java create mode 100644 smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/datamasking/DataMaskingTypeEnum.java create mode 100644 smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/datamasking/SmartDataMaskingUtil.java create mode 100644 smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/mail/MailService.java create mode 100644 smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/mail/MailTemplateDao.java create mode 100644 smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/mail/constant/MailTemplateCodeEnum.java create mode 100644 smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/mail/constant/MailTemplateTypeEnum.java create mode 100644 smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/mail/domain/MailTemplateEntity.java create mode 100644 smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/dao/PasswordLogDao.java create mode 100644 smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/domain/Level3ProtectConfigForm.java create mode 100644 smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/domain/PasswordLogEntity.java create mode 100644 smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/Level3ProtectConfigService.java delete mode 100644 smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/ProtectPasswordService.java create mode 100644 smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/SecurityFileService.java rename smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/{ProtectLoginService.java => SecurityLoginService.java} (63%) create mode 100644 smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/SecurityPasswordService.java create mode 100644 smart-admin-api/sa-base/src/main/resources/code-generator-template/java/sql/Menu.sql.vm create mode 100644 smart-admin-api/sa-base/src/main/resources/mapper/support/PasswordLogMapper.xml create mode 100644 smart-admin-web/javascript-ant-design-vue3/src/api/support/data-masking-api.js create mode 100644 smart-admin-web/javascript-ant-design-vue3/src/api/support/level3-protect-api.js create mode 100644 smart-admin-web/javascript-ant-design-vue3/src/layout/components/change-password/regular-change-password-modal.vue create mode 100644 smart-admin-web/javascript-ant-design-vue3/src/layout/components/header-user-space/header-message-detail-modal.vue create mode 100644 smart-admin-web/javascript-ant-design-vue3/src/views/support/level3protect/data-masking-list.vue create mode 100644 smart-admin-web/javascript-ant-design-vue3/src/views/support/level3protect/level3-protect-config-index.vue create mode 100644 smart-admin-web/javascript-ant-design-vue3/src/views/system/home/ad-modal.vue delete mode 100644 smart-admin-web/javascript-ant-design-vue3/src/views/system/home/components/to-be-done-card.vue create mode 100644 smart-admin-web/javascript-ant-design-vue3/src/views/system/home/components/to-be-done-card/home-to-be-done.vue create mode 100644 smart-admin-web/javascript-ant-design-vue3/src/views/system/home/components/to-be-done-card/to-be-done-modal.vue diff --git a/smart-admin-api/pom.xml b/smart-admin-api/pom.xml index b08d256d..5f2a4b42 100644 --- a/smart-admin-api/pom.xml +++ b/smart-admin-api/pom.xml @@ -19,9 +19,10 @@ UTF-8 UTF-8 1.8 - 2.7.5 + 2.7.18 2.0.8 3.5.2 + 8.0.33 3.9.1 1.7.0 4.3.0 @@ -42,7 +43,7 @@ 5.2.4 1.4 1.11.842 - 2.17.2 + 2.23.1 5.7.22 2.3 0.9.1 @@ -52,8 +53,12 @@ 2.7.0 1.59 2.13.4 + 2.16.1 1.2.0 3.25.0 + 2.2 + 2.3.33 + 1.18.1 @@ -81,6 +86,12 @@ + + com.mysql + mysql-connector-j + ${mysql-connector-j.version} + + com.baomidou mybatis-plus-boot-starter @@ -201,12 +212,6 @@ ${commons-text.version} - - org.apache.logging.log4j - log4j-spring-boot - ${log4j-spring-boot.version} - - cn.hutool hutool-all @@ -309,6 +314,12 @@ ${jackson-datatype-jsr310.version} + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + ${jackson-dataformat-yaml.version} + + net.1024lab smartdb @@ -341,6 +352,24 @@ ${redisson.version} + + org.yaml + snakeyaml + ${snakeyaml.version} + + + + org.jsoup + jsoup + ${jsoup.version} + + + + org.freemarker + freemarker + ${freemarker.version} + + diff --git a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/interceptor/AdminInterceptor.java b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/interceptor/AdminInterceptor.java index 1de8e721..9b01d544 100644 --- a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/interceptor/AdminInterceptor.java +++ b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/interceptor/AdminInterceptor.java @@ -50,9 +50,6 @@ public class AdminInterceptor implements HandlerInterceptor { @Resource private SystemEnvironment systemEnvironment; - @Value("${sa-token.active-timeout}") - private long tokenActiveTimeout; - @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { @@ -158,11 +155,6 @@ private void checkActiveTimeout(RequestEmployee requestEmployee, boolean debugNu return; } - // 小于1 ,也不需要检测 - if (tokenActiveTimeout < 1) { - return; - } - StpUtil.checkActiveTimeout(); StpUtil.updateLastActiveToNow(); } diff --git a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/business/goods/domain/form/GoodsQueryForm.java b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/business/goods/domain/form/GoodsQueryForm.java index 128939f8..6f61e100 100644 --- a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/business/goods/domain/form/GoodsQueryForm.java +++ b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/business/goods/domain/form/GoodsQueryForm.java @@ -1,9 +1,11 @@ package net.lab1024.sa.admin.module.business.goods.domain.form; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import net.lab1024.sa.admin.module.business.goods.constant.GoodsStatusEnum; import net.lab1024.sa.base.common.domain.PageParam; +import net.lab1024.sa.base.common.json.deserializer.DictValueVoDeserializer; import net.lab1024.sa.base.common.swagger.SchemaEnum; import net.lab1024.sa.base.common.validator.enumeration.CheckEnum; import org.hibernate.validator.constraints.Length; @@ -32,6 +34,7 @@ public class GoodsQueryForm extends PageParam { private Integer goodsStatus; @Schema(description = "产地") + @JsonDeserialize(using = DictValueVoDeserializer.class) private String place; @Schema(description = "上架状态") diff --git a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/business/goods/service/GoodsService.java b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/business/goods/service/GoodsService.java index 8b7b2d34..497a168e 100644 --- a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/business/goods/service/GoodsService.java +++ b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/business/goods/service/GoodsService.java @@ -199,7 +199,7 @@ public List getAllGoods() { GoodsExcelVO.builder() .goodsStatus(SmartEnumUtil.getEnumDescByValue(e.getGoodsStatus(), GoodsStatusEnum.class)) .categoryName(categoryQueryService.queryCategoryName(e.getCategoryId())) - .place(dictCacheService.selectValueNameByValueCode(e.getPlace())) + .place(Arrays.stream(e.getPlace().split(",")).map(code -> dictCacheService.selectValueNameByValueCode(code)).collect(Collectors.joining(","))) .price(e.getPrice()) .goodsName(e.getGoodsName()) .remark(e.getRemark()) diff --git a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/business/oa/notice/dao/NoticeDao.java b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/business/oa/notice/dao/NoticeDao.java index d6654a43..f6c5060f 100644 --- a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/business/oa/notice/dao/NoticeDao.java +++ b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/business/oa/notice/dao/NoticeDao.java @@ -114,4 +114,14 @@ List queryEmployeeNotViewNotice(Page page, */ void updateViewRecord(@Param("noticeId")Long noticeId, @Param("employeeId")Long requestEmployeeId,@Param("ip") String ip, @Param("userAgent")String userAgent); + /** + * 更新 浏览量 + * + * @param noticeId 通知 id + * @param pageViewCountIncrement 页面浏览量的增量 + * @param userViewCountIncrement 用户浏览量的增量 + */ + void updateViewCount(@Param("noticeId")Long noticeId,@Param("pageViewCountIncrement") Integer pageViewCountIncrement, @Param("userViewCountIncrement")Integer userViewCountIncrement); + + } diff --git a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/business/oa/notice/service/NoticeEmployeeService.java b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/business/oa/notice/service/NoticeEmployeeService.java index 5ac0f389..8208a74f 100644 --- a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/business/oa/notice/service/NoticeEmployeeService.java +++ b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/business/oa/notice/service/NoticeEmployeeService.java @@ -52,10 +52,10 @@ public class NoticeEmployeeService { public ResponseDTO> queryList(Long requestEmployeeId, NoticeEmployeeQueryForm noticeEmployeeQueryForm) { Page page = SmartPageUtil.convert2PageQuery(noticeEmployeeQueryForm); - //获取请求人的 部门及其子部门 List employeeDepartmentIdList = Lists.newArrayList(); EmployeeEntity employeeEntity = employeeService.getById(requestEmployeeId); - if (employeeEntity.getDepartmentId() != null) { + // 如果不是管理员 则获取请求人的 部门及其子部门 + if (!employeeEntity.getAdministratorFlag() && employeeEntity.getDepartmentId() != null) { employeeDepartmentIdList = departmentService.selfAndChildrenIdList(employeeEntity.getDepartmentId()); } @@ -106,8 +106,15 @@ public ResponseDTO view(Long requestEmployeeId, Long noticeId, S long viewCount = noticeDao.viewRecordCount(noticeId, requestEmployeeId); if (viewCount == 0) { noticeDao.insertViewRecord(noticeId, requestEmployeeId, ip, userAgent, 1); + // 该员工对于这个通知是第一次查看 页面浏览量+1 用户浏览量+1 + noticeDao.updateViewCount(noticeId, 1, 1); + noticeDetailVO.setPageViewCount(noticeDetailVO.getPageViewCount() + 1); + noticeDetailVO.setUserViewCount(noticeDetailVO.getUserViewCount() + 1); } else { noticeDao.updateViewRecord(noticeId, requestEmployeeId, ip, userAgent); + // 该员工对于这个通知不是第一次查看 页面浏览量+1 用户浏览量+0 + noticeDao.updateViewCount(noticeId, 1, 0); + noticeDetailVO.setPageViewCount(noticeDetailVO.getPageViewCount() + 1); } return ResponseDTO.ok(noticeDetailVO); diff --git a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/department/manager/DepartmentCacheManager.java b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/department/manager/DepartmentCacheManager.java index 3ed6f350..5a4ecc4d 100644 --- a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/department/manager/DepartmentCacheManager.java +++ b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/department/manager/DepartmentCacheManager.java @@ -141,13 +141,15 @@ public List buildTree(List voList) { return treeVOList; } - /** + /** * 构建所有根节点的下级树形结构 - * + * 返回值为层序遍历结果 + * [由于departmentDao中listAll给出数据根据Sort降序 所以同一层中Sort值较大的优先遍历] */ - private void recursiveBuildTree(List nodeList, List allDepartmentList) { + private List recursiveBuildTree(List nodeList, List allDepartmentList) { int nodeSize = nodeList.size(); - for (int i = 0; i < nodeSize; i++) { + List childIdList = new ArrayList<>(); + for(int i = 0; i < nodeSize; i++) { int preIndex = i - 1; int nextIndex = i + 1; DepartmentTreeVO node = nodeList.get(i); @@ -158,16 +160,34 @@ private void recursiveBuildTree(List nodeList, List selfAndAllChildrenIdList = Lists.newArrayList(); - selfAndAllChildrenIdList.add(node.getDepartmentId()); - node.setSelfAndAllChildrenIdList(selfAndAllChildrenIdList); - List children = getChildren(node.getDepartmentId(), allDepartmentList); + + List tempChildIdList = new ArrayList<>(); if (CollectionUtils.isNotEmpty(children)) { node.setChildren(children); - this.recursiveBuildTree(children, allDepartmentList); + tempChildIdList = this.recursiveBuildTree(children, allDepartmentList); } + + if(CollectionUtils.isEmpty(node.getSelfAndAllChildrenIdList())) { + node.setSelfAndAllChildrenIdList( + new ArrayList<>() + ); + } + node.getSelfAndAllChildrenIdList().add(node.getDepartmentId()); + + if(CollectionUtils.isNotEmpty(tempChildIdList)) { + node.getSelfAndAllChildrenIdList().addAll(tempChildIdList); + childIdList.addAll(tempChildIdList); + } + + } + + // 保证本层遍历顺序 + for(int i = nodeSize - 1; i >= 0; i--) { + childIdList.add(0, nodeList.get(i).getDepartmentId()); } + + return childIdList; } diff --git a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/department/service/DepartmentService.java b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/department/service/DepartmentService.java index 7b58bca2..dade9aaa 100644 --- a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/department/service/DepartmentService.java +++ b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/department/service/DepartmentService.java @@ -92,7 +92,7 @@ public ResponseDTO deleteDepartment(Long departmentId) { } // 是否有未删除员工 - int employeeNum = employeeDao.countByDepartmentId(departmentId); + int employeeNum = employeeDao.countByDepartmentId(departmentId, Boolean.FALSE); if (employeeNum > 0) { return ResponseDTO.userErrorParam("请先删除部门员工"); } diff --git a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/controller/EmployeeController.java b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/controller/EmployeeController.java index a79b4355..b6d1c6d5 100644 --- a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/controller/EmployeeController.java +++ b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/controller/EmployeeController.java @@ -10,6 +10,8 @@ import net.lab1024.sa.base.common.domain.PageResult; import net.lab1024.sa.base.common.domain.ResponseDTO; import net.lab1024.sa.base.common.util.SmartRequestUtil; +import net.lab1024.sa.base.module.support.apiencrypt.annotation.ApiDecrypt; +import net.lab1024.sa.base.module.support.securityprotect.service.Level3ProtectConfigService; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; @@ -23,7 +25,7 @@ * @Date 2021-12-09 22:57:49 * @Wechat zhuoda1024 * @Email lab1024@163.com - * @Copyright 1024创新实验室 + * @Copyright 1024创新实验室 */ @RestController @Tag(name = AdminSwaggerTagConst.System.SYSTEM_EMPLOYEE) @@ -32,6 +34,9 @@ public class EmployeeController { @Resource private EmployeeService employeeService; + @Resource + private Level3ProtectConfigService level3ProtectConfigService; + @PostMapping("/employee/query") @Operation(summary = "员工管理查询 @author 卓大") public ResponseDTO> query(@Valid @RequestBody EmployeeQueryForm query) { @@ -89,9 +94,17 @@ public ResponseDTO batchUpdateDepartment(@Valid @RequestBody EmployeeBat @Operation(summary = "修改密码 @author 卓大") @PostMapping("/employee/update/password") + @ApiDecrypt public ResponseDTO updatePassword(@Valid @RequestBody EmployeeUpdatePasswordForm updatePasswordForm) { updatePasswordForm.setEmployeeId(SmartRequestUtil.getRequestUserId()); - return employeeService.updatePassword(updatePasswordForm); + return employeeService.updatePassword(SmartRequestUtil.getRequestUser(), updatePasswordForm); + } + + @Operation(summary = "获取密码复杂度 @author 卓大") + @GetMapping("/employee/getPasswordComplexityEnabled") + @ApiDecrypt + public ResponseDTO getPasswordComplexityEnabled() { + return ResponseDTO.ok(level3ProtectConfigService.isPasswordComplexityEnabled()); } @Operation(summary = "重置员工密码 @author 卓大") diff --git a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/dao/EmployeeDao.java b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/dao/EmployeeDao.java index c9126970..c1297a84 100644 --- a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/dao/EmployeeDao.java +++ b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/dao/EmployeeDao.java @@ -69,7 +69,7 @@ EmployeeEntity getByActualName(@Param("actualName") String actualName, * 获取某个部门员工数 * */ - Integer countByDepartmentId(@Param("departmentId") Long departmentId); + Integer countByDepartmentId(@Param("departmentId") Long departmentId, @Param("deletedFlag") Boolean deletedFlag); /** * 获取一批员工 diff --git a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/domain/entity/EmployeeEntity.java b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/domain/entity/EmployeeEntity.java index 692f831a..edc53e61 100644 --- a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/domain/entity/EmployeeEntity.java +++ b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/domain/entity/EmployeeEntity.java @@ -53,6 +53,11 @@ public class EmployeeEntity { */ private String phone; + /** + * 邮箱 + */ + private String email; + /** * 部门id */ diff --git a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/domain/form/EmployeeAddForm.java b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/domain/form/EmployeeAddForm.java index bd7b4e63..2621eb7f 100644 --- a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/domain/form/EmployeeAddForm.java +++ b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/domain/form/EmployeeAddForm.java @@ -51,6 +51,9 @@ public class EmployeeAddForm { @Pattern(regexp = SmartVerificationUtil.PHONE_REGEXP, message = "手机号格式不正确") private String phone; + @Schema(description = "邮箱") + private String email; + @Schema(description = "角色列表") private List roleIdList; diff --git a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/domain/form/EmployeeUpdatePasswordForm.java b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/domain/form/EmployeeUpdatePasswordForm.java index 736248f0..9a44afb2 100644 --- a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/domain/form/EmployeeUpdatePasswordForm.java +++ b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/domain/form/EmployeeUpdatePasswordForm.java @@ -24,11 +24,9 @@ public class EmployeeUpdatePasswordForm { @Schema(description = "原密码") @NotBlank(message = "原密码不能为空哦") - @Pattern(regexp = SmartVerificationUtil.PWD_REGEXP, message = "原密码请输入6-15位(数字|大小写字母|小数点)") private String oldPassword; @Schema(description = "新密码") @NotBlank(message = "新密码不能为空哦") - @Pattern(regexp = SmartVerificationUtil.PWD_REGEXP, message = "新密码请输入6-15位(数字|大小写字母|小数点)") private String newPassword; } diff --git a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/domain/vo/EmployeeVO.java b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/domain/vo/EmployeeVO.java index 5ab64c53..a4b620a8 100644 --- a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/domain/vo/EmployeeVO.java +++ b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/domain/vo/EmployeeVO.java @@ -62,4 +62,7 @@ public class EmployeeVO { @Schema(description = "职务名称") private String positionName; + @Schema(description = "邮箱") + private String email; + } diff --git a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/manager/EmployeeManager.java b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/manager/EmployeeManager.java index 2aca93bb..128a03dd 100644 --- a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/manager/EmployeeManager.java +++ b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/manager/EmployeeManager.java @@ -60,10 +60,14 @@ public void updateEmployee(EmployeeEntity employee, List roleIdList) { // 保存员工 获得id employeeDao.updateById(employee); - if (CollectionUtils.isNotEmpty(roleIdList)) { - List roleEmployeeList = roleIdList.stream().map(e -> new RoleEmployeeEntity(e, employee.getEmployeeId())).collect(Collectors.toList()); - this.updateEmployeeRole(employee.getEmployeeId(), roleEmployeeList); + // 若为空,则删除所有角色 + if (CollectionUtils.isEmpty(roleIdList)) { + roleEmployeeDao.deleteByEmployeeId(employee.getEmployeeId()); + return; } + + List roleEmployeeList = roleIdList.stream().map(e -> new RoleEmployeeEntity(e, employee.getEmployeeId())).collect(Collectors.toList()); + this.updateEmployeeRole(employee.getEmployeeId(), roleEmployeeList); } /** diff --git a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/service/EmployeeService.java b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/service/EmployeeService.java index eb037d42..61cd6f79 100644 --- a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/service/EmployeeService.java +++ b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/service/EmployeeService.java @@ -20,15 +20,16 @@ import net.lab1024.sa.base.common.code.UserErrorCode; import net.lab1024.sa.base.common.constant.StringConst; import net.lab1024.sa.base.common.domain.PageResult; +import net.lab1024.sa.base.common.domain.RequestUser; import net.lab1024.sa.base.common.domain.ResponseDTO; import net.lab1024.sa.base.common.enumeration.UserTypeEnum; import net.lab1024.sa.base.common.util.SmartBeanUtil; import net.lab1024.sa.base.common.util.SmartPageUtil; -import net.lab1024.sa.base.module.support.securityprotect.service.ProtectPasswordService; -import org.apache.commons.codec.digest.DigestUtils; +import net.lab1024.sa.base.module.support.securityprotect.service.SecurityPasswordService; import org.apache.commons.collections4.CollectionUtils; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; import java.util.*; @@ -46,8 +47,6 @@ @Service public class EmployeeService { - private static final String PASSWORD_SALT_FORMAT = "smart_%s_admin_$^&*"; - @Resource private EmployeeDao employeeDao; @@ -64,7 +63,7 @@ public class EmployeeService { private DepartmentService departmentService; @Resource - private ProtectPasswordService protectPasswordService; + private SecurityPasswordService securityPasswordService; @Resource @Lazy @@ -121,16 +120,11 @@ public ResponseDTO> queryEmployee(EmployeeQueryForm emplo * 新增员工 */ public synchronized ResponseDTO addEmployee(EmployeeAddForm employeeAddForm) { - // 校验名称是否重复 + // 校验登录名是否重复 EmployeeEntity employeeEntity = employeeDao.getByLoginName(employeeAddForm.getLoginName(), null); if (null != employeeEntity) { return ResponseDTO.userErrorParam("登录名重复"); } - // 校验姓名是否重复 - employeeEntity = employeeDao.getByActualName(employeeAddForm.getActualName(), null); - if (null != employeeEntity) { - return ResponseDTO.userErrorParam("姓名重复"); - } // 校验电话是否存在 employeeEntity = employeeDao.getByPhone(employeeAddForm.getPhone(), null); if (null != employeeEntity) { @@ -146,8 +140,8 @@ public synchronized ResponseDTO addEmployee(EmployeeAddForm employeeAddF EmployeeEntity entity = SmartBeanUtil.copy(employeeAddForm, EmployeeEntity.class); // 设置密码 默认密码 - String password = protectPasswordService.randomPassword(); - entity.setLoginPwd(getEncryptPwd(password)); + String password = securityPasswordService.randomPassword(); + entity.setLoginPwd(SecurityPasswordService.getEncryptPwd(password)); // 保存数据 entity.setDeletedFlag(Boolean.FALSE); @@ -185,11 +179,6 @@ public synchronized ResponseDTO updateEmployee(EmployeeUpdateForm employ return ResponseDTO.userErrorParam("手机号已存在"); } - existEntity = employeeDao.getByActualName(employeeUpdateForm.getActualName(), null); - if (null != existEntity && !Objects.equals(existEntity.getEmployeeId(), employeeId)) { - return ResponseDTO.userErrorParam("姓名重复"); - } - // 不更新密码 EmployeeEntity entity = SmartBeanUtil.copy(employeeUpdateForm, EmployeeEntity.class); entity.setLoginPwd(null); @@ -301,36 +290,46 @@ public ResponseDTO batchUpdateDepartment(EmployeeBatchUpdateDepartmentFo /** * 更新密码 */ - public ResponseDTO updatePassword(EmployeeUpdatePasswordForm updatePasswordForm) { + @Transactional(rollbackFor = Throwable.class) + public ResponseDTO updatePassword(RequestUser requestUser, EmployeeUpdatePasswordForm updatePasswordForm) { Long employeeId = updatePasswordForm.getEmployeeId(); EmployeeEntity employeeEntity = employeeDao.selectById(employeeId); if (employeeEntity == null) { return ResponseDTO.error(UserErrorCode.DATA_NOT_EXIST); } // 校验原始密码 - String encryptPwd = getEncryptPwd(updatePasswordForm.getOldPassword()); - if (!Objects.equals(encryptPwd, employeeEntity.getLoginPwd())) { + String oldPassword = SecurityPasswordService.getEncryptPwd(updatePasswordForm.getOldPassword()); + if (!Objects.equals(oldPassword, employeeEntity.getLoginPwd())) { return ResponseDTO.userErrorParam("原密码有误,请重新输入"); } + // 校验密码复杂度 + ResponseDTO validatePassComplexity = securityPasswordService.validatePasswordComplexity(updatePasswordForm.getNewPassword()); + if (!validatePassComplexity.getOk()) { + return validatePassComplexity; + } + // 新旧密码相同 - String newPassword = updatePasswordForm.getNewPassword(); - if (Objects.equals(updatePasswordForm.getOldPassword(), newPassword)) { + String newPassword = SecurityPasswordService.getEncryptPwd(updatePasswordForm.getNewPassword()); + if (Objects.equals(oldPassword, newPassword)) { return ResponseDTO.userErrorParam("新密码与原始密码相同,请重新输入"); } - // 校验密码复杂度 - ResponseDTO validatePassComplexity = protectPasswordService.validatePassComplexity(newPassword); - if (!validatePassComplexity.getOk()) { - return validatePassComplexity; + // 根据三级等保规则,校验密码是否重复 + ResponseDTO passwordRepeatTimes = securityPasswordService.validatePasswordRepeatTimes(requestUser, updatePasswordForm.getNewPassword()); + if (!passwordRepeatTimes.getOk()) { + return ResponseDTO.error(passwordRepeatTimes); } // 更新密码 EmployeeEntity updateEntity = new EmployeeEntity(); updateEntity.setEmployeeId(employeeId); - updateEntity.setLoginPwd(getEncryptPwd(newPassword)); + updateEntity.setLoginPwd(newPassword); employeeDao.updateById(updateEntity); + // 保存修改密码密码记录 + securityPasswordService.saveUserChangePasswordLog(requestUser, newPassword, oldPassword); + return ResponseDTO.ok(); } @@ -364,18 +363,11 @@ public ResponseDTO> getAllEmployeeByDepartmentId(Long departmen * 重置密码 */ public ResponseDTO resetPassword(Integer employeeId) { - String password = protectPasswordService.randomPassword(); - employeeDao.updatePassword(employeeId, getEncryptPwd(password)); + String password = securityPasswordService.randomPassword(); + employeeDao.updatePassword(employeeId, SecurityPasswordService.getEncryptPwd(password)); return ResponseDTO.ok(password); } - /** - * 获取 加密后 的密码 - */ - public static String getEncryptPwd(String password) { - return DigestUtils.md5Hex(String.format(PASSWORD_SALT_FORMAT, password)); - } - /** * 查询全部员工 diff --git a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/login/controller/LoginController.java b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/login/controller/LoginController.java index 9bf884d5..d2ab72a0 100644 --- a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/login/controller/LoginController.java +++ b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/login/controller/LoginController.java @@ -2,8 +2,8 @@ import cn.dev33.satoken.stp.StpUtil; import cn.hutool.extra.servlet.ServletUtil; -import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; import net.lab1024.sa.admin.constant.AdminSwaggerTagConst; import net.lab1024.sa.admin.module.system.login.domain.LoginForm; import net.lab1024.sa.admin.module.system.login.domain.LoginResultVO; @@ -14,6 +14,7 @@ import net.lab1024.sa.base.common.domain.ResponseDTO; import net.lab1024.sa.base.common.util.SmartRequestUtil; import net.lab1024.sa.base.module.support.captcha.domain.CaptchaVO; +import net.lab1024.sa.base.module.support.securityprotect.service.Level3ProtectConfigService; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; @@ -27,7 +28,7 @@ * @Date 2021-12-15 21:05:46 * @Wechat zhuoda1024 * @Email lab1024@163.com - * @Copyright 1024创新实验室 + * @Copyright 1024创新实验室 */ @RestController @Tag(name = AdminSwaggerTagConst.System.SYSTEM_LOGIN) @@ -36,6 +37,9 @@ public class LoginController { @Resource private LoginService loginService; + @Resource + private Level3ProtectConfigService level3ProtectConfigService; + @NoNeedLogin @PostMapping("/login") @Operation(summary = "登录 @author 卓大") @@ -48,8 +52,8 @@ public ResponseDTO login(@Valid @RequestBody LoginForm loginForm, @GetMapping("/login/getLoginInfo") @Operation(summary = "获取登录结果信息 @author 卓大") public ResponseDTO getLoginInfo() { - LoginResultVO loginResult = loginService.getLoginResult(AdminRequestUtil.getRequestUser()); String tokenValue = StpUtil.getTokenValue(); + LoginResultVO loginResult = loginService.getLoginResult(AdminRequestUtil.getRequestUser(), tokenValue); loginResult.setToken(tokenValue); return ResponseDTO.ok(loginResult); } @@ -67,4 +71,20 @@ public ResponseDTO getCaptcha() { return loginService.getCaptcha(); } + @NoNeedLogin + @GetMapping("/login/sendEmailCode/{loginName}") + @Operation(summary = "获取邮箱登录验证码 @author 卓大") + public ResponseDTO sendEmailCode(@PathVariable String loginName) { + return loginService.sendEmailCode(loginName); + } + + + @NoNeedLogin + @GetMapping("/login/getTwoFactorLoginFlag") + @Operation(summary = "获取双因子登录标识 @author 卓大") + public ResponseDTO getTwoFactorLoginFlag() { + // 双因子登录 + boolean twoFactorLoginEnabled = level3ProtectConfigService.isTwoFactorLoginEnabled(); + return ResponseDTO.ok(twoFactorLoginEnabled); + } } diff --git a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/login/domain/LoginForm.java b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/login/domain/LoginForm.java index 207120c4..fd707a1e 100644 --- a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/login/domain/LoginForm.java +++ b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/login/domain/LoginForm.java @@ -34,4 +34,7 @@ public class LoginForm extends CaptchaForm { @SchemaEnum(desc = "登录终端", value = LoginDeviceEnum.class) @CheckEnum(value = LoginDeviceEnum.class, required = true, message = "此终端不允许登录") private Integer loginDevice; + + @Schema(description = "邮箱验证码") + private String emailCode; } diff --git a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/login/domain/LoginResultVO.java b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/login/domain/LoginResultVO.java index 4bcd0cbc..7ab52c6d 100644 --- a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/login/domain/LoginResultVO.java +++ b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/login/domain/LoginResultVO.java @@ -26,6 +26,9 @@ public class LoginResultVO extends RequestEmployee { @Schema(description = "菜单列表") private List menuList; + @Schema(description = "是否需要修改密码") + private Boolean needUpdatePwdFlag; + @Schema(description = "上次登录ip") private String lastLoginIp; diff --git a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/login/service/LoginService.java b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/login/service/LoginService.java index e32e177f..2f1bb2ad 100644 --- a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/login/service/LoginService.java +++ b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/login/service/LoginService.java @@ -3,6 +3,8 @@ import cn.dev33.satoken.stp.StpInterface; import cn.dev33.satoken.stp.StpUtil; import cn.hutool.core.lang.UUID; +import cn.hutool.core.util.NumberUtil; +import cn.hutool.core.util.RandomUtil; import cn.hutool.extra.servlet.ServletUtil; import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap; import lombok.extern.slf4j.Slf4j; @@ -27,7 +29,10 @@ import net.lab1024.sa.base.common.util.SmartBeanUtil; import net.lab1024.sa.base.common.util.SmartEnumUtil; import net.lab1024.sa.base.common.util.SmartIpUtil; +import net.lab1024.sa.base.common.util.SmartStringUtil; import net.lab1024.sa.base.constant.LoginDeviceEnum; +import net.lab1024.sa.base.constant.RedisKeyConst; +import net.lab1024.sa.base.module.support.apiencrypt.service.ApiEncryptService; import net.lab1024.sa.base.module.support.captcha.CaptchaService; import net.lab1024.sa.base.module.support.captcha.domain.CaptchaVO; import net.lab1024.sa.base.module.support.config.ConfigKeyEnum; @@ -37,9 +42,13 @@ import net.lab1024.sa.base.module.support.loginlog.LoginLogService; import net.lab1024.sa.base.module.support.loginlog.domain.LoginLogEntity; import net.lab1024.sa.base.module.support.loginlog.domain.LoginLogVO; +import net.lab1024.sa.base.module.support.mail.MailService; +import net.lab1024.sa.base.module.support.mail.constant.MailTemplateCodeEnum; +import net.lab1024.sa.base.module.support.redis.RedisService; import net.lab1024.sa.base.module.support.securityprotect.domain.LoginFailEntity; -import net.lab1024.sa.base.module.support.securityprotect.service.ProtectLoginService; -import net.lab1024.sa.base.module.support.securityprotect.service.ProtectPasswordService; +import net.lab1024.sa.base.module.support.securityprotect.service.Level3ProtectConfigService; +import net.lab1024.sa.base.module.support.securityprotect.service.SecurityLoginService; +import net.lab1024.sa.base.module.support.securityprotect.service.SecurityPasswordService; import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; @@ -107,14 +116,26 @@ public class LoginService implements StpInterface { private RoleMenuService roleMenuService; @Resource - private ProtectLoginService protectLoginService; + private SecurityLoginService securityLoginService; @Resource - private ProtectPasswordService profectPasswordService; + private SecurityPasswordService protectPasswordService; @Resource private IFileStorageService fileStorageService; + @Resource + private ApiEncryptService apiEncryptService; + + @Resource + private Level3ProtectConfigService level3ProtectConfigService; + + @Resource + private MailService mailService; + + @Resource + private RedisService redisService; + /** * 获取验证码 */ @@ -153,12 +174,18 @@ public ResponseDTO login(LoginForm loginForm, String ip, String u } // 解密前端加密的密码 - String requestPassword = profectPasswordService.decryptPassword(loginForm.getPassword()); + String requestPassword = apiEncryptService.decrypt(loginForm.getPassword()); // 验证密码 是否为万能密码 String superPassword = configService.getConfigValue(ConfigKeyEnum.SUPER_PASSWORD); boolean superPasswordFlag = superPassword.equals(requestPassword); + // 校验双因子登录 + ResponseDTO validateEmailCode = validateEmailCode(loginForm, employeeEntity, superPasswordFlag); + if (!validateEmailCode.getOk()) { + return ResponseDTO.error(validateEmailCode); + } + // 万能密码特殊操作 if (superPasswordFlag) { @@ -170,23 +197,27 @@ public ResponseDTO login(LoginForm loginForm, String ip, String u } else { // 按照等保登录要求,进行登录失败次数校验 - ResponseDTO loginFailEntityResponseDTO = protectLoginService.checkLogin(employeeEntity.getEmployeeId(), UserTypeEnum.ADMIN_EMPLOYEE); + ResponseDTO loginFailEntityResponseDTO = securityLoginService.checkLogin(employeeEntity.getEmployeeId(), UserTypeEnum.ADMIN_EMPLOYEE); if (!loginFailEntityResponseDTO.getOk()) { return ResponseDTO.error(loginFailEntityResponseDTO); } // 密码错误 - if (!employeeEntity.getLoginPwd().equals(EmployeeService.getEncryptPwd(requestPassword))) { + if (!employeeEntity.getLoginPwd().equals(SecurityPasswordService.getEncryptPwd(requestPassword))) { // 记录登录失败 saveLoginLog(employeeEntity, ip, userAgent, "密码错误", LoginLogResultEnum.LOGIN_FAIL); // 记录等级保护次数 - String msg = protectLoginService.recordLoginFail(employeeEntity.getEmployeeId(), UserTypeEnum.ADMIN_EMPLOYEE, employeeEntity.getLoginName(), loginFailEntityResponseDTO.getData()); + String msg = securityLoginService.recordLoginFail(employeeEntity.getEmployeeId(), UserTypeEnum.ADMIN_EMPLOYEE, employeeEntity.getLoginName(), loginFailEntityResponseDTO.getData()); return msg == null ? ResponseDTO.userErrorParam("登录名或密码错误!") : ResponseDTO.error(UserErrorCode.LOGIN_FAIL_WILL_LOCK, msg); } String saTokenLoginId = UserTypeEnum.ADMIN_EMPLOYEE.getValue() + StringConst.COLON + employeeEntity.getEmployeeId(); + // 登录 StpUtil.login(saTokenLoginId, String.valueOf(loginDeviceEnum.getDesc())); + + // 移除邮箱验证码 + deleteEmailCode(employeeEntity.getEmployeeId()); } // 获取员工信息 @@ -196,16 +227,17 @@ public ResponseDTO login(LoginForm loginForm, String ip, String u loginEmployeeCache.put(employeeEntity.getEmployeeId(), requestEmployee); // 移除登录失败 - protectLoginService.removeLoginFail(employeeEntity.getEmployeeId(), UserTypeEnum.ADMIN_EMPLOYEE); + securityLoginService.removeLoginFail(employeeEntity.getEmployeeId(), UserTypeEnum.ADMIN_EMPLOYEE); // 获取登录结果信息 - LoginResultVO loginResultVO = getLoginResult(requestEmployee); + String token = StpUtil.getTokenValue(); + LoginResultVO loginResultVO = getLoginResult(requestEmployee, token); //保存登录记录 saveLoginLog(employeeEntity, ip, userAgent, superPasswordFlag ? "万能密码登录" : loginDeviceEnum.getDesc(), LoginLogResultEnum.LOGIN_SUCCESS); // 设置 token - loginResultVO.setToken(StpUtil.getTokenValue()); + loginResultVO.setToken(token); // 清除权限缓存 permissionCache.remove(employeeEntity.getEmployeeId()); @@ -217,7 +249,7 @@ public ResponseDTO login(LoginForm loginForm, String ip, String u /** * 获取登录结果信息 */ - public LoginResultVO getLoginResult(RequestEmployee requestEmployee) { + public LoginResultVO getLoginResult(RequestEmployee requestEmployee, String token) { // 基础信息 LoginResultVO loginResultVO = SmartBeanUtil.copy(requestEmployee, LoginResultVO.class); @@ -240,6 +272,16 @@ public LoginResultVO getLoginResult(RequestEmployee requestEmployee) { loginResultVO.setLastLoginUserAgent(loginLogVO.getUserAgent()); } + // 是否需要强制修改密码 + boolean needChangePasswordFlag = protectPasswordService.checkNeedChangePassword(requestEmployee.getUserType().getValue(), requestEmployee.getUserId()); + loginResultVO.setNeedUpdatePwdFlag(needChangePasswordFlag); + + // 万能密码登录,则不需要设置强制修改密码 + String loginIdByToken = (String) StpUtil.getLoginIdByToken(token); + if (loginIdByToken != null && loginIdByToken.startsWith(SUPER_PASSWORD_LOGIN_ID_PREFIX)) { + loginResultVO.setNeedUpdatePwdFlag(false); + } + return loginResultVO; } @@ -259,9 +301,9 @@ private RequestEmployee loadLoginInfo(EmployeeEntity employeeEntity) { // 头像信息 String avatar = employeeEntity.getAvatar(); - if(StringUtils.isNotBlank(avatar)){ + if (StringUtils.isNotBlank(avatar)) { ResponseDTO getFileUrl = fileStorageService.getFileUrl(avatar); - if(BooleanUtils.isTrue(getFileUrl.getOk())){ + if (BooleanUtils.isTrue(getFileUrl.getOk())) { requestEmployee.setAvatar(getFileUrl.getData()); } } @@ -357,9 +399,8 @@ public ResponseDTO logout(String token, RequestUser requestUser) { /** * 清除员工登录缓存 - * @param employeeId */ - public void clearLoginEmployeeCache(Long employeeId){ + public void clearLoginEmployeeCache(Long employeeId) { // 清空登录信息缓存 loginEmployeeCache.remove(employeeId); } @@ -451,4 +492,94 @@ private UserPermission getUserPermission(Long employeeId) { return userPermission; } + + /** + * 发送 邮箱 验证码 + */ + public ResponseDTO sendEmailCode(String loginName) { + + // 开启双因子登录 + if (!level3ProtectConfigService.isTwoFactorLoginEnabled()) { + return ResponseDTO.userErrorParam("无需使用邮箱验证码"); + } + + // 验证登录名 + EmployeeEntity employeeEntity = employeeService.getByLoginName(loginName); + if (null == employeeEntity) { + return ResponseDTO.userErrorParam("登录名不存在!"); + } + + // 验证账号状态 + if (employeeEntity.getDisabledFlag()) { + return ResponseDTO.userErrorParam("您的账号已被禁用,请联系工作人员!"); + } + + String mail = employeeEntity.getEmail(); + if (SmartStringUtil.isBlank(mail)) { + return ResponseDTO.userErrorParam("您暂未配置邮箱地址,请联系管理员配置邮箱"); + } + + // 校验验证码发送时间,60秒内不能重复发生 + String redisVerificationCodeKey = redisService.generateRedisKey(RedisKeyConst.Support.LOGIN_VERIFICATION_CODE, UserTypeEnum.ADMIN_EMPLOYEE.getValue() + RedisKeyConst.SEPARATOR + employeeEntity.getEmployeeId()); + String emailCode = redisService.get(redisVerificationCodeKey); + long sendCodeTimeMills = -1; + if (!SmartStringUtil.isEmpty(emailCode)) { + sendCodeTimeMills = NumberUtil.parseLong(emailCode.split(StringConst.UNDERLINE)[1]); + } + + if (System.currentTimeMillis() - sendCodeTimeMills < 60 * 1000) { + return ResponseDTO.userErrorParam("邮箱验证码已发送,一分钟内请勿重复发送"); + } + + //生成验证码 + long currentTimeMillis = System.currentTimeMillis(); + String verificationCode = RandomUtil.randomNumbers(4); + redisService.set(redisVerificationCodeKey, verificationCode + StringConst.UNDERLINE + currentTimeMillis, 300); + + // 发送邮件验证码 + HashMap mailParams = new HashMap<>(); + mailParams.put("code", verificationCode); + return mailService.sendMail(MailTemplateCodeEnum.LOGIN_VERIFICATION_CODE, mailParams, Collections.singletonList(employeeEntity.getEmail())); + } + + + /** + * 校验邮箱验证码 + */ + private ResponseDTO validateEmailCode(LoginForm loginForm, EmployeeEntity employeeEntity, boolean superPasswordFlag) { + // 万能密码则不校验 + if (superPasswordFlag) { + return ResponseDTO.ok(); + } + + // 未开启双因子登录 + if (!level3ProtectConfigService.isTwoFactorLoginEnabled()) { + return ResponseDTO.ok(); + } + + if (SmartStringUtil.isEmpty(loginForm.getEmailCode())) { + return ResponseDTO.userErrorParam("请输入邮箱验证码"); + } + + // 校验验证码 + String redisVerificationCodeKey = redisService.generateRedisKey(RedisKeyConst.Support.LOGIN_VERIFICATION_CODE, UserTypeEnum.ADMIN_EMPLOYEE.getValue() + RedisKeyConst.SEPARATOR + employeeEntity.getEmployeeId()); + String emailCode = redisService.get(redisVerificationCodeKey); + if (SmartStringUtil.isEmpty(emailCode)) { + return ResponseDTO.userErrorParam("邮箱验证码已失效,请重新发送"); + } + + if (!emailCode.split(StringConst.UNDERLINE)[0].equals(loginForm.getEmailCode().trim())) { + return ResponseDTO.userErrorParam("邮箱验证码错误,请重新填写"); + } + + return ResponseDTO.ok(); + } + + /** + * 移除邮箱验证码 + */ + private void deleteEmailCode(Long employeeId) { + String redisVerificationCodeKey = redisService.generateRedisKey(RedisKeyConst.Support.LOGIN_VERIFICATION_CODE, UserTypeEnum.ADMIN_EMPLOYEE.getValue() + RedisKeyConst.SEPARATOR + employeeId); + redisService.delete(redisVerificationCodeKey); + } } diff --git a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/role/dao/RoleEmployeeDao.java b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/role/dao/RoleEmployeeDao.java index 06f27ca5..3c2cdd0a 100644 --- a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/role/dao/RoleEmployeeDao.java +++ b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/role/dao/RoleEmployeeDao.java @@ -12,6 +12,7 @@ import net.lab1024.sa.admin.module.system.role.domain.vo.RoleEmployeeVO; import java.util.List; +import java.util.Set; /** @@ -50,7 +51,7 @@ public interface RoleEmployeeDao extends BaseMapper { /** * 查询角色下的人员id */ - List selectEmployeeIdByRoleIdList(@Param("roleIdList") List roleIdList); + Set selectEmployeeIdByRoleIdList(@Param("roleIdList") List roleIdList); /** * @@ -79,5 +80,10 @@ public interface RoleEmployeeDao extends BaseMapper { /** * 批量删除某个角色下的某批用户的关联关系 */ - void batchDeleteEmployeeRole(@Param("roleId") Long roleId,@Param("employeeIds")List employeeIds); + void batchDeleteEmployeeRole(@Param("roleId") Long roleId, @Param("employeeIds") Set employeeIds); + + /** + * 判断某个角色下是否存在用户 + */ + Integer existsByRoleId(@Param("roleId") Long roleId); } diff --git a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/role/domain/form/RoleEmployeeUpdateForm.java b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/role/domain/form/RoleEmployeeUpdateForm.java index ef849e7c..4960b4ab 100644 --- a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/role/domain/form/RoleEmployeeUpdateForm.java +++ b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/role/domain/form/RoleEmployeeUpdateForm.java @@ -5,7 +5,7 @@ import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; -import java.util.List; +import java.util.Set; /** * 角色的员工更新 @@ -25,6 +25,6 @@ public class RoleEmployeeUpdateForm { @Schema(description = "员工id集合") @NotEmpty(message = "员工id不能为空") - protected List employeeIdList; + protected Set employeeIdList; } diff --git a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/role/manager/RoleEmployeeManager.java b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/role/manager/RoleEmployeeManager.java index f8fbd6f2..ba33dfd2 100644 --- a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/role/manager/RoleEmployeeManager.java +++ b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/role/manager/RoleEmployeeManager.java @@ -3,11 +3,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import net.lab1024.sa.admin.module.system.role.dao.RoleEmployeeDao; import net.lab1024.sa.admin.module.system.role.domain.entity.RoleEmployeeEntity; -import org.apache.commons.collections4.CollectionUtils; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.List; /** * 角色员工 manager @@ -16,20 +12,9 @@ * @Date 2022-04-08 21:53:04 * @Wechat zhuoda1024 * @Email lab1024@163.com - * @Copyright 1024创新实验室 + * @Copyright 1024创新实验室 */ @Service public class RoleEmployeeManager extends ServiceImpl { - /** - * 保存 角色员工 - * - */ - @Transactional(rollbackFor = Throwable.class) - public void saveRoleEmployee(Long roleId, List roleEmployeeList) { - this.getBaseMapper().deleteByRoleId(roleId); - if (CollectionUtils.isNotEmpty(roleEmployeeList)) { - this.saveBatch(roleEmployeeList); - } - } } diff --git a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/role/service/RoleEmployeeService.java b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/role/service/RoleEmployeeService.java index c0a8cdad..755aa892 100644 --- a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/role/service/RoleEmployeeService.java +++ b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/role/service/RoleEmployeeService.java @@ -1,6 +1,7 @@ package net.lab1024.sa.admin.module.system.role.service; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.google.common.collect.Lists; import net.lab1024.sa.admin.module.system.department.dao.DepartmentDao; import net.lab1024.sa.admin.module.system.department.domain.entity.DepartmentEntity; import net.lab1024.sa.admin.module.system.employee.domain.vo.EmployeeVO; @@ -20,12 +21,12 @@ import net.lab1024.sa.base.common.util.SmartPageUtil; import org.apache.commons.collections4.CollectionUtils; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.stream.Collectors; /** @@ -88,7 +89,6 @@ public List getAllEmployeeByRoleId(Long roleId) { * 移除员工角色 * */ - @Transactional(rollbackFor = Exception.class) public ResponseDTO removeRoleEmployee(Long employeeId, Long roleId) { if (null == employeeId || null == roleId) { return ResponseDTO.userErrorParam(); @@ -112,16 +112,21 @@ public ResponseDTO batchRemoveRoleEmployee(RoleEmployeeUpdateForm roleEm */ public ResponseDTO batchAddRoleEmployee(RoleEmployeeUpdateForm roleEmployeeUpdateForm) { Long roleId = roleEmployeeUpdateForm.getRoleId(); - List employeeIdList = roleEmployeeUpdateForm.getEmployeeIdList(); - // 保存新的角色员工 - List roleEmployeeList = null; - if (CollectionUtils.isNotEmpty(employeeIdList)) { - roleEmployeeList = employeeIdList.stream() + + // 已选择的员工id列表 + Set selectedEmployeeIdList = roleEmployeeUpdateForm.getEmployeeIdList(); + // 数据库里已有的员工id列表 + Set dbEmployeeIdList = roleEmployeeDao.selectEmployeeIdByRoleIdList(Lists.newArrayList(roleId)); + // 从已选择的员工id列表里 过滤数据库里不存在的 即需要添加的员工 id + Set addEmployeeIdList = selectedEmployeeIdList.stream().filter(id -> !dbEmployeeIdList.contains(id)).collect(Collectors.toSet()); + + // 添加角色员工 + if (CollectionUtils.isNotEmpty(addEmployeeIdList)) { + List roleEmployeeList = addEmployeeIdList.stream() .map(employeeId -> new RoleEmployeeEntity(roleId, employeeId)) .collect(Collectors.toList()); + roleEmployeeManager.saveBatch(roleEmployeeList); } - // 保存数据 - roleEmployeeManager.saveRoleEmployee(roleId, roleEmployeeList); return ResponseDTO.ok(); } diff --git a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/role/service/RoleService.java b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/role/service/RoleService.java index b69abea3..2d01b4bf 100644 --- a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/role/service/RoleService.java +++ b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/role/service/RoleService.java @@ -65,6 +65,11 @@ public ResponseDTO deleteRole(Long roleId) { if (null == roleEntity) { return ResponseDTO.error(UserErrorCode.DATA_NOT_EXIST); } + // 当没有员工绑定这个角色时才可以删除 + Integer exists = roleEmployeeDao.existsByRoleId(roleId); + if (exists != null) { + return ResponseDTO.error(UserErrorCode.ALREADY_EXIST, "该角色下存在员工,无法删除"); + } roleDao.deleteById(roleId); roleMenuDao.deleteByRoleId(roleId); roleEmployeeDao.deleteByRoleId(roleId); @@ -86,7 +91,7 @@ public ResponseDTO updateRole(RoleUpdateForm roleUpdateForm) { } existRoleEntity = roleDao.getByRoleCode(roleUpdateForm.getRoleCode()); - if (null != existRoleEntity) { + if (null != existRoleEntity && !existRoleEntity.getRoleId().equals(roleUpdateForm.getRoleId())) { return ResponseDTO.userErrorParam("角色编码重复,重复的角色为:" + existRoleEntity.getRoleName()); } diff --git a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/support/AdminDataMaskingDemoController.java b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/support/AdminDataMaskingDemoController.java new file mode 100644 index 00000000..7e6156b1 --- /dev/null +++ b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/support/AdminDataMaskingDemoController.java @@ -0,0 +1,88 @@ +package net.lab1024.sa.admin.module.system.support; + +import cn.hutool.core.util.RandomUtil; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; +import net.lab1024.sa.base.common.controller.SupportBaseController; +import net.lab1024.sa.base.common.domain.ResponseDTO; +import net.lab1024.sa.base.constant.SwaggerTagConst; +import net.lab1024.sa.base.module.support.datamasking.DataMasking; +import net.lab1024.sa.base.module.support.datamasking.DataMaskingTypeEnum; +import org.apache.commons.lang3.RandomStringUtils; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.ArrayList; +import java.util.List; + +/** + * 数据脱敏demo + * + * @Author 1024创新实验室-主任:卓大 + * @Date 2024/08/01 22:07:27 + * @Wechat zhuoda1024 + * @Email lab1024@163.com + * @Copyright 1024创新实验室,Since 2012 + */ + +@RestController +@Tag(name = SwaggerTagConst.Support.DATA_MASKING) +public class AdminDataMaskingDemoController extends SupportBaseController { + + @Operation(summary = "数据脱敏demo @author 1024创新实验室-主任-卓大") + @GetMapping("/dataMasking/demo/query") + public ResponseDTO> query() { + + List list = new ArrayList<>(); + for (int i = 0; i < RandomUtil.randomInt(10,16); i++) { + DataVO data = new DataVO(); + data.setUserId(RandomUtil.randomLong(1328479238, 83274298347982L)); + data.setPhone("1" + RandomUtil.randomNumbers(10)); + data.setIdCard("410" + RandomUtil.randomNumbers(3) + RandomUtil.randomInt(1980, 2010) + RandomUtil.randomInt(10, 12) + RandomUtil.randomInt(10, 30) + RandomUtil.randomNumbers(4)); + data.setAddress(RandomUtil.randomBoolean() ? "河南省洛阳市洛龙区一零二四大街1024号" : "河南省郑州市高新区六边形大街六边形大楼"); + data.setPassword(RandomUtil.randomString(10)); + data.setEmail(RandomUtil.randomString(RandomUtil.randomInt(6, 10)) + "@" + RandomUtil.randomString(2) + ".com"); + data.setCarLicense("豫" + RandomStringUtils.randomAlphabetic(1).toUpperCase()+" " + RandomStringUtils.randomAlphanumeric(5).toUpperCase()); + data.setBankCard("6225" + RandomStringUtils.randomNumeric(14)); + data.setOther(RandomStringUtils.randomAlphanumeric(1, 12)); + list.add(data); + } + + return ResponseDTO.ok(list); + } + + + @Data + public static class DataVO { + + @DataMasking(DataMaskingTypeEnum.USER_ID) + private Long userId; + + @DataMasking(DataMaskingTypeEnum.PHONE) + private String phone; + + @DataMasking(DataMaskingTypeEnum.ID_CARD) + private String idCard; + + @DataMasking(DataMaskingTypeEnum.ADDRESS) + private String address; + + @DataMasking(DataMaskingTypeEnum.PASSWORD) + private String password; + + @DataMasking(DataMaskingTypeEnum.EMAIL) + private String email; + + @DataMasking(DataMaskingTypeEnum.CAR_LICENSE) + private String carLicense; + + @DataMasking(DataMaskingTypeEnum.BANK_CARD) + private String bankCard; + + @DataMasking + private String other; + + } + +} diff --git a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/support/AdminProtectController.java b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/support/AdminProtectController.java index faa875a3..9e737f41 100644 --- a/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/support/AdminProtectController.java +++ b/smart-admin-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/system/support/AdminProtectController.java @@ -1,15 +1,20 @@ package net.lab1024.sa.admin.module.system.support; -import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; import net.lab1024.sa.base.common.controller.SupportBaseController; import net.lab1024.sa.base.common.domain.PageResult; import net.lab1024.sa.base.common.domain.ResponseDTO; import net.lab1024.sa.base.common.domain.ValidateList; import net.lab1024.sa.base.constant.SwaggerTagConst; +import net.lab1024.sa.base.module.support.config.ConfigKeyEnum; +import net.lab1024.sa.base.module.support.config.ConfigService; +import net.lab1024.sa.base.module.support.securityprotect.domain.Level3ProtectConfigForm; import net.lab1024.sa.base.module.support.securityprotect.domain.LoginFailQueryForm; import net.lab1024.sa.base.module.support.securityprotect.domain.LoginFailVO; -import net.lab1024.sa.base.module.support.securityprotect.service.ProtectLoginService; +import net.lab1024.sa.base.module.support.securityprotect.service.Level3ProtectConfigService; +import net.lab1024.sa.base.module.support.securityprotect.service.SecurityLoginService; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @@ -18,14 +23,13 @@ import javax.validation.Valid; /** - * * 网络安全 * * @Author 1024创新实验室-主任:卓大 * @Date 2023/10/17 19:07:27 * @Wechat zhuoda1024 * @Email lab1024@163.com - * @Copyright 1024创新实验室,Since 2012 + * @Copyright 1024创新实验室,Since 2012 */ @RestController @@ -33,20 +37,37 @@ public class AdminProtectController extends SupportBaseController { @Resource - private ProtectLoginService protectLoginService; + private SecurityLoginService securityLoginService; + + @Resource + private Level3ProtectConfigService level3ProtectConfigService; + + @Resource + private ConfigService configService; + @Operation(summary = "分页查询 @author 1024创新实验室-主任-卓大") @PostMapping("/protect/loginFail/queryPage") public ResponseDTO> queryPage(@RequestBody @Valid LoginFailQueryForm queryForm) { - return ResponseDTO.ok(protectLoginService.queryPage(queryForm)); + return ResponseDTO.ok(securityLoginService.queryPage(queryForm)); } @Operation(summary = "批量删除 @author 1024创新实验室-主任-卓大") @PostMapping("/protect/loginFail/batchDelete") public ResponseDTO batchDelete(@RequestBody ValidateList idList) { - return protectLoginService.batchDelete(idList); + return securityLoginService.batchDelete(idList); } + @Operation(summary = "更新三级等保配置 @author 1024创新实验室-主任-卓大") + @PostMapping("/protect/level3protect/updateConfig") + public ResponseDTO updateConfig(@RequestBody @Valid Level3ProtectConfigForm configForm) { + return level3ProtectConfigService.updateLevel3Config(configForm); + } + @Operation(summary = "查询 三级等保配置 @author 1024创新实验室-主任-卓大") + @GetMapping("/protect/level3protect/getConfig") + public ResponseDTO getConfig() { + return ResponseDTO.ok(configService.getConfigValue(ConfigKeyEnum.LEVEL3_PROTECT_CONFIG)); + } } diff --git a/smart-admin-api/sa-admin/src/main/resources/dev/application.yaml b/smart-admin-api/sa-admin/src/main/resources/dev/application.yaml index ba540719..7cd85cfa 100644 --- a/smart-admin-api/sa-admin/src/main/resources/dev/application.yaml +++ b/smart-admin-api/sa-admin/src/main/resources/dev/application.yaml @@ -19,27 +19,4 @@ server: # 环境 spring: profiles: - active: '@profiles.active@' - -####################################### 安全等级保护 相关配置 ################################################## -# # -# 建议开启 "三级等保" 所要求的配置,具体如下: # -# 1)连续登录失败 5 次锁定账户 30 分钟, # -# 2)登录超时时长建议为 30分钟,超过此时间没有访问系统会重新要求登录 # -# 3)密码复杂度至少三种字符,最小 8 位 # -# # -############################################################################################################# - -classified-protect: - # 连续登录失败次数则锁定,-1表示不受限制,可以一直尝试登录 - login-max-fail-times: 5 - # 连续登录失败锁定时间(单位:秒),-1表示不锁定,建议锁定30分钟 - login-fail-locked-seconds: 1800 - # 密码复杂度开启(默认复杂度为:至少三种字符,最小 8 位), true 开启,false 不开启,建议开启 - password-complexity-enabled: true - -sa-token: - # token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结 - active-timeout: 1800 - # token 有效期(单位:秒) 1天(86400秒),-1 代表永久有效 - timeout: 86400 \ No newline at end of file + active: '@profiles.active@' \ No newline at end of file diff --git a/smart-admin-api/sa-admin/src/main/resources/mapper/business/goods/GoodsMapper.xml b/smart-admin-api/sa-admin/src/main/resources/mapper/business/goods/GoodsMapper.xml index 7fcadccb..05316d83 100644 --- a/smart-admin-api/sa-admin/src/main/resources/mapper/business/goods/GoodsMapper.xml +++ b/smart-admin-api/sa-admin/src/main/resources/mapper/business/goods/GoodsMapper.xml @@ -18,7 +18,7 @@ INSTR(goods_name,#{query.searchWord}) - AND place = #{query.place} + AND INSTR(place,#{query.place}) AND goods_status = #{query.goodsStatus} diff --git a/smart-admin-api/sa-admin/src/main/resources/mapper/business/notice/NoticeMapper.xml b/smart-admin-api/sa-admin/src/main/resources/mapper/business/notice/NoticeMapper.xml deleted file mode 100644 index 580bb9b4..00000000 --- a/smart-admin-api/sa-admin/src/main/resources/mapper/business/notice/NoticeMapper.xml +++ /dev/null @@ -1,69 +0,0 @@ - - - - - UPDATE t_notice - SET watch_amount = watch_amount + 1 - WHERE notice_id = #{noticeId} - - - UPDATE t_notice - SET deleted_flag = #{deletedFlag} - WHERE notice_id IN - - #{item} - - - - UPDATE t_notice - SET notice_type = #{noticeType}, - notice_belong_type = #{noticeBelongType}, - notice_title = #{noticeTitle}, - notice_content = #{noticeContent}, - link_address = #{linkAddress}, - cover_file_key = #{coverFileKey}, - accessory_file_keys = #{accessoryFileKeys}, - top_flag = #{topFlag}, - publish_time = #{publishTime}, - disabled_flag = #{disabledFlag} - WHERE notice_id = #{noticeId} - - - - \ No newline at end of file diff --git a/smart-admin-api/sa-admin/src/main/resources/mapper/business/oa/notice/NoticeDao.xml b/smart-admin-api/sa-admin/src/main/resources/mapper/business/oa/notice/NoticeMapper.xml similarity index 97% rename from smart-admin-api/sa-admin/src/main/resources/mapper/business/oa/notice/NoticeDao.xml rename to smart-admin-api/sa-admin/src/main/resources/mapper/business/oa/notice/NoticeMapper.xml index 7c623e33..b1dc8ad5 100644 --- a/smart-admin-api/sa-admin/src/main/resources/mapper/business/oa/notice/NoticeDao.xml +++ b/smart-admin-api/sa-admin/src/main/resources/mapper/business/oa/notice/NoticeMapper.xml @@ -242,5 +242,11 @@ where notice_id = #{noticeId} and employee_id = #{employeeId} + + update t_notice + set page_view_count = page_view_count + #{pageViewCountIncrement}, + user_view_count = user_view_count + #{userViewCountIncrement} + where notice_id = #{noticeId} + \ No newline at end of file diff --git a/smart-admin-api/sa-admin/src/main/resources/mapper/system/employee/EmployeeMapper.xml b/smart-admin-api/sa-admin/src/main/resources/mapper/system/employee/EmployeeMapper.xml index 24f5eebb..506c6832 100644 --- a/smart-admin-api/sa-admin/src/main/resources/mapper/system/employee/EmployeeMapper.xml +++ b/smart-admin-api/sa-admin/src/main/resources/mapper/system/employee/EmployeeMapper.xml @@ -85,7 +85,7 @@ diff --git a/smart-admin-api/sa-admin/src/main/resources/mapper/system/role/RoleEmployeeMapper.xml b/smart-admin-api/sa-admin/src/main/resources/mapper/system/role/RoleEmployeeMapper.xml index 9e0a1379..390a19c8 100644 --- a/smart-admin-api/sa-admin/src/main/resources/mapper/system/role/RoleEmployeeMapper.xml +++ b/smart-admin-api/sa-admin/src/main/resources/mapper/system/role/RoleEmployeeMapper.xml @@ -138,4 +138,12 @@ #{item} + + + \ No newline at end of file diff --git a/smart-admin-api/sa-admin/src/main/resources/pre/application.yaml b/smart-admin-api/sa-admin/src/main/resources/pre/application.yaml index cbac6806..a56dcc58 100644 --- a/smart-admin-api/sa-admin/src/main/resources/pre/application.yaml +++ b/smart-admin-api/sa-admin/src/main/resources/pre/application.yaml @@ -19,27 +19,4 @@ server: # 环境 spring: profiles: - active: '@profiles.active@' - -####################################### 安全等级保护 相关配置 ################################################## -# # -# 建议开启 "三级等保" 所要求的配置,具体如下: # -# 1)连续登录失败 5 次锁定账户 30 分钟, # -# 2)登录超时时长建议为 30分钟,超过此时间没有访问系统会重新要求登录 # -# 3)密码复杂度至少三种字符,最小 8 位 # -# # -############################################################################################################# - -classified-protect: - # 连续登录失败次数则锁定,-1表示不受限制,可以一直尝试登录 - login-max-fail-times: 5 - # 连续登录失败锁定时间(单位:秒),-1表示不锁定,建议锁定30分钟 - login-fail-locked-seconds: 1800 - # 密码复杂度开启(默认复杂度为:至少三种字符,最小 8 位), true 开启,false 不开启,建议开启 - password-complexity-enabled: true - -sa-token: - # token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结 - active-timeout: 1800 - # token 有效期(单位:秒) 1天(86400秒),-1 代表永久有效 - timeout: 86400 \ No newline at end of file + active: '@profiles.active@' \ No newline at end of file diff --git a/smart-admin-api/sa-admin/src/main/resources/prod/application.yaml b/smart-admin-api/sa-admin/src/main/resources/prod/application.yaml index cbac6806..a56dcc58 100644 --- a/smart-admin-api/sa-admin/src/main/resources/prod/application.yaml +++ b/smart-admin-api/sa-admin/src/main/resources/prod/application.yaml @@ -19,27 +19,4 @@ server: # 环境 spring: profiles: - active: '@profiles.active@' - -####################################### 安全等级保护 相关配置 ################################################## -# # -# 建议开启 "三级等保" 所要求的配置,具体如下: # -# 1)连续登录失败 5 次锁定账户 30 分钟, # -# 2)登录超时时长建议为 30分钟,超过此时间没有访问系统会重新要求登录 # -# 3)密码复杂度至少三种字符,最小 8 位 # -# # -############################################################################################################# - -classified-protect: - # 连续登录失败次数则锁定,-1表示不受限制,可以一直尝试登录 - login-max-fail-times: 5 - # 连续登录失败锁定时间(单位:秒),-1表示不锁定,建议锁定30分钟 - login-fail-locked-seconds: 1800 - # 密码复杂度开启(默认复杂度为:至少三种字符,最小 8 位), true 开启,false 不开启,建议开启 - password-complexity-enabled: true - -sa-token: - # token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结 - active-timeout: 1800 - # token 有效期(单位:秒) 1天(86400秒),-1 代表永久有效 - timeout: 86400 \ No newline at end of file + active: '@profiles.active@' \ No newline at end of file diff --git a/smart-admin-api/sa-admin/src/main/resources/test/application.yaml b/smart-admin-api/sa-admin/src/main/resources/test/application.yaml index e6af9476..6ca92cf3 100644 --- a/smart-admin-api/sa-admin/src/main/resources/test/application.yaml +++ b/smart-admin-api/sa-admin/src/main/resources/test/application.yaml @@ -8,7 +8,7 @@ # 项目配置: 名称、日志目录 project: name: sa-admin - log-directory: /home/project/smartadmin/sit/log + log-directory: /home/project/smartadmin/test/log # 项目端口和url根路径 server: @@ -19,27 +19,4 @@ server: # 环境 spring: profiles: - active: '@profiles.active@' - -####################################### 安全等级保护 相关配置 ################################################## -# # -# 建议开启 "三级等保" 所要求的配置,具体如下: # -# 1)连续登录失败 5 次锁定账户 30 分钟, # -# 2)登录超时时长建议为 30分钟,超过此时间没有访问系统会重新要求登录 # -# 3)密码复杂度至少三种字符,最小 8 位 # -# # -############################################################################################################# - -classified-protect: - # 连续登录失败次数则锁定,-1表示不受限制,可以一直尝试登录 - login-max-fail-times: 5 - # 连续登录失败锁定时间(单位:秒),-1表示不锁定,建议锁定30分钟 - login-fail-locked-seconds: 1800 - # 密码复杂度开启(默认复杂度为:至少三种字符,最小 8 位), true 开启,false 不开启,建议开启 - password-complexity-enabled: true - -sa-token: - # token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结 - active-timeout: 1800 - # token 有效期(单位:秒) 1天(86400秒),-1 代表永久有效 - timeout: 86400 \ No newline at end of file + active: '@profiles.active@' \ No newline at end of file diff --git a/smart-admin-api/sa-base/pom.xml b/smart-admin-api/sa-base/pom.xml index 870fb9d8..0f76c8fb 100644 --- a/smart-admin-api/sa-base/pom.xml +++ b/smart-admin-api/sa-base/pom.xml @@ -88,8 +88,8 @@ - mysql - mysql-connector-java + com.mysql + mysql-connector-j @@ -263,6 +263,11 @@ jackson-datatype-jsr310 + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + + net.1024lab smartdb @@ -278,6 +283,27 @@ redisson-spring-data-27 + + org.yaml + snakeyaml + + + + org.springframework.boot + spring-boot-starter-mail + + + + org.jsoup + jsoup + + + + org.freemarker + freemarker + + + diff --git a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/common/json/deserializer/DictValueVoDeserializer.java b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/common/json/deserializer/DictValueVoDeserializer.java index 86eba416..7631db6a 100644 --- a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/common/json/deserializer/DictValueVoDeserializer.java +++ b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/common/json/deserializer/DictValueVoDeserializer.java @@ -28,19 +28,19 @@ public class DictValueVoDeserializer extends JsonDeserializer { @Override public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException { - List list = new ArrayList<>(); + List list = new ArrayList<>(); ObjectCodec objectCodec = jsonParser.getCodec(); JsonNode listOrObjectNode = objectCodec.readTree(jsonParser); String deserialize = ""; try { if (listOrObjectNode.isArray()) { for (JsonNode node : listOrObjectNode) { - list.add(objectCodec.treeToValue(node, DictValueVO.class)); + list.add(node.asText()); } } else { - list.add(objectCodec.treeToValue(listOrObjectNode, DictValueVO.class)); + list.add(listOrObjectNode.asText()); } - deserialize = list.stream().map(DictValueVO::getValueCode).collect(Collectors.joining(",")); + deserialize = String.join(",", list); } catch (Exception e) { log.error(e.getMessage(), e); deserialize = listOrObjectNode.asText(); diff --git a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/common/json/serializer/DataMaskingSerializer.java b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/common/json/serializer/DataMaskingSerializer.java new file mode 100644 index 00000000..ffe7c84e --- /dev/null +++ b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/common/json/serializer/DataMaskingSerializer.java @@ -0,0 +1,59 @@ +package net.lab1024.sa.base.common.json.serializer; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.ContextualSerializer; +import net.lab1024.sa.base.module.support.datamasking.DataMasking; +import net.lab1024.sa.base.module.support.datamasking.DataMaskingTypeEnum; +import net.lab1024.sa.base.module.support.datamasking.SmartDataMaskingUtil; +import org.apache.commons.lang3.ObjectUtils; + +import java.io.IOException; + +/** + * 脱敏序列化 + * + * @author 罗伊 + * @description: + * @date 2024/7/21 4:39 下午 + */ +public class DataMaskingSerializer extends JsonSerializer implements ContextualSerializer { + + private DataMaskingTypeEnum typeEnum; + + @Override + public void serialize(Object value, JsonGenerator jsonGenerator, SerializerProvider serializers) throws IOException { + + if (ObjectUtils.isEmpty(value)) { + jsonGenerator.writeObject(value); + return; + } + + if (typeEnum == null) { + jsonGenerator.writeObject(SmartDataMaskingUtil.dataMasking(String.valueOf(value))); + return; + } + + jsonGenerator.writeObject(SmartDataMaskingUtil.dataMasking(value, typeEnum)); + } + + @Override + public JsonSerializer createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException { + // 判断beanProperty是不是空 + if (null == property) { + return prov.findNullValueSerializer(property); + } + + DataMasking annotation = property.getAnnotation(DataMasking.class); + if (null == annotation) { + return prov.findValueSerializer(property.getType(), property); + } + + typeEnum = annotation.value(); + return this; + } + +} \ No newline at end of file diff --git a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/common/util/SmartPageUtil.java b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/common/util/SmartPageUtil.java index 4e2f24c0..9dca8487 100644 --- a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/common/util/SmartPageUtil.java +++ b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/common/util/SmartPageUtil.java @@ -12,7 +12,6 @@ import java.util.ArrayList; import java.util.List; -import java.util.stream.Collectors; /** * 分页工具类 @@ -32,6 +31,10 @@ public class SmartPageUtil { public static Page convert2PageQuery(PageParam pageParam) { Page page = new Page<>(pageParam.getPageNum(), pageParam.getPageSize()); + if (pageParam.getSearchCount() != null) { + page.setSearchCount(pageParam.getSearchCount()); + } + List sortItemList = pageParam.getSortItemList(); if (CollectionUtils.isEmpty(sortItemList)) { return page; diff --git a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/config/DataSourceConfig.java b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/config/DataSourceConfig.java index 4fb6945e..f1eea694 100644 --- a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/config/DataSourceConfig.java +++ b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/config/DataSourceConfig.java @@ -7,10 +7,12 @@ import com.alibaba.druid.support.http.WebStatFilter; import com.alibaba.druid.support.spring.stat.DruidStatInterceptor; import com.baomidou.mybatisplus.annotation.DbType; +import com.baomidou.mybatisplus.core.config.GlobalConfig; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean; import lombok.extern.slf4j.Slf4j; import net.lab1024.sa.base.common.domain.DataScopePlugin; +import net.lab1024.sa.base.handler.MybatisPlusFillHandler; import org.apache.ibatis.plugin.Interceptor; import org.apache.ibatis.session.SqlSessionFactory; import org.springframework.aop.support.DefaultPointcutAdvisor; @@ -144,6 +146,8 @@ public SqlSessionFactory sqlSessionFactory() throws Exception { pluginsList.add(dataScopePlugin); } factoryBean.setPlugins(pluginsList.toArray(new Interceptor[pluginsList.size()])); + // 添加字段自动填充处理 + factoryBean.setGlobalConfig(new GlobalConfig().setBanner(false).setMetaObjectHandler(new MybatisPlusFillHandler())); return factoryBean.getObject(); } diff --git a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/config/RedisConfig.java b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/config/RedisConfig.java index f6616e46..24a14773 100644 --- a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/config/RedisConfig.java +++ b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/config/RedisConfig.java @@ -6,6 +6,7 @@ import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -43,7 +44,8 @@ public RedisTemplate redisTemplate() { .setSerializationInclusion(JsonInclude.Include.NON_NULL); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); - om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); + // enableDefaultTyping 官方已弃用 所以改为 activateDefaultTyping + om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); RedisTemplate template = new RedisTemplate<>(); template.setConnectionFactory(factory); diff --git a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/config/SwaggerConfig.java b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/config/SwaggerConfig.java index c1f343d1..e980dba1 100644 --- a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/config/SwaggerConfig.java +++ b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/config/SwaggerConfig.java @@ -28,7 +28,7 @@ * springdoc-openapi 配置 * nginx配置前缀时如果需要访问【/swagger-ui/index.html】需添加额外nginx配置 * location /v3/api-docs/ { - * proxy_pass http://127.0.0.1:11024/v3/api-docs/; + * proxy_pass http://127.0.0.1:1024/v3/api-docs/; * } * @Author 1024创新实验室-主任: 卓大 * @Date 2020-03-25 22:54:46 @@ -43,7 +43,7 @@ public class SwaggerConfig { /** * 用于解决/swagger-ui/index.html页面ServersUrl 测试环境部署错误问题 */ - @Value("${springdoc.swagger-ui.server-base-url:''}") + @Value("${springdoc.swagger-ui.server-base-url}") private String serverBaseUrl; public static final String[] SWAGGER_WHITELIST = { diff --git a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/constant/RedisKeyConst.java b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/constant/RedisKeyConst.java index f4e70413..29733c93 100644 --- a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/constant/RedisKeyConst.java +++ b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/constant/RedisKeyConst.java @@ -23,5 +23,7 @@ public static class Support { public static final String CAPTCHA = "captcha:"; + public static final String LOGIN_VERIFICATION_CODE = "login:verification-code:"; + } } diff --git a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/constant/SwaggerTagConst.java b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/constant/SwaggerTagConst.java index 8a9642d7..fa30161e 100644 --- a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/constant/SwaggerTagConst.java +++ b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/constant/SwaggerTagConst.java @@ -49,6 +49,8 @@ public static class Support { public static final String PROTECT = "业务支撑-网络安全"; + public static final String DATA_MASKING = "业务支撑-数据脱敏"; + public static final String JOB = "业务支撑-定时任务"; public static final String MESSAGE = "业务支撑-消息"; diff --git a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/handler/MybatisPlusFillHandler.java b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/handler/MybatisPlusFillHandler.java new file mode 100644 index 00000000..372ff757 --- /dev/null +++ b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/handler/MybatisPlusFillHandler.java @@ -0,0 +1,40 @@ +package net.lab1024.sa.base.handler; + +import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.reflection.MetaObject; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; + +/** + * Mybatis Plus 插入或者更新时指定字段设置值 + * + * @author zhoumingfa + */ +@Component +@Slf4j +public class MybatisPlusFillHandler implements MetaObjectHandler { + + public static final String CREATE_TIME = "createTime"; + + public static final String UPDATE_TIME = "updateTime"; + + @Override + public void insertFill(MetaObject metaObject) { + if (metaObject.hasSetter(CREATE_TIME)) { + this.fillStrategy(metaObject, CREATE_TIME, LocalDateTime.now()); + } + if (metaObject.hasSetter(UPDATE_TIME)) { + this.fillStrategy(metaObject, UPDATE_TIME, LocalDateTime.now()); + } + } + + @Override + public void updateFill(MetaObject metaObject) { + if (metaObject.hasSetter(UPDATE_TIME)) { + this.fillStrategy(metaObject, UPDATE_TIME, LocalDateTime.now()); + } + } + +} diff --git a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/captcha/CaptchaService.java b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/captcha/CaptchaService.java index b1850925..829135b7 100644 --- a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/captcha/CaptchaService.java +++ b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/captcha/CaptchaService.java @@ -42,8 +42,10 @@ public class CaptchaService { @Resource private DefaultKaptcha defaultKaptcha; - @Autowired + + @Resource private SystemEnvironment systemEnvironment; + @Resource private RedisService redisService; diff --git a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/constant/CodeFrontComponentEnum.java b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/constant/CodeFrontComponentEnum.java index 5358806c..10c8779e 100644 --- a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/constant/CodeFrontComponentEnum.java +++ b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/constant/CodeFrontComponentEnum.java @@ -41,7 +41,7 @@ public enum CodeFrontComponentEnum implements BaseEnum { } @Override - public Object getValue() { + public String getValue() { return value; } diff --git a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/domain/form/CodeGeneratorConfigForm.java b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/domain/form/CodeGeneratorConfigForm.java index 10170b00..4e3c0398 100644 --- a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/domain/form/CodeGeneratorConfigForm.java +++ b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/domain/form/CodeGeneratorConfigForm.java @@ -32,7 +32,6 @@ public class CodeGeneratorConfigForm { @Schema(description = "表名") private String tableName; - @Valid @NotNull(message = "基础信息不能为空") @Schema(description = "基础信息") diff --git a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/domain/model/CodeInsertAndUpdateField.java b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/domain/model/CodeInsertAndUpdateField.java index db623864..5e83fd5d 100644 --- a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/domain/model/CodeInsertAndUpdateField.java +++ b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/domain/model/CodeInsertAndUpdateField.java @@ -39,8 +39,8 @@ public class CodeInsertAndUpdateField { @Schema(description = "更新标识") private Boolean updateFlag; - @SchemaEnum(value = CodeGeneratorPageTypeEnum.class) - @CheckEnum(value = CodeFrontComponentEnum.class, message = "3.增加、修改 增加、修改 组件类型 枚举值错误", required = true) + @SchemaEnum(value = CodeFrontComponentEnum.class) + @CheckEnum(value = CodeFrontComponentEnum.class, message = "3.增加、修改 组件类型 枚举值错误", required = true) private String frontComponent; } diff --git a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/service/CodeGeneratorTemplateService.java b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/service/CodeGeneratorTemplateService.java index 5161f0bb..5a86305c 100644 --- a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/service/CodeGeneratorTemplateService.java +++ b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/service/CodeGeneratorTemplateService.java @@ -13,16 +13,13 @@ import net.lab1024.sa.base.module.support.codegenerator.domain.entity.CodeGeneratorConfigEntity; import net.lab1024.sa.base.module.support.codegenerator.domain.form.CodeGeneratorConfigForm; import net.lab1024.sa.base.module.support.codegenerator.domain.model.*; -import net.lab1024.sa.base.module.support.codegenerator.service.variable.backend.ControllerVariableService; -import net.lab1024.sa.base.module.support.codegenerator.service.variable.backend.DaoVariableService; -import net.lab1024.sa.base.module.support.codegenerator.service.variable.backend.ManagerVariableService; -import net.lab1024.sa.base.module.support.codegenerator.service.variable.backend.ServiceVariableService; +import net.lab1024.sa.base.module.support.codegenerator.service.variable.CodeGenerateBaseVariableService; +import net.lab1024.sa.base.module.support.codegenerator.service.variable.backend.*; import net.lab1024.sa.base.module.support.codegenerator.service.variable.backend.domain.*; import net.lab1024.sa.base.module.support.codegenerator.service.variable.front.ApiVariableService; import net.lab1024.sa.base.module.support.codegenerator.service.variable.front.ConstVariableService; import net.lab1024.sa.base.module.support.codegenerator.service.variable.front.FormVariableService; import net.lab1024.sa.base.module.support.codegenerator.service.variable.front.ListVariableService; -import net.lab1024.sa.base.module.support.codegenerator.service.variable.CodeGenerateBaseVariableService; import net.lab1024.sa.base.module.support.codegenerator.util.CodeGeneratorTool; import org.apache.commons.collections4.CollectionUtils; import org.apache.velocity.Template; @@ -36,7 +33,7 @@ import java.io.File; import java.io.OutputStream; import java.io.StringWriter; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.*; import java.util.stream.Collectors; @@ -47,7 +44,7 @@ * @Date 2022-06-30 22:15:38 * @Wechat zhuoda1024 * @Email lab1024@163.com - * @Copyright 1024创新实验室 + * @Copyright 1024创新实验室 */ @Service @@ -70,6 +67,8 @@ public void init() { map.put("java/manager/Manager.java", new ManagerVariableService()); map.put("java/dao/Dao.java", new DaoVariableService()); map.put("java/mapper/Mapper.xml", new MapperVariableService()); + // 菜单 SQL + map.put("java/sql/Menu.sql", new MenuVariableService()); // 前端 map.put("js/api.js", new ApiVariableService()); map.put("js/const.js", new ConstVariableService()); @@ -94,6 +93,7 @@ public void zipGeneratedFiles(OutputStream outputStream, String tableName, CodeG String fileName = templateFile.startsWith("java") ? upperCamel + templateSplit[templateSplit.length - 1] : lowerHyphen + "-" + templateSplit[templateSplit.length - 1]; String fullPathFileName = templateFile.replaceAll(templateSplit[templateSplit.length - 1], fileName); fullPathFileName = fullPathFileName.replaceAll("java/", "java/" + basic.getModuleName().toLowerCase() + "/"); + fullPathFileName = fullPathFileName.replaceAll("js/", "js/" + lowerHyphen + "/"); String fileContent = generate(tableName, templateFile, codeGeneratorConfigEntity); File file = new File(uuid + "/" + fullPathFileName); @@ -129,7 +129,7 @@ public void zipGeneratedFiles(OutputStream outputStream, String tableName, CodeG } - ZipUtil.zip(outputStream, Charset.forName("utf-8"), false, null, dir); + ZipUtil.zip(outputStream, StandardCharsets.UTF_8, false, null, dir); FileUtil.del(dir); diff --git a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/service/variable/CodeGenerateBaseVariableService.java b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/service/variable/CodeGenerateBaseVariableService.java index 9602aeae..d39bf6bf 100644 --- a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/service/variable/CodeGenerateBaseVariableService.java +++ b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/service/variable/CodeGenerateBaseVariableService.java @@ -2,6 +2,7 @@ import com.google.common.base.CaseFormat; import net.lab1024.sa.base.common.util.SmartStringUtil; +import net.lab1024.sa.base.module.support.codegenerator.constant.CodeFrontComponentEnum; import net.lab1024.sa.base.module.support.codegenerator.domain.form.CodeGeneratorConfigForm; import net.lab1024.sa.base.module.support.codegenerator.domain.model.CodeField; import net.lab1024.sa.base.module.support.codegenerator.domain.model.CodeInsertAndUpdate; @@ -17,7 +18,7 @@ * @Date 2022/9/29 17:20:41 * @Wechat zhuoda1024 * @Email lab1024@163.com - * @Copyright 1024创新实验室 + * @Copyright 1024创新实验室 */ public abstract class CodeGenerateBaseVariableService { @@ -43,13 +44,13 @@ public List getJavaBeanImportClass(CodeGeneratorConfigForm form) { String upperCamelName = CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_CAMEL, form.getBasic().getModuleName()); ArrayList list = new ArrayList<>(); - list.add("import " + form.getBasic().getJavaPackageName() + ".domain.entity." + upperCamelName + "Entity;" ); + list.add("import " + form.getBasic().getJavaPackageName() + ".domain.entity." + upperCamelName + "Entity;"); - list.add("import " + form.getBasic().getJavaPackageName() + ".domain.form." + upperCamelName + "AddForm;" ); - list.add("import " + form.getBasic().getJavaPackageName() + ".domain.form." + upperCamelName + "UpdateForm;" ); - list.add("import " + form.getBasic().getJavaPackageName() + ".domain.form." + upperCamelName + "QueryForm;" ); + list.add("import " + form.getBasic().getJavaPackageName() + ".domain.form." + upperCamelName + "AddForm;"); + list.add("import " + form.getBasic().getJavaPackageName() + ".domain.form." + upperCamelName + "UpdateForm;"); + list.add("import " + form.getBasic().getJavaPackageName() + ".domain.form." + upperCamelName + "QueryForm;"); - list.add("import " + form.getBasic().getJavaPackageName() + ".domain.vo." + upperCamelName + "VO;" ); + list.add("import " + form.getBasic().getJavaPackageName() + ".domain.vo." + upperCamelName + "VO;"); return list; } @@ -88,43 +89,33 @@ protected boolean isFile(String columnName, CodeGeneratorConfigForm form) { } CodeInsertAndUpdateField field = first.get(); - return SmartStringUtil.contains(field.getFrontComponent(), "Upload" ); + return SmartStringUtil.equals(field.getFrontComponent(), CodeFrontComponentEnum.FILE_UPLOAD.getValue()); } /** - * 是否为 枚举 + * 是否为 字典 */ protected boolean isDict(String columnName, CodeGeneratorConfigForm form) { - List fields = form.getFields(); - if (CollectionUtils.isEmpty(fields)) { - return false; - } - - Optional first = fields.stream().filter(e -> columnName.equals(e.getColumnName())).findFirst(); - if (first.isPresent()) { - return false; - } - - CodeField codeField = first.get(); - return codeField.getDict() != null; + CodeField codeField = getCodeField(columnName, form); + return codeField != null && codeField.getDict() != null; } /** * 是否为 枚举 */ protected boolean isEnum(String columnName, CodeGeneratorConfigForm form) { + CodeField codeField = getCodeField(columnName, form); + return codeField != null && codeField.getEnumName() != null; + } + + private CodeField getCodeField(String columnName, CodeGeneratorConfigForm form) { List fields = form.getFields(); if (CollectionUtils.isEmpty(fields)) { - return false; + return null; } Optional first = fields.stream().filter(e -> columnName.equals(e.getColumnName())).findFirst(); - if (first.isPresent()) { - return false; - } - - CodeField codeField = first.get(); - return codeField.getEnumName() != null; + return first.orElse(null); } /** diff --git a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/service/variable/backend/MenuVariableService.java b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/service/variable/backend/MenuVariableService.java new file mode 100644 index 00000000..17ab19f2 --- /dev/null +++ b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/service/variable/backend/MenuVariableService.java @@ -0,0 +1,27 @@ +package net.lab1024.sa.base.module.support.codegenerator.service.variable.backend; + +import net.lab1024.sa.base.module.support.codegenerator.domain.form.CodeGeneratorConfigForm; +import net.lab1024.sa.base.module.support.codegenerator.service.variable.CodeGenerateBaseVariableService; + +import java.util.HashMap; +import java.util.Map; + +/** + * 目前暂时没用到 这是一个空实现 + * + * @author zhoumingfa + * @date 2024/8/13 + */ +public class MenuVariableService extends CodeGenerateBaseVariableService { + + @Override + public boolean isSupport(CodeGeneratorConfigForm form) { + return true; + } + + @Override + public Map getInjectVariablesMap(CodeGeneratorConfigForm form) { + return new HashMap<>(2); + } + +} diff --git a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/service/variable/backend/domain/AddFormVariableService.java b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/service/variable/backend/domain/AddFormVariableService.java index 0d2e13fc..1d29259a 100644 --- a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/service/variable/backend/domain/AddFormVariableService.java +++ b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/service/variable/backend/domain/AddFormVariableService.java @@ -2,6 +2,7 @@ import cn.hutool.core.bean.BeanUtil; import net.lab1024.sa.base.common.util.SmartStringUtil; +import net.lab1024.sa.base.module.support.codegenerator.constant.CodeFrontComponentEnum; import net.lab1024.sa.base.module.support.codegenerator.domain.form.CodeGeneratorConfigForm; import net.lab1024.sa.base.module.support.codegenerator.domain.model.CodeField; import net.lab1024.sa.base.module.support.codegenerator.domain.model.CodeInsertAndUpdate; @@ -100,7 +101,6 @@ public ImmutablePair, List>> getPackageListAndF } } - //字典 if (SmartStringUtil.isNotEmpty(codeField.getDict())) { finalFieldMap.put("dict", "\n @JsonDeserialize(using = DictValueVoDeserializer.class)"); @@ -109,7 +109,7 @@ public ImmutablePair, List>> getPackageListAndF } //文件上传 - if (SmartStringUtil.contains(field.getFrontComponent(), "Upload")) { + if (CodeFrontComponentEnum.FILE_UPLOAD.equalsValue(field.getFrontComponent())) { finalFieldMap.put("file", "\n @JsonDeserialize(using = FileKeyVoDeserializer.class)"); packageList.add("import com.fasterxml.jackson.databind.annotation.JsonDeserialize;"); packageList.add("import net.lab1024.sa.base.common.json.deserializer.FileKeyVoDeserializer;"); diff --git a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/service/variable/backend/domain/EntityVariableService.java b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/service/variable/backend/domain/EntityVariableService.java index 59a3fa86..391bffa4 100644 --- a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/service/variable/backend/domain/EntityVariableService.java +++ b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/service/variable/backend/domain/EntityVariableService.java @@ -14,7 +14,7 @@ * @Date 2022/9/29 17:20:41 * @Wechat zhuoda1024 * @Email lab1024@163.com - * @Copyright 1024创新实验室 + * @Copyright 1024创新实验室 */ public class EntityVariableService extends CodeGenerateBaseVariableService { @@ -54,14 +54,21 @@ public List getImportPackageList(List fields) { // mybatis plus result.add("import com.baomidou.mybatisplus.annotation.TableName;"); + // 自动填充注解 + boolean existCreateAndUpdate = fields.stream().anyMatch(e -> "create_time".equals(e.getColumnName()) || "update_time".equals(e.getColumnName())); + if (existCreateAndUpdate) { + result.add("import com.baomidou.mybatisplus.annotation.FieldFill;"); + result.add("import com.baomidou.mybatisplus.annotation.TableField;"); + } + //主键 - boolean isExistPrimaryKey = fields.stream().filter(e -> e.getPrimaryKeyFlag() != null && e.getPrimaryKeyFlag()).findFirst().isPresent(); + boolean isExistPrimaryKey = fields.stream().anyMatch(e -> e.getPrimaryKeyFlag() != null && e.getPrimaryKeyFlag()); if (isExistPrimaryKey) { result.add("import com.baomidou.mybatisplus.annotation.TableId;"); } //自增 - boolean isExistAutoIncrease = fields.stream().filter(e -> e.getAutoIncreaseFlag() != null && e.getAutoIncreaseFlag()).findFirst().isPresent(); + boolean isExistAutoIncrease = fields.stream().anyMatch(e -> e.getAutoIncreaseFlag() != null && e.getAutoIncreaseFlag()); if (isExistAutoIncrease) { result.add("import com.baomidou.mybatisplus.annotation.IdType;"); } diff --git a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/service/variable/backend/domain/MapperVariableService.java b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/service/variable/backend/domain/MapperVariableService.java index 1be48fc6..14f8ff5f 100644 --- a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/service/variable/backend/domain/MapperVariableService.java +++ b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/service/variable/backend/domain/MapperVariableService.java @@ -3,19 +3,20 @@ import cn.hutool.core.bean.BeanUtil; import net.lab1024.sa.base.module.support.codegenerator.constant.CodeQueryFieldQueryTypeEnum; import net.lab1024.sa.base.module.support.codegenerator.domain.form.CodeGeneratorConfigForm; -import net.lab1024.sa.base.module.support.codegenerator.domain.model.CodeInsertAndUpdateField; import net.lab1024.sa.base.module.support.codegenerator.domain.model.CodeQueryField; import net.lab1024.sa.base.module.support.codegenerator.service.variable.CodeGenerateBaseVariableService; -import org.apache.commons.collections4.CollectionUtils; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; /** * @Author 1024创新实验室-主任:卓大 * @Date 2022/9/29 17:20:41 * @Wechat zhuoda1024 * @Email lab1024@163.com - * @Copyright 1024创新实验室 + * @Copyright 1024创新实验室 */ public class MapperVariableService extends CodeGenerateBaseVariableService { @@ -39,55 +40,47 @@ public Map getInjectVariablesMap(CodeGeneratorConfigForm form) { List columnNameList = queryField.getColumnNameList(); if (columnNameList.size() == 1) { // AND INSTR(t_notice.title,#{query.keywords}) - stringBuilder.append(" AND INSTR(" ) - .append(form.getTableName()).append("." ).append(queryField.getColumnNameList().get(0)) - .append(",#{queryForm." ) + stringBuilder.append(" AND INSTR(") + .append(form.getTableName()).append(".").append(queryField.getColumnNameList().get(0)) + .append(",#{queryForm.") .append(queryField.getFieldName()) - .append("})" ); + .append("})"); } else { for (int i = 0; i < columnNameList.size(); i++) { if (i == 0) { - stringBuilder.append("AND ( INSTR(" ) - .append(form.getTableName()).append("." ).append(queryField.getColumnNameList().get(i)) - .append(",#{queryForm." ) + stringBuilder.append("AND ( INSTR(") + .append(form.getTableName()).append(".").append(queryField.getColumnNameList().get(i)) + .append(",#{queryForm.") .append(queryField.getFieldName()) - .append("})" ); + .append("})"); } else { // OR INSTR(t_notice.author,#{query.keywords}) - stringBuilder.append("\n OR INSTR(" ) - .append(form.getTableName()).append("." ).append(queryField.getColumnNameList().get(i)) - .append(",#{queryForm." ) + stringBuilder.append("\n OR INSTR(") + .append(form.getTableName()).append(".").append(queryField.getColumnNameList().get(i)) + .append(",#{queryForm.") .append(queryField.getFieldName()) - .append("})" ); + .append("})"); } } - stringBuilder.append("\n )" ); + stringBuilder.append("\n )"); } fieldMap.put("likeStr", stringBuilder.toString()); - }else{ - fieldMap.put("columnName",queryField.getColumnNameList().get(0)); + } else if (CodeQueryFieldQueryTypeEnum.DICT.equalsValue(queryField.getQueryTypeEnum())) { + String stringBuilder = "AND INSTR(" + + form.getTableName() + "." + queryField.getColumnNameList().get(0) + + ",#{queryForm." + + queryField.getFieldName() + + "})"; + fieldMap.put("likeStr", stringBuilder); + } + else { + fieldMap.put("columnName", queryField.getColumnNameList().get(0)); } } variablesMap.put("queryFields", finalQueryFiledList); - variablesMap.put("daoClassName", form.getBasic().getJavaPackageName() + ".dao." + form.getBasic().getModuleName() + "Dao" ); + variablesMap.put("daoClassName", form.getBasic().getJavaPackageName() + ".dao." + form.getBasic().getModuleName() + "Dao"); return variablesMap; } - - public List getPackageList(List fields, CodeGeneratorConfigForm form) { - if (CollectionUtils.isEmpty(fields)) { - return new ArrayList<>(); - } - - HashSet packageList = new HashSet<>(); - - //1、javabean相关的包 - packageList.addAll(getJavaBeanImportClass(form)); - - //2、dao - packageList.add("import " + form.getBasic().getJavaPackageName() + ".dao." + form.getBasic().getModuleName() + "Dao;" ); - return new ArrayList<>(packageList); - } - } diff --git a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/service/variable/backend/domain/QueryFormVariableService.java b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/service/variable/backend/domain/QueryFormVariableService.java index 2c513790..86780e0a 100644 --- a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/service/variable/backend/domain/QueryFormVariableService.java +++ b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/service/variable/backend/domain/QueryFormVariableService.java @@ -2,6 +2,7 @@ import cn.hutool.core.bean.BeanUtil; import net.lab1024.sa.base.common.util.SmartEnumUtil; +import net.lab1024.sa.base.common.util.SmartStringUtil; import net.lab1024.sa.base.module.support.codegenerator.constant.CodeQueryFieldQueryTypeEnum; import net.lab1024.sa.base.module.support.codegenerator.domain.form.CodeGeneratorConfigForm; import net.lab1024.sa.base.module.support.codegenerator.domain.model.CodeField; @@ -18,7 +19,7 @@ * @Date 2022/9/29 17:20:41 * @Wechat zhuoda1024 * @Email lab1024@163.com - * @Copyright 1024创新实验室 + * @Copyright 1024创新实验室 */ public class QueryFormVariableService extends CodeGenerateBaseVariableService { @@ -41,14 +42,11 @@ public Map getInjectVariablesMap(CodeGeneratorConfigForm form) { public ImmutablePair, List>> getPackageListAndFields(CodeGeneratorConfigForm form) { + List fields = form.getQueryFields(); - if (CollectionUtils.isEmpty(fields)) { - return ImmutablePair.of(new ArrayList<>(), new ArrayList<>()); - } HashSet packageList = new HashSet<>(); - /** * 1、LocalDate、LocalDateTime、BigDecimal 类型的包名 * 2、排序 @@ -75,9 +73,6 @@ public ImmutablePair, List>> getPackageListAndF CodeField codeField = null; switch (queryTypeEnum) { - case LIKE: - finalFieldMap.put("javaType", "String"); - break; case EQUAL: codeField = getCodeFieldByColumnName(field.getColumnNameList().get(0), form); if (codeField == null) { @@ -109,6 +104,14 @@ public ImmutablePair, List>> getPackageListAndF finalFieldMap.put("javaType", codeField.getJavaType()); break; + case DICT: + codeField = getCodeFieldByColumnName(field.getColumnNameList().get(0), form); + if (SmartStringUtil.isNotEmpty(codeField.getDict())) { + finalFieldMap.put("dict", "\n @JsonDeserialize(using = DictValueVoDeserializer.class)"); + packageList.add("import com.fasterxml.jackson.databind.annotation.JsonDeserialize;"); + packageList.add("import net.lab1024.sa.base.common.json.deserializer.DictValueVoDeserializer;"); + } + finalFieldMap.put("javaType", "String"); default: finalFieldMap.put("javaType", "String"); } @@ -116,12 +119,11 @@ public ImmutablePair, List>> getPackageListAndF finalFieldList.add(finalFieldMap); } - // lombok packageList.add("import lombok.Data;"); + packageList.add("import lombok.EqualsAndHashCode;"); - List packageNameList = packageList.stream().filter(Objects::nonNull).collect(Collectors.toList()); - Collections.sort(packageNameList); + List packageNameList = packageList.stream().filter(Objects::nonNull).sorted().collect(Collectors.toList()); return ImmutablePair.of(packageNameList, finalFieldList); } diff --git a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/service/variable/backend/domain/UpdateFormVariableService.java b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/service/variable/backend/domain/UpdateFormVariableService.java index 89bb5a18..004e7670 100644 --- a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/service/variable/backend/domain/UpdateFormVariableService.java +++ b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/service/variable/backend/domain/UpdateFormVariableService.java @@ -2,6 +2,7 @@ import cn.hutool.core.bean.BeanUtil; import net.lab1024.sa.base.common.util.SmartStringUtil; +import net.lab1024.sa.base.module.support.codegenerator.constant.CodeFrontComponentEnum; import net.lab1024.sa.base.module.support.codegenerator.domain.form.CodeGeneratorConfigForm; import net.lab1024.sa.base.module.support.codegenerator.domain.model.CodeField; import net.lab1024.sa.base.module.support.codegenerator.domain.model.CodeInsertAndUpdate; @@ -18,7 +19,7 @@ * @Date 2022/9/29 17:20:41 * @Wechat zhuoda1024 * @Email lab1024@163.com - * @Copyright 1024创新实验室 + * @Copyright 1024创新实验室 */ public class UpdateFormVariableService extends CodeGenerateBaseVariableService { @@ -42,7 +43,7 @@ public Map getInjectVariablesMap(CodeGeneratorConfigForm form) { return false; } - if(Boolean.TRUE.equals(codeField.getPrimaryKeyFlag())){ + if (Boolean.TRUE.equals(codeField.getPrimaryKeyFlag())) { e.setRequiredFlag(true); } @@ -123,7 +124,7 @@ public ImmutablePair, List>> getPackageListAndF } //文件上传 - if (SmartStringUtil.contains(field.getFrontComponent(), "Upload")) { + if (CodeFrontComponentEnum.FILE_UPLOAD.equalsValue(field.getFrontComponent())) { finalFieldMap.put("file", "\n @JsonDeserialize(using = FileKeyVoDeserializer.class)"); packageList.add("import com.fasterxml.jackson.databind.annotation.JsonDeserialize;"); packageList.add("import net.lab1024.sa.base.common.json.deserializer.FileKeyVoDeserializer;"); diff --git a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/service/variable/backend/domain/VOVariableService.java b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/service/variable/backend/domain/VOVariableService.java index fceef52a..cde09227 100644 --- a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/service/variable/backend/domain/VOVariableService.java +++ b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/service/variable/backend/domain/VOVariableService.java @@ -17,7 +17,7 @@ * @Date 2022/9/29 17:20:41 * @Wechat zhuoda1024 * @Email lab1024@163.com - * @Copyright 1024创新实验室 + * @Copyright 1024创新实验室 */ public class VOVariableService extends CodeGenerateBaseVariableService { @@ -87,16 +87,16 @@ public ImmutablePair, List>> getPackageListAndF //字典 if (isDict(field.getColumnName(), form)) { - finalFieldMap.put("dict", "\n @JsonDeserialize(using = DictValueVoDeserializer.class)"); - packageList.add("import com.fasterxml.jackson.databind.annotation.JsonDeserialize;"); - packageList.add("import net.lab1024.sa.base.common.json.deserializer.DictValueVoDeserializer;"); + finalFieldMap.put("dict", "\n @JsonSerialize(using = DictValueVoSerializer.class)"); + packageList.add("import com.fasterxml.jackson.databind.annotation.JsonSerialize;"); + packageList.add("import net.lab1024.sa.base.common.json.serializer.DictValueVoSerializer;"); } //文件上传 if (isFile(field.getColumnName(), form)) { - finalFieldMap.put("file", "\n @JsonDeserialize(using = FileKeyVoDeserializer.class)"); - packageList.add("import com.fasterxml.jackson.databind.annotation.JsonDeserialize;"); - packageList.add("import net.lab1024.sa.base.common.json.deserializer.FileKeyVoDeserializer;"); + finalFieldMap.put("file", "\n @JsonSerialize(using = FileKeyVoSerializer.class)"); + packageList.add("import com.fasterxml.jackson.databind.annotation.JsonSerialize;"); + packageList.add("import net.lab1024.sa.base.common.json.serializer.FileKeyVoSerializer;"); } packageList.add(getJavaPackageName(codeField.getJavaType())); diff --git a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/service/variable/front/FormVariableService.java b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/service/variable/front/FormVariableService.java index 58ee1f0b..bdd4363f 100644 --- a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/service/variable/front/FormVariableService.java +++ b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/service/variable/front/FormVariableService.java @@ -57,19 +57,20 @@ public Map getInjectVariablesMap(CodeGeneratorConfigForm form) { fieldsVariableList.add(objectMap); - if (CodeFrontComponentEnum.ENUM_SELECT.getValue().equals(field.getFrontComponent())) { + if (CodeFrontComponentEnum.ENUM_SELECT.equalsValue(field.getFrontComponent())) { frontImportSet.add("import SmartEnumSelect from '/@/components/framework/smart-enum-select/index.vue';"); } - if (CodeFrontComponentEnum.BOOLEAN_SELECT.getValue().equals(field.getFrontComponent())) { + if (CodeFrontComponentEnum.BOOLEAN_SELECT.equalsValue(field.getFrontComponent())) { frontImportSet.add("import BooleanSelect from '/@/components/framework/boolean-select/index.vue';"); } - if (CodeFrontComponentEnum.DICT_SELECT.getValue().equals(field.getFrontComponent())) { + if (CodeFrontComponentEnum.DICT_SELECT.equalsValue(field.getFrontComponent())) { frontImportSet.add("import DictSelect from '/@/components/support/dict-select/index.vue';"); } - if (CodeFrontComponentEnum.FILE_UPLOAD.getValue().equals(field.getFrontComponent())) { + if (CodeFrontComponentEnum.FILE_UPLOAD.equalsValue(field.getFrontComponent())) { + frontImportSet.add("import { FILE_FOLDER_TYPE_ENUM } from '/@/constants/support/file-const';"); frontImportSet.add("import FileUpload from '/@/components/support/file-upload/index.vue';"); } } diff --git a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/service/variable/front/ListVariableService.java b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/service/variable/front/ListVariableService.java index 170fb363..74e582ad 100644 --- a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/service/variable/front/ListVariableService.java +++ b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/codegenerator/service/variable/front/ListVariableService.java @@ -4,6 +4,7 @@ import com.google.common.base.CaseFormat; import net.lab1024.sa.base.module.support.codegenerator.constant.CodeQueryFieldQueryTypeEnum; import net.lab1024.sa.base.module.support.codegenerator.domain.form.CodeGeneratorConfigForm; +import net.lab1024.sa.base.module.support.codegenerator.domain.model.CodeField; import net.lab1024.sa.base.module.support.codegenerator.domain.model.CodeQueryField; import net.lab1024.sa.base.module.support.codegenerator.service.variable.CodeGenerateBaseVariableService; @@ -35,19 +36,23 @@ public Map getInjectVariablesMap(CodeGeneratorConfigForm form) { for (CodeQueryField queryField : queryFields) { Map objectMap = BeanUtil.beanToMap(queryField); - variableList.add(objectMap); - if("Enum".equals(queryField.getQueryTypeEnum())){ + CodeField codeField = getCodeFieldByColumnName(queryField.getColumnNameList().get(0), form); + objectMap.put("frontEnumName", codeField.getEnumName()); + objectMap.put("dict", codeField.getDict()); + + if(CodeQueryFieldQueryTypeEnum.ENUM.equalsValue(queryField.getQueryTypeEnum())){ frontImportSet.add("import SmartEnumSelect from '/@/components/framework/smart-enum-select/index.vue';"); } - if("Dict".equals(queryField.getQueryTypeEnum())){ + if(CodeQueryFieldQueryTypeEnum.DICT.equalsValue(queryField.getQueryTypeEnum())){ frontImportSet.add("import DictSelect from '/@/components/support/dict-select/index.vue';"); } - if(CodeQueryFieldQueryTypeEnum.DATE_RANGE.getValue().equals(queryField.getQueryTypeEnum())){ + if(CodeQueryFieldQueryTypeEnum.DATE_RANGE.equalsValue(queryField.getQueryTypeEnum())){ frontImportSet.add("import { defaultTimeRanges } from '/@/lib/default-time-ranges';"); } + variableList.add(objectMap); } variablesMap.put("queryFields",variableList); diff --git a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/config/ConfigKeyEnum.java b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/config/ConfigKeyEnum.java index 35f287af..da4514d7 100644 --- a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/config/ConfigKeyEnum.java +++ b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/config/ConfigKeyEnum.java @@ -22,6 +22,7 @@ public enum ConfigKeyEnum implements BaseEnum { */ SUPER_PASSWORD("super_password", "万能密码"), + LEVEL3_PROTECT_CONFIG("level3_protect_config", "三级等保配置"), ; private final String value; diff --git a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/config/ConfigService.java b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/config/ConfigService.java index 6872da3d..3522dea5 100644 --- a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/config/ConfigService.java +++ b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/config/ConfigService.java @@ -109,7 +109,8 @@ public ConfigVO getConfig(String configKey) { * */ public String getConfigValue(ConfigKeyEnum configKey) { - return this.getConfig(configKey).getConfigValue(); + ConfigVO config = this.getConfig(configKey); + return config == null ? null : config.getConfigValue(); } /** @@ -125,12 +126,12 @@ public T getConfigValue2Obj(ConfigKeyEnum configKey, Class clazz) { * 添加系统配置 * */ - public ResponseDTO add(ConfigAddForm configAddDTO) { - ConfigEntity entity = configDao.selectByKey(configAddDTO.getConfigKey()); + public ResponseDTO add(ConfigAddForm configAddForm) { + ConfigEntity entity = configDao.selectByKey(configAddForm.getConfigKey()); if (null != entity) { return ResponseDTO.error(UserErrorCode.ALREADY_EXIST); } - entity = SmartBeanUtil.copy(configAddDTO, ConfigEntity.class); + entity = SmartBeanUtil.copy(configAddForm, ConfigEntity.class); configDao.insert(entity); // 刷新缓存 diff --git a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/datamasking/DataMasking.java b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/datamasking/DataMasking.java new file mode 100644 index 00000000..c3847d71 --- /dev/null +++ b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/datamasking/DataMasking.java @@ -0,0 +1,27 @@ +package net.lab1024.sa.base.module.support.datamasking; + +import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import net.lab1024.sa.base.common.json.serializer.DataMaskingSerializer; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 脱敏注解 + * + * @author 罗伊 + * @description: + * @date 2024/7/21 4:39 下午 + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +@JacksonAnnotationsInside +@JsonSerialize(using = DataMaskingSerializer.class, nullsUsing = DataMaskingSerializer.class) +public @interface DataMasking { + + DataMaskingTypeEnum value() default DataMaskingTypeEnum.COMMON; + +} diff --git a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/datamasking/DataMaskingTypeEnum.java b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/datamasking/DataMaskingTypeEnum.java new file mode 100644 index 00000000..7df897ad --- /dev/null +++ b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/datamasking/DataMaskingTypeEnum.java @@ -0,0 +1,40 @@ +package net.lab1024.sa.base.module.support.datamasking; + +import cn.hutool.core.util.DesensitizedUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 脱敏数据类型 + * + * @Author 1024创新实验室-创始人兼主任:卓大 + * @Date 2024/8/1 + * @Wechat zhuoda1024 + * @Email lab1024@163.com + * @Copyright 1024创新实验室 ,Since 2012 + */ + +@AllArgsConstructor +@Getter +public enum DataMaskingTypeEnum { + + COMMON(null, "通用"), + PHONE(DesensitizedUtil.DesensitizedType.MOBILE_PHONE, "手机号"), + CHINESE_NAME(DesensitizedUtil.DesensitizedType.CHINESE_NAME, "中文名"), + ID_CARD(DesensitizedUtil.DesensitizedType.ID_CARD, "身份证号"), + FIXED_PHONE(DesensitizedUtil.DesensitizedType.FIXED_PHONE, "座机号"), + ADDRESS(DesensitizedUtil.DesensitizedType.ADDRESS, "地址"), + EMAIL(DesensitizedUtil.DesensitizedType.EMAIL, "电子邮件"), + PASSWORD(DesensitizedUtil.DesensitizedType.PASSWORD, "密码"), + CAR_LICENSE(DesensitizedUtil.DesensitizedType.CAR_LICENSE, "中国大陆车牌"), + BANK_CARD(DesensitizedUtil.DesensitizedType.BANK_CARD, "银行卡"), + USER_ID(DesensitizedUtil.DesensitizedType.USER_ID, "用户id"); + + + + private DesensitizedUtil.DesensitizedType type; + + private String desc; + + +} diff --git a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/datamasking/SmartDataMaskingUtil.java b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/datamasking/SmartDataMaskingUtil.java new file mode 100644 index 00000000..4443da40 --- /dev/null +++ b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/datamasking/SmartDataMaskingUtil.java @@ -0,0 +1,216 @@ +package net.lab1024.sa.base.module.support.datamasking; + +import cn.hutool.core.util.DesensitizedUtil; +import cn.hutool.core.util.StrUtil; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; + +import java.beans.IntrospectionException; +import java.beans.PropertyDescriptor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 脱敏工具类 + * + * @Author 1024创新实验室-主任: 卓大 + * @Date 2024-07-23 21:38:52 + * @Wechat zhuoda1024 + * @Email lab1024@163.com + * @Copyright 1024创新实验室 + */ +public class SmartDataMaskingUtil { + + /** + * 类 加注解字段缓存 + */ + private static final ConcurrentHashMap, List> fieldMap = new ConcurrentHashMap<>(); + + public static String dataMasking(String value) { + if (StringUtils.isBlank(value)) { + return value; + } + + if (value.length() < 4) { + return StrUtil.hide(value, 0, value.length()); + } + + int valueLength = value.length(); + int startHideIndex = getHideStartIndex(valueLength); + int endHideIndex = getHideEndIndex(valueLength); + return StrUtil.hide(value, startHideIndex, endHideIndex); + } + + public static Object dataMasking(Object value, DataMaskingTypeEnum dataType) { + + if (value == null) { + return null; + } + + if (dataType == null) { + return dataMasking(String.valueOf(value)); + } + + switch (dataType) { + case PHONE: + return DesensitizedUtil.mobilePhone(String.valueOf(value)); + case CHINESE_NAME: + return DesensitizedUtil.chineseName(String.valueOf(value)); + case ID_CARD: + return DesensitizedUtil.idCardNum(String.valueOf(value), 6, 2); + case FIXED_PHONE: + return DesensitizedUtil.fixedPhone(String.valueOf(value)); + case ADDRESS: + return StrUtil.hide(String.valueOf(value), 6, String.valueOf(value).length() - 1); + case EMAIL: + return DesensitizedUtil.email(String.valueOf(value)); + case PASSWORD: + return DesensitizedUtil.password(String.valueOf(value)); + case CAR_LICENSE: + return DesensitizedUtil.carLicense(String.valueOf(value)); + case BANK_CARD: + return DesensitizedUtil.bankCard(String.valueOf(value)); + case USER_ID: + return DesensitizedUtil.userId(); + default: + return dataMasking(String.valueOf(value)); + } + } + + /** + * 批量脱敏 + */ + public static void dataMasking(Collection objectList) throws IntrospectionException, InvocationTargetException, IllegalAccessException { + if (CollectionUtils.isEmpty(objectList)) { + return; + } + + for (T object : objectList) { + dataMasking(object); + } + } + + + /** + * 单个脱敏 + */ + public static void dataMasking(T object) throws IntrospectionException, InvocationTargetException, IllegalAccessException { + Class tClass = object.getClass(); + List fieldList = getField(object); + for (Field field : fieldList) { + field.setAccessible(true); + String fieldValue = ""; + try { + PropertyDescriptor pd = new PropertyDescriptor(field.getName(), tClass); + Method getMethod = pd.getReadMethod(); + Object value = getMethod.invoke(object); + if (value != null) { + fieldValue = value.toString(); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + if (StringUtils.isBlank(fieldValue)) { + continue; + } + int valueLength = fieldValue.length(); + int startHideIndex = getHideStartIndex(valueLength); + int endHideIndex = getHideEndIndex(valueLength); + try { + field.set(object, StrUtil.hide(fieldValue, startHideIndex, endHideIndex)); + } catch (Exception e1) { + throw new RuntimeException(e1); + } + } + } + + private static int getHideStartIndex(int totalLength) { + if (totalLength <= 4) { + return 1; + } else if (totalLength <= 6) { + return 1; + } else if (totalLength <= 10) { + return 2; + } else if (totalLength <= 18) { + return 3; + } else if (totalLength <= 27) { + return 5; + } else if (totalLength <= 34) { + return 7; + } else if (totalLength <= 41) { + return 9; + } else { + return 15; + } + } + + private static int getHideEndIndex(int totalLength) { + if (totalLength <= 4) { + return totalLength - 1; + } else if (totalLength <= 6) { + return totalLength - 2; + } else if (totalLength <= 10) { + return totalLength - 2; + } else if (totalLength <= 18) { + return totalLength - 4; + } else if (totalLength <= 27) { + return totalLength - 6; + } else if (totalLength <= 34) { + return totalLength - 8; + } else if (totalLength <= 41) { + return totalLength - 10; + } else { + return totalLength - 16; + } + } + + + public static List getField(Object object) throws IntrospectionException { + // 从缓存中查询 + Class tClass = object.getClass(); + List fieldList = fieldMap.get(tClass); + if (null != fieldList) { + return fieldList; + } + + // 这一段递归代码 是为了 从父类中获取属性 + Class tempClass = tClass; + fieldList = new ArrayList<>(); + while (tempClass != null) { + Field[] declaredFields = tempClass.getDeclaredFields(); + for (Field field : declaredFields) { + boolean stringField = false; + try { + PropertyDescriptor pd = new PropertyDescriptor(field.getName(), tClass); + Method getMethod = pd.getReadMethod(); + Type returnType = getMethod.getGenericReturnType(); + stringField = "java.lang.String".equals(returnType.getTypeName()); + } catch (Exception e) { + throw new RuntimeException(e); + } + if (field.isAnnotationPresent(DataMasking.class) && stringField) { + field.setAccessible(true); + fieldList.add(field); + } + } + tempClass = tempClass.getSuperclass(); + } + fieldMap.put(tClass, fieldList); + return fieldList; + } + + public static void main(String[] args) { + System.out.println(dataMasking("a", null)); + System.out.println(dataMasking("ab", null)); + System.out.println(dataMasking("abc", null)); + System.out.println(dataMasking("abcd", null)); + System.out.println(dataMasking("abcde", null)); + } + +} \ No newline at end of file diff --git a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/file/service/FileService.java b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/file/service/FileService.java index 48fb6641..9b30e482 100644 --- a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/file/service/FileService.java +++ b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/file/service/FileService.java @@ -19,9 +19,9 @@ import net.lab1024.sa.base.module.support.file.domain.vo.FileUploadVO; import net.lab1024.sa.base.module.support.file.domain.vo.FileVO; import net.lab1024.sa.base.module.support.redis.RedisService; +import net.lab1024.sa.base.module.support.securityprotect.service.SecurityFileService; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; -import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; @@ -39,7 +39,7 @@ * @Date 2019年10月11日 15:34:47 * @Wechat zhuoda1024 * @Email lab1024@163.com - * @Copyright 1024创新实验室 + * @Copyright 1024创新实验室 */ @Service public class FileService { @@ -58,9 +58,8 @@ public class FileService { @Resource private RedisService redisService; - @Value("${spring.servlet.multipart.max-file-size}") - private String maxFileSize; - + @Resource + private SecurityFileService securityFileService; /** * 文件上传服务 @@ -89,11 +88,10 @@ public ResponseDTO fileUpload(MultipartFile file, Integer folderTy return ResponseDTO.userErrorParam("文件名称最大长度为:" + FILE_NAME_MAX_LENGTH); } - // 校验文件大小 - String maxSizeStr = maxFileSize.toLowerCase().replace("mb", ""); - long maxSize = Integer.parseInt(maxSizeStr) * 1024 * 1024L; - if (file.getSize() > maxSize) { - return ResponseDTO.userErrorParam("上传文件最大为:" + maxSize); + // 校验文件大小以及安全性 + ResponseDTO validateFile = securityFileService.checkFile(file); + if (!validateFile.getOk()) { + return ResponseDTO.error(validateFile); } // 进行上传 @@ -192,7 +190,7 @@ public ResponseDTO getDownloadFile(String fileKey, String userAg // 根据文件服务类 获取对应文件服务 查询 url ResponseDTO download = fileStorageService.download(fileKey); - if(download.getOk()){ + if (download.getOk()) { download.getData().getMetadata().setFileName(fileVO.getFileName()); } return download; diff --git a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/job/core/SmartJobExecutor.java b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/job/core/SmartJobExecutor.java index c2508af9..721410a8 100644 --- a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/job/core/SmartJobExecutor.java +++ b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/job/core/SmartJobExecutor.java @@ -142,6 +142,8 @@ private Long saveLogBeforeExecute(SmartJobEntity jobEntity, logEntity.setSuccessFlag(true); // 执行开始时间 logEntity.setExecuteStartTime(executeTime); + logEntity.setExecuteEndTime(executeTime); + logEntity.setExecuteTimeMillis(0L); logEntity.setCreateName(executorName); logEntity.setIp(SmartIpUtil.getLocalFirstIp()); logEntity.setProcessId(SmartJobUtil.getProcessId()); diff --git a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/mail/MailService.java b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/mail/MailService.java new file mode 100644 index 00000000..3e2f533e --- /dev/null +++ b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/mail/MailService.java @@ -0,0 +1,179 @@ +package net.lab1024.sa.base.module.support.mail; + + +import cn.hutool.core.lang.UUID; +import freemarker.cache.StringTemplateLoader; +import freemarker.template.Configuration; +import freemarker.template.Template; +import lombok.extern.slf4j.Slf4j; +import net.lab1024.sa.base.common.domain.ResponseDTO; +import net.lab1024.sa.base.common.domain.SystemEnvironment; +import net.lab1024.sa.base.module.support.mail.constant.MailTemplateCodeEnum; +import net.lab1024.sa.base.module.support.mail.constant.MailTemplateTypeEnum; +import net.lab1024.sa.base.module.support.mail.domain.MailTemplateEntity; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.text.StringSubstitutor; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.mail.javamail.MimeMessageHelper; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import javax.mail.MessagingException; +import javax.mail.internet.MimeMessage; +import java.io.File; +import java.io.StringWriter; +import java.io.Writer; +import java.util.List; +import java.util.Map; + +/** + * + * 发生邮件:
+ * 1、支持直接发送
+ * 2、支持使用邮件模板发送 + * + * @Author 1024创新实验室-创始人兼主任:卓大 + * @Date 2024/8/5 + * @Wechat zhuoda1024 + * @Email lab1024@163.com + * @Copyright 1024创新实验室 ,Since 2012 + */ +@Slf4j +@Component +public class MailService { + + @Autowired + private JavaMailSender javaMailSender; + + @Resource + private MailTemplateDao mailTemplateDao; + + @Resource + private SystemEnvironment systemEnvironment; + + @Value("${spring.mail.username}") + private String clientMail; + + + /** + * 使用模板发送邮件 + */ + public ResponseDTO sendMail(MailTemplateCodeEnum templateCode, Map templateParamsMap, List receiverUserList, List fileList) { + + MailTemplateEntity mailTemplateEntity = mailTemplateDao.selectById(templateCode.name().toLowerCase()); + if (mailTemplateEntity == null) { + return ResponseDTO.userErrorParam("模版不存在"); + } + + if (mailTemplateEntity.getDisableFlag()) { + return ResponseDTO.userErrorParam("模版已禁用,无法发送"); + } + + String content = null; + if (MailTemplateTypeEnum.FREEMARKER.name().equalsIgnoreCase(mailTemplateEntity.getTemplateType().trim())) { + content = freemarkerResolverContent(mailTemplateEntity.getTemplateContent(), templateParamsMap); + } else if (MailTemplateTypeEnum.STRING.name().equalsIgnoreCase(mailTemplateEntity.getTemplateType().trim())) { + content = stringResolverContent(mailTemplateEntity.getTemplateContent(), templateParamsMap); + } else { + return ResponseDTO.userErrorParam("模版类型不存在"); + } + + try { + + this.sendMail(mailTemplateEntity.getTemplateSubject(), content, fileList, receiverUserList, true); + + } catch (Throwable e) { + log.error("邮件发送失败", e); + return ResponseDTO.userErrorParam("邮件发送失败"); + } + return ResponseDTO.ok(); + } + + /** + * 使用模板发送邮件 + */ + public ResponseDTO sendMail(MailTemplateCodeEnum templateCode, Map templateParamsMap, List receiverUserList) { + return this.sendMail(templateCode, templateParamsMap, receiverUserList, null); + } + + + /** + * 发送邮件 + * + * @param subject 主题 + * @param content 内容 + * @param fileList 文件 + * @param receiverUserList 接收方 + * @throws MessagingException + */ + public void sendMail(String subject, String content, List fileList, List receiverUserList, boolean isHtml) throws MessagingException { + + if (CollectionUtils.isEmpty(receiverUserList)) { + throw new RuntimeException("接收方不能为空"); + } + + if (StringUtils.isBlank(content)) { + throw new RuntimeException("邮件内容不能为空"); + } + + if (!systemEnvironment.isProd()) { + subject = "(测试)" + subject; + } + + MimeMessage mimeMessage = javaMailSender.createMimeMessage(); + + //是否为多文件上传 + boolean multiparty = !CollectionUtils.isEmpty(fileList); + MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, multiparty); + helper.setFrom(clientMail); + helper.setTo(receiverUserList.toArray(new String[0])); + helper.setSubject(subject); + //发送html格式 + helper.setText(content, isHtml); + + //附件 + if (multiparty) { + for (File file : fileList) { + helper.addAttachment(file.getName(), file); + } + } + javaMailSender.send(mimeMessage); + } + + /** + * 使用字符串生成最终内容 + */ + private String stringResolverContent(String stringTemplate, Map templateParamsMap) { + StringSubstitutor stringSubstitutor = new StringSubstitutor(templateParamsMap); + String contractHtml = stringSubstitutor.replace(stringTemplate); + Document doc = Jsoup.parse(contractHtml); + doc.outputSettings().syntax(Document.OutputSettings.Syntax.xml); + return doc.outerHtml(); + } + + + /** + * 使用 freemarker 生成最终内容 + */ + private String freemarkerResolverContent(String htmlTemplate, Map templateParamsMap) { + Configuration configuration = new Configuration(Configuration.VERSION_2_3_23); + StringTemplateLoader stringLoader = new StringTemplateLoader(); + String templateName = UUID.fastUUID().toString(true); + stringLoader.putTemplate(templateName, htmlTemplate); + configuration.setTemplateLoader(stringLoader); + try { + Template template = configuration.getTemplate(templateName, "utf-8"); + Writer out = new StringWriter(2048); + template.process(templateParamsMap, out); + return out.toString(); + } catch (Throwable e) { + log.error("freemarkerResolverContent error: ", e); + } + return ""; + } +} diff --git a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/mail/MailTemplateDao.java b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/mail/MailTemplateDao.java new file mode 100644 index 00000000..d57c1c79 --- /dev/null +++ b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/mail/MailTemplateDao.java @@ -0,0 +1,22 @@ +package net.lab1024.sa.base.module.support.mail; + + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import net.lab1024.sa.base.module.support.mail.domain.MailTemplateEntity; +import org.apache.ibatis.annotations.Mapper; +import org.springframework.stereotype.Component; + +/** + * 邮件模板 + * + * @Author 1024创新实验室-创始人兼主任:卓大 + * @Date 2024/8/5 + * @Wechat zhuoda1024 + * @Email lab1024@163.com + * @Copyright 1024创新实验室 ,Since 2012 + */ +@Mapper +@Component +public interface MailTemplateDao extends BaseMapper { + +} diff --git a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/mail/constant/MailTemplateCodeEnum.java b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/mail/constant/MailTemplateCodeEnum.java new file mode 100644 index 00000000..9eab6ca6 --- /dev/null +++ b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/mail/constant/MailTemplateCodeEnum.java @@ -0,0 +1,19 @@ +package net.lab1024.sa.base.module.support.mail.constant; + +/** + * 模版编码 + * + * @Author 1024创新实验室-创始人兼主任:卓大 + * @Date 2024/8/5 + * @Wechat zhuoda1024 + * @Email lab1024@163.com + * @Copyright 1024创新实验室 ,Since 2012 + */ +public enum MailTemplateCodeEnum { + + /** + * 登录验证码 + */ + LOGIN_VERIFICATION_CODE + +} diff --git a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/mail/constant/MailTemplateTypeEnum.java b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/mail/constant/MailTemplateTypeEnum.java new file mode 100644 index 00000000..f26ef24b --- /dev/null +++ b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/mail/constant/MailTemplateTypeEnum.java @@ -0,0 +1,30 @@ +package net.lab1024.sa.base.module.support.mail.constant; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import net.lab1024.sa.base.common.enumeration.BaseEnum; + +/** + * 邮件模板类型 + * + * @Author 1024创新实验室-创始人兼主任:卓大 + * @Date 2024/8/5 + * @Wechat zhuoda1024 + * @Email lab1024@163.com + * @Copyright 1024创新实验室 ,Since 2012 + */ + +@Getter +@AllArgsConstructor +public enum MailTemplateTypeEnum implements BaseEnum { + + STRING("string", "字符串替代器"), + + FREEMARKER("freemarker", "freemarker模板引擎"); + + private String value; + + private String desc; + + +} \ No newline at end of file diff --git a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/mail/domain/MailTemplateEntity.java b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/mail/domain/MailTemplateEntity.java new file mode 100644 index 00000000..8f161639 --- /dev/null +++ b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/mail/domain/MailTemplateEntity.java @@ -0,0 +1,51 @@ +package net.lab1024.sa.base.module.support.mail.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * + * 邮件模板 + * + * @Author 1024创新实验室-创始人兼主任:卓大 + * @Date 2024/8/5 + * @Wechat zhuoda1024 + * @Email lab1024@163.com + * @Copyright 1024创新实验室 ,Since 2012 + */ +@Data +@TableName("t_mail_template") +public class MailTemplateEntity { + + @TableId(type = IdType.NONE) + private String templateCode; + + /** + * 主题 + */ + private String templateSubject; + + /** + * 模板类型 + */ + private String templateType; + + /** + * 模板内容 + */ + private String templateContent; + + /** + * 禁用标识 + */ + private Boolean disableFlag; + + + private LocalDateTime updateTime; + + private LocalDateTime createTime; +} diff --git a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/operatelog/domain/OperateLogQueryForm.java b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/operatelog/domain/OperateLogQueryForm.java index 53bdbce7..5e9bc708 100644 --- a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/operatelog/domain/OperateLogQueryForm.java +++ b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/operatelog/domain/OperateLogQueryForm.java @@ -22,6 +22,12 @@ public class OperateLogQueryForm extends PageParam { @Schema(description = "用户类型") private Integer operateUserType; + @Schema(description = "关键字:模块、操作内容") + private String keywords; + + @Schema(description = "请求关键字:请求地址、请求方法、请求参数") + private String requestKeywords; + @Schema(description = "开始日期") private String startDate; diff --git a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/reload/ReloadCommand.java b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/reload/ReloadCommand.java index c37cfa6f..7b73fed7 100644 --- a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/reload/ReloadCommand.java +++ b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/reload/ReloadCommand.java @@ -8,7 +8,6 @@ import net.lab1024.sa.base.module.support.reload.dao.ReloadResultDao; import net.lab1024.sa.base.module.support.reload.domain.ReloadItemEntity; import net.lab1024.sa.base.module.support.reload.domain.ReloadResultEntity; -import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; import javax.annotation.Resource; diff --git a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/dao/PasswordLogDao.java b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/dao/PasswordLogDao.java new file mode 100644 index 00000000..8b424eab --- /dev/null +++ b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/dao/PasswordLogDao.java @@ -0,0 +1,34 @@ +package net.lab1024.sa.base.module.support.securityprotect.dao; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import net.lab1024.sa.base.module.support.securityprotect.domain.PasswordLogEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Mapper +@Component +public interface PasswordLogDao extends BaseMapper { + + /** + * 查询最后一次修改密码记录 + * + * @param userType + * @param userId + * @return + */ + PasswordLogEntity selectLastByUserTypeAndUserId(@Param("userType") Integer userType, @Param("userId") Long userId); + + + /** + * 查询最近几次修改后的密码 + * + * @param userType + * @param userId + * @return + */ + List selectOldPassword(@Param("userType") Integer userType, @Param("userId") Long userId, @Param("limit") int limit); + +} diff --git a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/domain/Level3ProtectConfigForm.java b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/domain/Level3ProtectConfigForm.java new file mode 100644 index 00000000..3f8fa3bf --- /dev/null +++ b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/domain/Level3ProtectConfigForm.java @@ -0,0 +1,58 @@ +package net.lab1024.sa.base.module.support.securityprotect.domain; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotNull; + +/** + * 三级等保相关配置 + * + * @Author 1024创新实验室-创始人兼主任:卓大 + * @Date 2024/7/30 + * @Wechat zhuoda1024 + * @Email lab1024@163.com + * @Copyright 1024创新实验室 ,Since 2012 + */ + +@Data +public class Level3ProtectConfigForm { + + @Schema(description = "连续登录失败次数则锁定") + @NotNull(message = "连续登录失败次数则锁定 不能为空") + private Integer loginFailMaxTimes; + + @Schema(description = "连续登录失败锁定时间(单位:分钟)") + @NotNull(message = "连续登录失败锁定时间(单位:分钟) 不能为空") + private Integer loginFailLockMinutes; + + @Schema(description = "最低活跃时间(单位:分钟)") + @NotNull(message = "最低活跃时间(单位:分钟) 不能为空") + private Integer loginActiveTimeoutMinutes; + + @Schema(description = "开启双因子登录") + @NotNull(message = "开启双因子登录 不能为空") + private Boolean twoFactorLoginEnabled; + + @Schema(description = "密码复杂度 是否开启,默认:开启") + @NotNull(message = "密码复杂度 是否开启 不能为空") + private Boolean passwordComplexityEnabled; + + @Schema(description = "定期修改密码时间间隔(默认:月)") + @NotNull(message = "定期修改密码时间间隔(默认:月) 不能为空") + private Integer regularChangePasswordMonths; + + @Schema(description = "定期修改密码不允许重复次数,默认:3次以内密码不能相同(默认:次)") + @NotNull(message = "定期修改密码不允许重复次数 不能为空") + private Integer regularChangePasswordNotAllowRepeatTimes; + + @Schema(description = "文件检测,默认:不开启") + @NotNull(message = "文件检测 是否开启 不能为空") + private Boolean fileDetectFlag; + + @Schema(description = "文件大小限制,单位 mb ,(默认:50 mb)") + @NotNull(message = "文件大小限制 不能为空") + private Long maxUploadFileSizeMb; + + +} diff --git a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/domain/PasswordLogEntity.java b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/domain/PasswordLogEntity.java new file mode 100644 index 00000000..93927dc6 --- /dev/null +++ b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/domain/PasswordLogEntity.java @@ -0,0 +1,43 @@ +package net.lab1024.sa.base.module.support.securityprotect.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * @author yandy + * @description: + * @date 2024/7/15 1:39 下午 + */ +@Data +@TableName("t_password_log") +public class PasswordLogEntity { + + /** + * 主键id + */ + @TableId(type = IdType.AUTO) + private Long id; + + private Integer userType; + + private Long userId; + + private String oldPassword; + + private String newPassword; + + /** + * 更新时间 + */ + private LocalDateTime updateTime; + + /** + * 创建时间 + */ + private LocalDateTime createTime; + +} \ No newline at end of file diff --git a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/Level3ProtectConfigService.java b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/Level3ProtectConfigService.java new file mode 100644 index 00000000..37bba688 --- /dev/null +++ b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/Level3ProtectConfigService.java @@ -0,0 +1,207 @@ +package net.lab1024.sa.base.module.support.securityprotect.service; + +import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson.JSON; +import lombok.extern.slf4j.Slf4j; +import net.lab1024.sa.base.common.domain.ResponseDTO; +import net.lab1024.sa.base.module.support.config.ConfigKeyEnum; +import net.lab1024.sa.base.module.support.config.ConfigService; +import net.lab1024.sa.base.module.support.securityprotect.domain.Level3ProtectConfigForm; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; + +/** + * 三级等保配置 + * + * @Author 1024创新实验室-创始人兼主任:卓大 + * @Date 2024/7/30 + * @Wechat zhuoda1024 + * @Email lab1024@163.com + * @Copyright 1024创新实验室 ,Since 2012 + */ + +@Service +@Slf4j +public class Level3ProtectConfigService { + + /** + * 开启双因子登录,默认:开启 + */ + private boolean twoFactorLoginEnabled = false; + + /** + * 连续登录失败次数则锁定,-1表示不受限制,可以一直尝试登录 + */ + private int loginFailMaxTimes = -1; + + /** + * 连续登录失败锁定时间(单位:秒),-1表示不锁定,建议锁定30分钟 + */ + private int loginFailLockSeconds = 1800; + + /** + * 最低活跃时间(单位:秒),超过此时间没有操作系统就会被冻结,默认-1 代表不限制,永不冻结; 默认 30分钟 + */ + private int loginActiveTimeoutSeconds = 1800; + + /** + * 密码复杂度 是否开启,默认:开启 + */ + private boolean passwordComplexityEnabled = true; + + /** + * 定期修改密码时间间隔(默认:天),默认:建议90天更换密码 + */ + private int regularChangePasswordDays = 90; + + /** + * 定期修改密码不允许相同次数,默认:3次以内密码不能相同 + */ + private int regularChangePasswordNotAllowRepeatTimes = 3; + + /** + * 文件大小限制,单位 mb ,(默认:50 mb) + */ + private long maxUploadFileSizeMb = 50; + + /** + * 文件检测,默认:不开启 + */ + private boolean fileDetectFlag = false; + + + @Resource + private ConfigService configService; + + /** + * 文件检测,默认:不开启 + */ + public boolean isFileDetectFlag() { + return fileDetectFlag; + } + + /** + * 文件大小限制,单位 mb ,(默认:50 mb) + */ + public long getMaxUploadFileSizeMb() { + return maxUploadFileSizeMb; + } + + /** + * 连续登录失败次数则锁定,-1表示不受限制,可以一直尝试登录 + */ + public int getLoginFailMaxTimes() { + return loginFailMaxTimes; + } + + /** + * 连续登录失败锁定时间(单位:秒),-1表示不锁定,建议锁定30分钟 + */ + public int getLoginFailLockSeconds() { + return loginFailLockSeconds; + } + + /** + * 最低活跃时间(单位:秒),超过此时间没有操作系统就会被冻结,默认-1 代表不限制,永不冻结; 默认 30分钟 + */ + public int getLoginActiveTimeoutSeconds() { + return loginActiveTimeoutSeconds; + } + + /** + * 定期修改密码时间间隔(默认:天),默认:建议90天更换密码 + */ + public int getRegularChangePasswordDays() { + return regularChangePasswordDays; + } + + /** + * 开启双因子登录,默认:开启 + */ + public boolean isTwoFactorLoginEnabled() { + return twoFactorLoginEnabled; + } + + /** + * 密码复杂度 是否开启,默认:开启 + */ + public boolean isPasswordComplexityEnabled() { + return passwordComplexityEnabled; + } + + /** + * 定期修改密码不允许相同次数,默认:3次以内密码不能相同 + */ + public int getRegularChangePasswordNotAllowRepeatTimes() { + return regularChangePasswordNotAllowRepeatTimes; + } + + @PostConstruct + void init() { + String configValue = configService.getConfigValue(ConfigKeyEnum.LEVEL3_PROTECT_CONFIG); + if (StrUtil.isEmpty(configValue)) { + throw new ExceptionInInitializerError("t_config 表 三级等保配置为空,请进行配置!"); + } + Level3ProtectConfigForm level3ProtectConfigForm = JSON.parseObject(configValue, Level3ProtectConfigForm.class); + setProp(level3ProtectConfigForm); + } + + /** + * 设置属性 + */ + private void setProp(Level3ProtectConfigForm configForm) { + + if (configForm.getFileDetectFlag() != null) { + this.fileDetectFlag = configForm.getFileDetectFlag(); + } + + if (configForm.getMaxUploadFileSizeMb() != null) { + this.maxUploadFileSizeMb = configForm.getMaxUploadFileSizeMb(); + } + + if (configForm.getLoginFailLockMinutes() != null) { + this.loginFailLockSeconds = configForm.getLoginFailLockMinutes() * 60; + } + + if (configForm.getLoginActiveTimeoutMinutes() != null) { + this.loginActiveTimeoutSeconds = configForm.getLoginActiveTimeoutMinutes() * 60; + } + + if (configForm.getPasswordComplexityEnabled() != null) { + this.passwordComplexityEnabled = configForm.getPasswordComplexityEnabled(); + } + + if (configForm.getRegularChangePasswordMonths() != null) { + this.regularChangePasswordDays = configForm.getRegularChangePasswordMonths() * 30; + } + + if (configForm.getTwoFactorLoginEnabled() != null) { + this.twoFactorLoginEnabled = configForm.getTwoFactorLoginEnabled(); + } + + if (configForm.getRegularChangePasswordNotAllowRepeatTimes() != null) { + this.regularChangePasswordNotAllowRepeatTimes = configForm.getRegularChangePasswordNotAllowRepeatTimes(); + } + + // 设置 最低活跃时间(单位:秒) + if (this.loginActiveTimeoutSeconds > 0) { + StpUtil.getStpLogic().getConfigOrGlobal().setActiveTimeout(getLoginActiveTimeoutSeconds()); + } else { + StpUtil.getStpLogic().getConfigOrGlobal().setActiveTimeout(-1); + } + } + + /** + * 更新三级等保配置 + */ + public ResponseDTO updateLevel3Config(Level3ProtectConfigForm configForm) { + // 设置属性 + setProp(configForm); + // 保存数据库 + String configFormJsonString = JSON.toJSONString(configForm, true); + return configService.updateValueByKey(ConfigKeyEnum.LEVEL3_PROTECT_CONFIG, configFormJsonString); + } +} diff --git a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/ProtectPasswordService.java b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/ProtectPasswordService.java deleted file mode 100644 index 80549654..00000000 --- a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/ProtectPasswordService.java +++ /dev/null @@ -1,99 +0,0 @@ -package net.lab1024.sa.base.module.support.securityprotect.service; - -import net.lab1024.sa.base.common.domain.ResponseDTO; -import net.lab1024.sa.base.common.util.SmartStringUtil; -import net.lab1024.sa.base.module.support.apiencrypt.service.ApiEncryptService; -import org.apache.commons.lang3.RandomStringUtils; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; - -import javax.annotation.Resource; - -/** - * 三级等保 密码 相关 - * - * @Author 1024创新实验室-主任:卓大 - * @Date 2023/10/11 19:25:59 - * @Wechat zhuoda1024 - * @Email lab1024@163.com - * @Copyright 1024创新实验室,Since 2012 - */ - -@Service -public class ProtectPasswordService { - - /** - * 密码长度8-20位且包含大写字母、小写字母、数字三种 - */ - public static final String PASSWORD_PATTERN = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)[a-zA-Z\\d]{8,20}$"; - - /** - * 密码长度8-20位且包含大写字母、小写字母、数字三种 - */ - public static final String PASSWORD_FORMAT_MSG = "密码必须为长度8-20位且包含大写字母、小写字母、数字三种"; - - - private static final int PASSWORD_LENGTH = 8; - - - /** - * 密码复杂度开启, 默认为true 开启,false 不开启 - */ - @Value("${classified-protect.password-complexity-enabled}") - private Boolean passwordComplexityEnabled; - - - @Resource - private ApiEncryptService apiEncryptService; - - /** - * 校验密码复杂度 - * - * @return - */ - public ResponseDTO validatePassComplexity(String password) { - - // 无需校验 - if (!passwordComplexityEnabled) { - return ResponseDTO.ok(); - } - - if (SmartStringUtil.isEmpty(password)) { - return ResponseDTO.userErrorParam(PASSWORD_FORMAT_MSG); - } - - if (!password.matches(PASSWORD_PATTERN)) { - return ResponseDTO.userErrorParam(PASSWORD_FORMAT_MSG); - } - - return ResponseDTO.ok(); - } - - /** - * 随机生成密码 - * - * @return - */ - public String randomPassword() { - // 未开启密码复杂度,则由8为数字构成 - if (passwordComplexityEnabled) { - return RandomStringUtils.randomNumeric(PASSWORD_LENGTH); - } else { - // 3位大写字母,2位数字,3位小写字母 - return RandomStringUtils.randomAlphabetic(3).toUpperCase() + RandomStringUtils.randomNumeric(2) + RandomStringUtils.randomAlphabetic(3).toLowerCase(); - } - } - - - /** - * 解密 SM4 or AES 加密过的密码 - * - * @param encryptedPassword - * @return - */ - public String decryptPassword(String encryptedPassword) { - return apiEncryptService.decrypt(encryptedPassword); - } - - -} diff --git a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/SecurityFileService.java b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/SecurityFileService.java new file mode 100644 index 00000000..cdea03a4 --- /dev/null +++ b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/SecurityFileService.java @@ -0,0 +1,52 @@ +package net.lab1024.sa.base.module.support.securityprotect.service; + +import net.lab1024.sa.base.common.domain.ResponseDTO; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import java.io.File; + +/** + * 三级等保 文件上传 相关 + * + * @Author 1024创新实验室-主任:卓大 + * @Date 2024/08/22 19:25:59 + * @Wechat zhuoda1024 + * @Email lab1024@163.com + * @Copyright 1024创新实验室,Since 2012 + */ + +@Service +public class SecurityFileService { + + @Resource + private Level3ProtectConfigService level3ProtectConfigService; + + + /** + * 检测文件安全类型 + */ + public ResponseDTO checkFile(MultipartFile file) { + + // 检验文件大小 + if (level3ProtectConfigService.getMaxUploadFileSizeMb() > 0) { + long maxSize = level3ProtectConfigService.getMaxUploadFileSizeMb() * 1024 * 1024; + if (file.getSize() > maxSize) { + return ResponseDTO.userErrorParam("上传文件最大为:" + level3ProtectConfigService.getMaxUploadFileSizeMb() + " mb"); + } + } + + // 文件类型安全检测 + if (!level3ProtectConfigService.isFileDetectFlag()) { + return ResponseDTO.ok(); + } + + // 检测文件类型 + // ..... + + return ResponseDTO.ok(); + } + +} +; \ No newline at end of file diff --git a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/ProtectLoginService.java b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/SecurityLoginService.java similarity index 63% rename from smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/ProtectLoginService.java rename to smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/SecurityLoginService.java index c20d5e8d..4088ff29 100644 --- a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/ProtectLoginService.java +++ b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/SecurityLoginService.java @@ -12,7 +12,6 @@ import net.lab1024.sa.base.module.support.securityprotect.domain.LoginFailQueryForm; import net.lab1024.sa.base.module.support.securityprotect.domain.LoginFailVO; import org.apache.commons.collections4.CollectionUtils; -import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import javax.annotation.Resource; @@ -26,27 +25,18 @@ * @Date 2023/10/11 19:25:59 * @Wechat zhuoda1024 * @Email lab1024@163.com - * @Copyright 1024创新实验室,Since 2012 + * @Copyright 1024创新实验室,Since 2012 */ @Service -public class ProtectLoginService { +public class SecurityLoginService { private static final String LOGIN_LOCK_MSG = "您已连续登录失败%s次,账号锁定%s分钟,解锁时间为:%s,请您耐心等待!"; private static final String LOGIN_FAIL_MSG = "登录名或密码错误!连续登录失败%s次,账号将锁定%s分钟!您还可以再尝试%s次!"; - /** - * 连续登录失败次数则锁定,-1表示不受限制,可以一直登录 - */ - @Value("${classified-protect.login-max-fail-times}") - private Integer loginMaxFailTimes; - - /** - * 连续登录失败锁定时间(单位:秒),-1表示不锁定 - */ - @Value("${classified-protect.login-fail-locked-seconds}") - private Integer loginFailLockedSeconds; + @Resource + private Level3ProtectConfigService level3ProtectConfigService; @Resource private LoginFailDao loginFailDao; @@ -61,8 +51,8 @@ public class ProtectLoginService { */ public ResponseDTO checkLogin(Long userId, UserTypeEnum userType) { - // 无需校验 - if (loginMaxFailTimes < 1) { + // 若登录最大失败次数小于1,无需校验 + if (level3ProtectConfigService.getLoginFailMaxTimes() < 1) { return ResponseDTO.ok(); } @@ -72,19 +62,24 @@ public ResponseDTO checkLogin(Long userId, UserTypeEnum userTyp return ResponseDTO.ok(); } - // 校验次数 - if (loginFailEntity.getLoginFailCount() < loginMaxFailTimes) { + // 校验登录失败次数 + if (loginFailEntity.getLoginFailCount() < level3ProtectConfigService.getLoginFailMaxTimes()) { + return ResponseDTO.ok(loginFailEntity); + } + + // 校验是否锁定 + if (loginFailEntity.getLoginLockBeginTime() == null) { return ResponseDTO.ok(loginFailEntity); } // 校验锁定时长 - if(loginFailEntity.getLoginLockBeginTime().plusSeconds(loginFailLockedSeconds).isBefore(LocalDateTime.now())){ + if (loginFailEntity.getLoginLockBeginTime().plusSeconds(level3ProtectConfigService.getLoginFailLockSeconds()).isBefore(LocalDateTime.now())) { // 过了锁定时间 return ResponseDTO.ok(loginFailEntity); } - LocalDateTime unlockTime = loginFailEntity.getLoginLockBeginTime().plusSeconds(loginFailLockedSeconds); - return ResponseDTO.error(UserErrorCode.LOGIN_FAIL_LOCK, String.format(LOGIN_LOCK_MSG, loginFailEntity.getLoginFailCount(), loginFailLockedSeconds / 60, LocalDateTimeUtil.formatNormal(unlockTime))); + LocalDateTime unlockTime = loginFailEntity.getLoginLockBeginTime().plusSeconds(level3ProtectConfigService.getLoginFailLockSeconds()); + return ResponseDTO.error(UserErrorCode.LOGIN_FAIL_LOCK, String.format(LOGIN_LOCK_MSG, loginFailEntity.getLoginFailCount(), level3ProtectConfigService.getLoginFailLockSeconds() / 60, LocalDateTimeUtil.formatNormal(unlockTime))); } /** @@ -96,43 +91,40 @@ public ResponseDTO checkLogin(Long userId, UserTypeEnum userTyp */ public String recordLoginFail(Long userId, UserTypeEnum userType, String loginName, LoginFailEntity loginFailEntity) { - // 无需校验 - if (loginMaxFailTimes < 1) { + // 若登录最大失败次数小于1,无需记录 + if (level3ProtectConfigService.getLoginFailMaxTimes() < 1) { return null; } + // 登录失败 + int loginFailCount = loginFailEntity == null ? 1 : loginFailEntity.getLoginFailCount() + 1; + boolean lockFlag = loginFailCount >= level3ProtectConfigService.getLoginFailMaxTimes(); + LocalDateTime lockBeginTime = lockFlag ? LocalDateTime.now() : null; + if (loginFailEntity == null) { loginFailEntity = LoginFailEntity.builder() .userId(userId) .userType(userType.getValue()) .loginName(loginName) - .loginFailCount(1) - .lockFlag(false) - .loginLockBeginTime(null).build(); + .loginFailCount(loginFailCount) + .lockFlag(lockFlag) + .loginLockBeginTime(lockBeginTime) + .build(); loginFailDao.insert(loginFailEntity); } else { - - // 如果是已经锁定状态,则重新计算 - if(loginFailEntity.getLockFlag()){ - loginFailEntity.setLockFlag(false); - loginFailEntity.setLoginFailCount(1); - loginFailEntity.setLoginLockBeginTime(null); - }else{ - loginFailEntity.setLoginLockBeginTime(LocalDateTime.now()); - loginFailEntity.setLoginFailCount(loginFailEntity.getLoginFailCount() + 1); - loginFailEntity.setLockFlag(loginFailEntity.getLoginFailCount() >= loginMaxFailTimes); - } - + loginFailEntity.setLoginLockBeginTime(lockBeginTime); + loginFailEntity.setLoginFailCount(loginFailCount); + loginFailEntity.setLockFlag(lockFlag); loginFailEntity.setLoginName(loginName); loginFailDao.updateById(loginFailEntity); } // 提示信息 - if (loginFailEntity.getLoginFailCount() >= loginMaxFailTimes) { - LocalDateTime unlockTime = loginFailEntity.getLoginLockBeginTime().plusSeconds(loginFailLockedSeconds); - return String.format(LOGIN_LOCK_MSG, loginFailEntity.getLoginFailCount(), loginFailLockedSeconds / 60, LocalDateTimeUtil.formatNormal(unlockTime)); + if (lockFlag) { + LocalDateTime unlockTime = loginFailEntity.getLoginLockBeginTime().plusSeconds(level3ProtectConfigService.getLoginFailLockSeconds()); + return String.format(LOGIN_LOCK_MSG, loginFailEntity.getLoginFailCount(), level3ProtectConfigService.getLoginFailLockSeconds() / 60, LocalDateTimeUtil.formatNormal(unlockTime)); } else { - return String.format(LOGIN_FAIL_MSG, loginMaxFailTimes, loginFailLockedSeconds / 60, loginMaxFailTimes - loginFailEntity.getLoginFailCount()); + return String.format(LOGIN_FAIL_MSG, level3ProtectConfigService.getLoginFailMaxTimes(), level3ProtectConfigService.getLoginFailLockSeconds() / 60, level3ProtectConfigService.getLoginFailMaxTimes() - loginFailEntity.getLoginFailCount()); } } @@ -143,8 +135,9 @@ public String recordLoginFail(Long userId, UserTypeEnum userType, String loginNa * @param userType */ public void removeLoginFail(Long userId, UserTypeEnum userType) { - // 无需校验 - if (loginMaxFailTimes < 1) { + + // 若登录最大失败次数小于1,无需校验 + if (level3ProtectConfigService.getLoginFailMaxTimes() < 1) { return; } @@ -160,8 +153,7 @@ public void removeLoginFail(Long userId, UserTypeEnum userType) { public PageResult queryPage(LoginFailQueryForm queryForm) { Page page = SmartPageUtil.convert2PageQuery(queryForm); List list = loginFailDao.queryPage(page, queryForm); - PageResult pageResult = SmartPageUtil.convert2PageResult(page, list); - return pageResult; + return SmartPageUtil.convert2PageResult(page, list); } /** diff --git a/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/SecurityPasswordService.java b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/SecurityPasswordService.java new file mode 100644 index 00000000..3d371b44 --- /dev/null +++ b/smart-admin-api/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/SecurityPasswordService.java @@ -0,0 +1,149 @@ +package net.lab1024.sa.base.module.support.securityprotect.service; + +import net.lab1024.sa.base.common.domain.RequestUser; +import net.lab1024.sa.base.common.domain.ResponseDTO; +import net.lab1024.sa.base.common.util.SmartStringUtil; +import net.lab1024.sa.base.module.support.securityprotect.dao.PasswordLogDao; +import net.lab1024.sa.base.module.support.securityprotect.domain.PasswordLogEntity; +import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.lang3.RandomStringUtils; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.time.LocalDateTime; +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; + +/** + * 三级等保 密码 相关 + * + * @Author 1024创新实验室-主任:卓大 + * @Date 2023/10/11 19:25:59 + * @Wechat zhuoda1024 + * @Email lab1024@163.com + * @Copyright 1024创新实验室,Since 2012 + */ + +@Service +public class SecurityPasswordService { + + /** + * 密码长度8-20位且包含大小写字母、数字、特殊符号三种及以上组合 + */ + public static final String PASSWORD_PATTERN = "^(?![a-zA-Z]+$)(?![A-Z0-9]+$)(?![A-Z\\W_!@#$%^&*`~()-+=]+$)(?![a-z0-9]+$)(?![a-z\\W_!@#$%^&*`~()-+=]+$)(?![0-9\\W_!@#$%^&*`~()-+=]+$)[a-zA-Z0-9\\W_!@#$%^&*`~()-+=]*$"; + + public static final String PASSWORD_FORMAT_MSG = "密码必须为长度8-20位且必须包含大小写字母、数字、特殊符号(如:@#$%^&*()_+-=)等三种字符"; + + + private static final int PASSWORD_LENGTH = 8; + + private static final String PASSWORD_SALT_FORMAT = "smart_%s_admin_$^&*"; + + + @Resource + private PasswordLogDao passwordLogDao; + + @Resource + private Level3ProtectConfigService level3ProtectConfigService; + + /** + * 校验密码复杂度 + */ + public ResponseDTO validatePasswordComplexity(String password) { + + if (SmartStringUtil.isEmpty(password)) { + return ResponseDTO.userErrorParam(PASSWORD_FORMAT_MSG); + } + + // 密码长度必须大于等于8位 + if (password.length() < PASSWORD_LENGTH) { + return ResponseDTO.userErrorParam(PASSWORD_FORMAT_MSG); + } + + // 无需校验 密码复杂度 + if (!level3ProtectConfigService.isPasswordComplexityEnabled()) { + return ResponseDTO.ok(); + } + + if (!password.matches(PASSWORD_PATTERN)) { + return ResponseDTO.userErrorParam(PASSWORD_FORMAT_MSG); + } + + return ResponseDTO.ok(); + } + + /** + * 校验密码重复次数 + */ + public ResponseDTO validatePasswordRepeatTimes(RequestUser requestUser, String newPassword) { + + // 密码重复次数小于1 无需校验 + if (level3ProtectConfigService.getRegularChangePasswordNotAllowRepeatTimes() < 1) { + return ResponseDTO.ok(); + } + + // 检查最近几次是否有重复密码 + List oldPasswords = passwordLogDao.selectOldPassword(requestUser.getUserType().getValue(), requestUser.getUserId(), level3ProtectConfigService.getRegularChangePasswordNotAllowRepeatTimes()); + if (oldPasswords != null && oldPasswords.contains(getEncryptPwd(newPassword))) { + return ResponseDTO.userErrorParam(String.format("与前%s个历史密码重复,请换个密码!", level3ProtectConfigService.getRegularChangePasswordNotAllowRepeatTimes())); + } + + return ResponseDTO.ok(); + } + + /** + * 随机生成密码 + */ + public String randomPassword() { + // 未开启密码复杂度,则由8为数字构成 + if (!level3ProtectConfigService.isPasswordComplexityEnabled()) { + return RandomStringUtils.randomNumeric(PASSWORD_LENGTH); + } + + // 3位大写字母,2位数字,2位小写字母 + 1位特殊符号 + return RandomStringUtils.randomAlphabetic(3).toUpperCase() + + RandomStringUtils.randomNumeric(2) + + RandomStringUtils.randomAlphabetic(2).toLowerCase() + + (ThreadLocalRandom.current().nextBoolean() ? "#" : "@"); + } + + + /** + * 保存修改密码 + */ + public void saveUserChangePasswordLog(RequestUser requestUser, String newPassword, String oldPassword) { + + PasswordLogEntity passwordLogEntity = new PasswordLogEntity(); + passwordLogEntity.setNewPassword(newPassword); + passwordLogEntity.setOldPassword(oldPassword); + passwordLogEntity.setUserId(requestUser.getUserId()); + passwordLogEntity.setUserType(requestUser.getUserType().getValue()); + passwordLogDao.insert(passwordLogEntity); + } + + /** + * 检查是否需要修改密码 + */ + public boolean checkNeedChangePassword(Integer userType, Long userId) { + + if (level3ProtectConfigService.getRegularChangePasswordDays() < 1) { + return false; + } + + PasswordLogEntity passwordLogEntity = passwordLogDao.selectLastByUserTypeAndUserId(userType, userId); + if (passwordLogEntity == null) { + return false; + } + + LocalDateTime nextUpdateTime = passwordLogEntity.getCreateTime().plusDays(level3ProtectConfigService.getRegularChangePasswordDays()); + return nextUpdateTime.isBefore(LocalDateTime.now()); + } + + /** + * 获取 加密后 的密码 + */ + public static String getEncryptPwd(String password) { + return DigestUtils.md5Hex(String.format(PASSWORD_SALT_FORMAT, password)); + } + +} diff --git a/smart-admin-api/sa-base/src/main/resources/code-generator-template/java/constant/enum.java.vm b/smart-admin-api/sa-base/src/main/resources/code-generator-template/java/constant/enum.java.vm index e67215ae..87c2c644 100644 --- a/smart-admin-api/sa-base/src/main/resources/code-generator-template/java/constant/enum.java.vm +++ b/smart-admin-api/sa-base/src/main/resources/code-generator-template/java/constant/enum.java.vm @@ -2,6 +2,7 @@ package ${packageName}; import lombok.AllArgsConstructor; import lombok.Getter; +import net.lab1024.sa.base.common.enumeration.BaseEnum; /** * ${enumDesc} diff --git a/smart-admin-api/sa-base/src/main/resources/code-generator-template/java/controller/Controller.java.vm b/smart-admin-api/sa-base/src/main/resources/code-generator-template/java/controller/Controller.java.vm index b6296487..13ccc5b8 100644 --- a/smart-admin-api/sa-base/src/main/resources/code-generator-template/java/controller/Controller.java.vm +++ b/smart-admin-api/sa-base/src/main/resources/code-generator-template/java/controller/Controller.java.vm @@ -3,6 +3,7 @@ package ${packageName}; #foreach ($importClass in $importPackageList) $importClass #end +import cn.dev33.satoken.annotation.SaCheckPermission; import net.lab1024.sa.base.common.domain.ResponseDTO; import net.lab1024.sa.base.common.domain.PageResult; import org.springframework.web.bind.annotation.PostMapping; @@ -23,7 +24,7 @@ import javax.validation.Valid; */ @RestController -@Tag(name = "") +@Tag(name = "${basic.description}") public class ${name.upperCamel}Controller { @Resource @@ -31,6 +32,7 @@ public class ${name.upperCamel}Controller { @Operation(summary = "分页查询 @author ${basic.backendAuthor}") @PostMapping("/${name.lowerCamel}/queryPage") + @SaCheckPermission("${name.lowerCamel}:query") public ResponseDTO> queryPage(@RequestBody @Valid ${name.upperCamel}QueryForm queryForm) { return ResponseDTO.ok(${name.lowerCamel}Service.queryPage(queryForm)); } @@ -38,12 +40,14 @@ public class ${name.upperCamel}Controller { #if($insertAndUpdate.isSupportInsertAndUpdate) @Operation(summary = "添加 @author ${basic.backendAuthor}") @PostMapping("/${name.lowerCamel}/add") + @SaCheckPermission("${name.lowerCamel}:add") public ResponseDTO add(@RequestBody @Valid ${name.upperCamel}AddForm addForm) { return ${name.lowerCamel}Service.add(addForm); } @Operation(summary = "更新 @author ${basic.backendAuthor}") @PostMapping("/${name.lowerCamel}/update") + @SaCheckPermission("${name.lowerCamel}:update") public ResponseDTO update(@RequestBody @Valid ${name.upperCamel}UpdateForm updateForm) { return ${name.lowerCamel}Service.update(updateForm); } @@ -53,6 +57,7 @@ public class ${name.upperCamel}Controller { #if($deleteInfo.deleteEnum == "Batch" || $deleteInfo.deleteEnum == "SingleAndBatch") @Operation(summary = "批量删除 @author ${basic.backendAuthor}") @PostMapping("/${name.lowerCamel}/batchDelete") + @SaCheckPermission("${name.lowerCamel}:delete") public ResponseDTO batchDelete(@RequestBody ValidateList<${primaryKeyJavaType}> idList) { return ${name.lowerCamel}Service.batchDelete(idList); } @@ -61,6 +66,7 @@ public class ${name.upperCamel}Controller { #if($deleteInfo.deleteEnum == "Single" || $deleteInfo.deleteEnum == "SingleAndBatch") @Operation(summary = "单个删除 @author ${basic.backendAuthor}") @GetMapping("/${name.lowerCamel}/delete/{${primaryKeyFieldName}}") + @SaCheckPermission("${name.lowerCamel}:delete") public ResponseDTO batchDelete(@PathVariable ${primaryKeyJavaType} ${primaryKeyFieldName}) { return ${name.lowerCamel}Service.delete(${primaryKeyFieldName}); } diff --git a/smart-admin-api/sa-base/src/main/resources/code-generator-template/java/dao/Dao.java.vm b/smart-admin-api/sa-base/src/main/resources/code-generator-template/java/dao/Dao.java.vm index b14411a9..938b758e 100644 --- a/smart-admin-api/sa-base/src/main/resources/code-generator-template/java/dao/Dao.java.vm +++ b/smart-admin-api/sa-base/src/main/resources/code-generator-template/java/dao/Dao.java.vm @@ -38,14 +38,15 @@ public interface ${name.upperCamel}Dao extends BaseMapper<${name.upperCamel}Enti * 更新删除状态 */ long updateDeleted(@Param("${primaryKeyFieldName}")${primaryKeyJavaType} ${primaryKeyFieldName},@Param("${deletedFlag}")boolean deletedFlag); + #end #if($deleteInfo.deleteEnum == "Batch" || $deleteInfo.deleteEnum == "SingleAndBatch") /** * 批量更新删除状态 */ void batchUpdateDeleted(@Param("idList")List<${primaryKeyJavaType}> idList,@Param("${deletedFlag}")boolean deletedFlag); + #end #end #end - } diff --git a/smart-admin-api/sa-base/src/main/resources/code-generator-template/java/domain/entity/Entity.java.vm b/smart-admin-api/sa-base/src/main/resources/code-generator-template/java/domain/entity/Entity.java.vm index dd1fe0bb..04230b36 100644 --- a/smart-admin-api/sa-base/src/main/resources/code-generator-template/java/domain/entity/Entity.java.vm +++ b/smart-admin-api/sa-base/src/main/resources/code-generator-template/java/domain/entity/Entity.java.vm @@ -20,13 +20,19 @@ public class ${name.upperCamel}Entity { /** * $field.label */ -#if($field.primaryKeyFlag && $field.autoIncreaseFlag) + #if($field.primaryKeyFlag && $field.autoIncreaseFlag) @TableId(type = IdType.AUTO) -#end -#if($field.primaryKeyFlag && !$field.autoIncreaseFlag) + #end + #if($field.primaryKeyFlag && !$field.autoIncreaseFlag) @TableId -#end + #end + #if($field.columnName == "create_time") + @TableField(fill = FieldFill.INSERT) + #end + #if($field.columnName == "update_time") + @TableField(fill = FieldFill.INSERT_UPDATE) + #end private $field.javaType $field.fieldName; #end -} \ No newline at end of file +} diff --git a/smart-admin-api/sa-base/src/main/resources/code-generator-template/java/domain/form/QueryForm.java.vm b/smart-admin-api/sa-base/src/main/resources/code-generator-template/java/domain/form/QueryForm.java.vm index ba764981..69a247e2 100644 --- a/smart-admin-api/sa-base/src/main/resources/code-generator-template/java/domain/form/QueryForm.java.vm +++ b/smart-admin-api/sa-base/src/main/resources/code-generator-template/java/domain/form/QueryForm.java.vm @@ -14,7 +14,8 @@ $importClass */ @Data -public class ${name.upperCamel}QueryForm extends PageParam{ +@EqualsAndHashCode(callSuper = false) +public class ${name.upperCamel}QueryForm extends PageParam { #foreach ($field in $fields) #if($field.isEnum) @@ -35,4 +36,4 @@ public class ${name.upperCamel}QueryForm extends PageParam{ #end #end -} \ No newline at end of file +} diff --git a/smart-admin-api/sa-base/src/main/resources/code-generator-template/java/domain/vo/VO.java.vm b/smart-admin-api/sa-base/src/main/resources/code-generator-template/java/domain/vo/VO.java.vm index 88aed8b3..4a84efd0 100644 --- a/smart-admin-api/sa-base/src/main/resources/code-generator-template/java/domain/vo/VO.java.vm +++ b/smart-admin-api/sa-base/src/main/resources/code-generator-template/java/domain/vo/VO.java.vm @@ -17,14 +17,8 @@ public class ${name.upperCamel}VO { #foreach ($field in $fields) -#if($field.isEnum) - ${field.apiModelProperty} + ${field.apiModelProperty}$!{field.notEmpty}$!{field.dict}$!{field.file} private $field.javaType $field.fieldName; #end -#if(!$field.isEnum) - ${field.apiModelProperty}$!{field.dict}$!{field.file} - private $field.javaType $field.fieldName; -#end -#end -} \ No newline at end of file +} diff --git a/smart-admin-api/sa-base/src/main/resources/code-generator-template/java/mapper/Mapper.xml.vm b/smart-admin-api/sa-base/src/main/resources/code-generator-template/java/mapper/Mapper.xml.vm index d6809423..7a827122 100644 --- a/smart-admin-api/sa-base/src/main/resources/code-generator-template/java/mapper/Mapper.xml.vm +++ b/smart-admin-api/sa-base/src/main/resources/code-generator-template/java/mapper/Mapper.xml.vm @@ -2,10 +2,17 @@ + + + #foreach ($field in $fields) + ${tableName}.${field.columnName}#if($foreach.hasNext),#end + #end + + -#if($dao.deletedFieldUpperName != $null) - - update ${mapper.tableName} set ${mapper.deletedColumnName} = #{deletedFlag} - where ${mapper.mainKeyColumnName} in - - #{item} - - -#end - #if($deleteInfo.isSupportDelete) ### 假删除 #if(!${deleteInfo.isPhysicallyDeleted}) #if($deleteInfo.deleteEnum == "Batch" || $deleteInfo.deleteEnum == "SingleAndBatch") - update ${tableName} set deleted_flag = #{deletedFlag} where ${primaryKeyColumnName} in @@ -71,4 +72,5 @@ #end #end #end - \ No newline at end of file + + diff --git a/smart-admin-api/sa-base/src/main/resources/code-generator-template/java/service/Service.java.vm b/smart-admin-api/sa-base/src/main/resources/code-generator-template/java/service/Service.java.vm index 923e6e06..3be777d5 100644 --- a/smart-admin-api/sa-base/src/main/resources/code-generator-template/java/service/Service.java.vm +++ b/smart-admin-api/sa-base/src/main/resources/code-generator-template/java/service/Service.java.vm @@ -64,7 +64,7 @@ public class ${name.upperCamel}Service { #end #if($deleteInfo.isSupportDelete) - #if($deleteInfo.deleteEnum == "BATCH" || $deleteInfo.deleteEnum == "SingleAndBatch") + #if($deleteInfo.deleteEnum == "Batch" || $deleteInfo.deleteEnum == "SingleAndBatch") /** * 批量删除 * @@ -97,7 +97,7 @@ public class ${name.upperCamel}Service { ### 真删除 or 假删除 #if(!${deleteInfo.isPhysicallyDeleted}) - ${name.lowerCamel}Dao.updateDeleted(${primaryKeyFieldName},true); + ${name.lowerCamel}Dao.updateDeleted(${primaryKeyFieldName}, true); #end #if(${deleteInfo.isPhysicallyDeleted}) ${name.lowerCamel}Dao.deleteById(${primaryKeyFieldName}); diff --git a/smart-admin-api/sa-base/src/main/resources/code-generator-template/java/sql/Menu.sql.vm b/smart-admin-api/sa-base/src/main/resources/code-generator-template/java/sql/Menu.sql.vm new file mode 100644 index 00000000..90525f78 --- /dev/null +++ b/smart-admin-api/sa-base/src/main/resources/code-generator-template/java/sql/Menu.sql.vm @@ -0,0 +1,22 @@ +# 默认是按前端工程文件的 /views/business 文件夹的路径作为前端组件路径,如果你没把生成的 .vue 前端代码放在 /views/business 下, +# 那就根据自己实际情况修改下面 SQL 的 path,component 字段值,避免执行 SQL 后菜单无法访问。 +# 如果你一切都是按照默认,那么下面的 SQL 基本不用改 + +INSERT INTO t_menu ( menu_name, menu_type, parent_id, path, component, frame_flag, cache_flag, visible_flag, disabled_flag, perms_type, create_user_id ) +VALUES ( '${basic.description}', 2, 0, '/${name.lowerHyphenCamel}/list', '/business/${name.lowerHyphenCamel}/${name.lowerHyphenCamel}-list.vue', false, false, true, false, 1, 1 ); + +# 按菜单名称查询该菜单的 menu_id 作为按钮权限的 父菜单ID 与 功能点关联菜单ID +SET @parent_id = NULL; +SELECT t_menu.menu_id INTO @parent_id FROM t_menu WHERE t_menu.menu_name = '${basic.description}'; + +INSERT INTO t_menu ( menu_name, menu_type, parent_id, frame_flag, cache_flag, visible_flag, disabled_flag, api_perms, perms_type, context_menu_id, create_user_id ) +VALUES ( '查询', 3, @parent_id, false, true, true, false, '${name.lowerCamel}:query', 1, @parent_id, 1 ); + +INSERT INTO t_menu ( menu_name, menu_type, parent_id, frame_flag, cache_flag, visible_flag, disabled_flag, api_perms, perms_type, context_menu_id, create_user_id ) +VALUES ( '添加', 3, @parent_id, false, true, true, false, '${name.lowerCamel}:add', 1, @parent_id, 1 ); + +INSERT INTO t_menu ( menu_name, menu_type, parent_id, frame_flag, cache_flag, visible_flag, disabled_flag, api_perms, perms_type, context_menu_id, create_user_id ) +VALUES ( '更新', 3, @parent_id, false, true, true, false, '${name.lowerCamel}:update', 1, @parent_id, 1 ); + +INSERT INTO t_menu ( menu_name, menu_type, parent_id, frame_flag, cache_flag, visible_flag, disabled_flag, api_perms, perms_type, context_menu_id, create_user_id ) +VALUES ( '删除', 3, @parent_id, false, true, true, false, '${name.lowerCamel}:delete', 1, @parent_id, 1 ); diff --git a/smart-admin-api/sa-base/src/main/resources/code-generator-template/js/form.vue.vm b/smart-admin-api/sa-base/src/main/resources/code-generator-template/js/form.vue.vm index 1de462cd..7567a12e 100644 --- a/smart-admin-api/sa-base/src/main/resources/code-generator-template/js/form.vue.vm +++ b/smart-admin-api/sa-base/src/main/resources/code-generator-template/js/form.vue.vm @@ -8,7 +8,7 @@