diff --git a/README.md b/README.md index a414f9d39..c91056066 100644 --- a/README.md +++ b/README.md @@ -4,16 +4,19 @@ [![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/blob/master/LICENSE) [![使用IntelliJ IDEA开发维护](https://img.shields.io/badge/IntelliJ%20IDEA-提供支持-blue.svg)](https://www.jetbrains.com/?from=RuoYi-Vue-Plus)
-[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-3.1.0-success.svg)](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus) +[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-3.2.0-success.svg)](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus) [![Spring Boot](https://img.shields.io/badge/Spring%20Boot-2.5-blue.svg)]() -[![JDK-8+](https://img.shields.io/badge/JDK-8+-green.svg)]() +[![JDK-8+](https://img.shields.io/badge/JDK-8-green.svg)]() [![JDK-11](https://img.shields.io/badge/JDK-11-green.svg)]() +[![JDK-17](https://img.shields.io/badge/JDK-17-green.svg)]() RuoYi-Vue-Plus 是基于 RuoYi-Vue 针对 `分布式集群` 场景升级(不兼容原框架) | 功能介绍 | 使用技术 | 文档地址 | 特性注意事项 | |---|---|---|---| | 当前框架 | RuoYi-Vue-Plus | [RuoYi-Vue-Plus文档](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages) | 重写RuoYi-Vue全方位升级(不兼容原框架) | +| satoken分支 | RuoYi-Vue-Plus-satoken | [satoken分支地址](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/tree/satoken/) | 使用satoken重构权限鉴权(仅供学习不推荐上生产) | +| 单体分支 | RuoYi-Vue-Plus-fast | [fast分支地址](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/tree/fast/) | 单体应用结构 | | 原框架 | RuoYi-Vue | [RuoYi-Vue官网](http://ruoyi.vip/) | 定期同步需要的功能 | | 前端开发框架 | Vue、Element UI | [Element UI官网](https://element.eleme.cn/#/zh-CN) | | | 后端开发框架 | SpringBoot | [SpringBoot官网](https://spring.io/projects/spring-boot/#learn) | | @@ -24,10 +27,12 @@ RuoYi-Vue-Plus 是基于 RuoYi-Vue 针对 `分布式集群` 场景升级(不兼 | 数据库框架 | Mybatis-Plus | [Mybatis-Plus文档](https://baomidou.com/guide/) | 快速 CRUD 增加开发效率 | | 数据库框架 | p6spy | [p6spy官网](https://p6spy.readthedocs.io/) | 更强劲的 SQL 分析 | | 多数据源框架 | dynamic-datasource | [dynamic-ds文档](https://www.kancloud.cn/tracy5546/dynamic-datasource/content) | 支持主从与多种类数据库异构 | -| Redis客户端 | Redisson | [Redisson文档](https://github.com/redisson/redisson/wiki/%E7%9B%AE%E5%BD%95) | 性能强劲、API丰富 | | 序列化框架 | Jackson | [Jackson官网](https://github.com/FasterXML/jackson) | 统一使用 jackson 高效可靠 | | 网络框架 | Feign、OkHttp3 | [Feign官网](https://github.com/OpenFeign/feign) | 接口化管理 HTTP 请求 | +| Redis客户端 | Redisson | [Redisson文档](https://github.com/redisson/redisson/wiki/%E7%9B%AE%E5%BD%95) | 支持单机、集群配置 | +| 分布式限流 | Redisson | [Redisson文档](https://github.com/redisson/redisson/wiki/%E7%9B%AE%E5%BD%95) | 全局、请求IP、集群ID 多种限流 | | 分布式锁 | Lock4j | [Lock4j官网](https://gitee.com/baomidou/lock4j) | 注解锁、工具锁 多种多样 | +| 分布式幂等 | Lock4j | [Lock4j文档](https://gitee.com/baomidou/lock4j) | 基于分布式锁实现 | | 文件存储 | Minio | [Minio文档](https://docs.min.io/) | 本地存储 | | 文件存储 | 七牛、阿里、腾讯 | [OSS使用文档](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages?sort_id=4359146&doc_id=1469725) | 云存储 | | 监控框架 | SpringBoot-Admin | [SpringBoot-Admin文档](https://codecentric.github.io/spring-boot-admin/current/) | 全方位服务监控 | @@ -67,6 +72,7 @@ RuoYi-Vue-Plus 是基于 RuoYi-Vue 针对 `分布式集群` 场景升级(不兼 * 同步升级 RuoYi-Vue * GitHub 地址 [RuoYi-Vue-Plus-github](https://github.com/JavaLionLi/RuoYi-Vue-Plus) * 单模块 fast 分支 [RuoYi-Vue-Plus-fast](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/tree/fast/) +* satoken 分支 [RuoYi-Vue-Plus-satoken](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/tree/satoken/) * 用户扩展项目 [扩展项目列表](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages?sort_id=4478302&doc_id=1469725) ## 加群与捐献 diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 1535f3d6f..e676d5607 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -103,7 +103,7 @@ services: ipv4_address: 172.30.0.54 ruoyi-server1: - image: "ruoyi/ruoyi-server:3.1.0" + image: "ruoyi/ruoyi-server:3.2.0" container_name: ruoyi-server1 environment: # 时区上海 @@ -118,7 +118,7 @@ services: ipv4_address: 172.30.0.60 ruoyi-server2: - image: "ruoyi/ruoyi-server:3.1.0" + image: "ruoyi/ruoyi-server:3.2.0" container_name: ruoyi-server2 environment: # 时区上海 @@ -133,7 +133,7 @@ services: ipv4_address: 172.30.0.61 ruoyi-monitor-admin: - image: "ruoyi/ruoyi-monitor-admin:3.1.0" + image: "ruoyi/ruoyi-monitor-admin:3.2.0" container_name: ruoyi-monitor-admin environment: # 时区上海 diff --git a/pom.xml b/pom.xml index eba5a164c..6ccbdbeb3 100644 --- a/pom.xml +++ b/pom.xml @@ -6,40 +6,44 @@ com.ruoyi ruoyi-vue-plus - 3.1.0 + 3.2.0 RuoYi-Vue-Plus https://gitee.com/JavaLionLi/RuoYi-Vue-Plus RuoYi-Vue-Plus后台管理系统 - 3.1.0 - 2.5.4 + 3.2.0 + 2.5.5 UTF-8 UTF-8 1.8 3.2.0 1.2.6 3.0.3 + 1.5.22 4.1.2 - 2.2.10 + 2.2.11 1.7 0.9.1 - 3.4.3.3 + 3.4.3.4 3.9.1 - 5.7.11 + 5.7.13 3.0.3 11.6 4.9.1 2.5.1 - 3.16.2 + 3.16.3 2.2.1 3.4.1 + + 3.0.1 + 7.8.0 3.13.1 - 5.6.51 + 5.6.55 8.3.0 @@ -48,7 +52,7 @@ ruoyi 1.2.2 - + @@ -73,6 +77,18 @@ com.github.xiaoymin knife4j-spring-boot-starter ${knife4j.version} + + + swagger-annotations + io.swagger + + + + + + io.swagger + swagger-annotations + ${swagger-annotations.version} @@ -86,6 +102,16 @@ com.alibaba easyexcel ${easyexcel.version} + + + org.apache.poi + poi + + + org.apache.poi + poi-ooxml-schemas + + @@ -136,6 +162,12 @@ org.springframework.cloud spring-cloud-starter-openfeign ${feign.version} + + + feign-core + io.github.openfeign + + @@ -239,7 +271,12 @@ - + + + com.sun.xml.bind + jaxb-impl + ${jaxb.version} + @@ -318,48 +355,9 @@ prod warn - health,info + health, info, logfile - - - - jdk8 - - true - 1.8 - - - 1.8 - - - - jdk11 - - 11 - - - 11 - 3.0.1 - - - - - - com.sun.xml.bind - jaxb-impl - ${jaxb.version} - - - - - - - com.sun.xml.bind - jaxb-impl - - - - + \ No newline at end of file diff --git a/ruoyi-admin/pom.xml b/ruoyi-admin/pom.xml index b455a5e47..12327e305 100644 --- a/ruoyi-admin/pom.xml +++ b/ruoyi-admin/pom.xml @@ -5,7 +5,7 @@ ruoyi-vue-plus com.ruoyi - 3.1.0 + 3.2.0 4.0.0 jar @@ -36,6 +36,11 @@ ruoyi-framework + + com.ruoyi + ruoyi-system + + com.ruoyi @@ -82,7 +87,7 @@ org.apache.maven.plugins maven-war-plugin - 3.1.0 + 3.2.0 false ${project.artifactId} @@ -110,4 +115,4 @@ - + \ No newline at end of file diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java index 76141b9b7..287d4c587 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java @@ -6,8 +6,8 @@ import com.ruoyi.common.core.domain.entity.SysMenu; import com.ruoyi.common.core.domain.entity.SysUser; import com.ruoyi.common.core.domain.model.LoginBody; import com.ruoyi.common.utils.SecurityUtils; -import com.ruoyi.framework.web.service.SysLoginService; -import com.ruoyi.framework.web.service.SysPermissionService; +import com.ruoyi.system.service.SysLoginService; +import com.ruoyi.system.service.SysPermissionService; import com.ruoyi.system.service.ISysMenuService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysOssConfigController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysOssConfigController.java index 55f25dfa7..e72bd1f18 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysOssConfigController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysOssConfigController.java @@ -25,14 +25,14 @@ import javax.validation.constraints.NotNull; import java.util.Arrays; /** - * 云存储配置Controller + * 对象存储配置Controller * * @author Lion Li * @author 孤舟烟雨 * @date 2021-08-13 */ @Validated -@Api(value = "云存储配置控制器", tags = {"云存储配置管理"}) +@Api(value = "对象存储配置控制器", tags = {"对象存储配置管理"}) @RequiredArgsConstructor(onConstructor_ = @Autowired) @RestController @RequestMapping("/system/oss/config") @@ -41,9 +41,9 @@ public class SysOssConfigController extends BaseController { private final ISysOssConfigService iSysOssConfigService; /** - * 查询云存储配置列表 + * 查询对象存储配置列表 */ - @ApiOperation("查询云存储配置列表") + @ApiOperation("查询对象存储配置列表") @PreAuthorize("@ss.hasPermi('system:oss:list')") @GetMapping("/list") public TableDataInfo list(@Validated(QueryGroup.class) SysOssConfigBo bo) { @@ -51,9 +51,9 @@ public class SysOssConfigController extends BaseController { } /** - * 获取云存储配置详细信息 + * 获取对象存储配置详细信息 */ - @ApiOperation("获取云存储配置详细信息") + @ApiOperation("获取对象存储配置详细信息") @PreAuthorize("@ss.hasPermi('system:oss:query')") @GetMapping("/{ossConfigId}") public AjaxResult getInfo(@NotNull(message = "主键不能为空") @@ -62,11 +62,11 @@ public class SysOssConfigController extends BaseController { } /** - * 新增云存储配置 + * 新增对象存储配置 */ - @ApiOperation("新增云存储配置") + @ApiOperation("新增对象存储配置") @PreAuthorize("@ss.hasPermi('system:oss:add')") - @Log(title = "云存储配置", businessType = BusinessType.INSERT) + @Log(title = "对象存储配置", businessType = BusinessType.INSERT) @RepeatSubmit() @PostMapping() public AjaxResult add(@Validated(AddGroup.class) @RequestBody SysOssConfigBo bo) { @@ -74,11 +74,11 @@ public class SysOssConfigController extends BaseController { } /** - * 修改云存储配置 + * 修改对象存储配置 */ - @ApiOperation("修改云存储配置") + @ApiOperation("修改对象存储配置") @PreAuthorize("@ss.hasPermi('system:oss:edit')") - @Log(title = "云存储配置", businessType = BusinessType.UPDATE) + @Log(title = "对象存储配置", businessType = BusinessType.UPDATE) @RepeatSubmit() @PutMapping() public AjaxResult edit(@Validated(EditGroup.class) @RequestBody SysOssConfigBo bo) { @@ -86,11 +86,11 @@ public class SysOssConfigController extends BaseController { } /** - * 删除云存储配置 + * 删除对象存储配置 */ - @ApiOperation("删除云存储配置") + @ApiOperation("删除对象存储配置") @PreAuthorize("@ss.hasPermi('system:oss:remove')") - @Log(title = "云存储配置", businessType = BusinessType.DELETE) + @Log(title = "对象存储配置", businessType = BusinessType.DELETE) @DeleteMapping("/{ossConfigIds}") public AjaxResult remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ossConfigIds) { @@ -101,7 +101,7 @@ public class SysOssConfigController extends BaseController { * 状态修改 */ @PreAuthorize("@ss.hasPermi('system:oss:edit')") - @Log(title = "云存储状态修改", businessType = BusinessType.UPDATE) + @Log(title = "对象存储状态修改", businessType = BusinessType.UPDATE) @PutMapping("/changeStatus") public AjaxResult changeStatus(@RequestBody SysOssConfigBo bo) { return toAjax(iSysOssConfigService.updateOssConfigStatus(bo)); diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysOssController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysOssController.java index ff379910a..3c043534d 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysOssController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysOssController.java @@ -3,6 +3,7 @@ package com.ruoyi.web.controller.system; import cn.hutool.core.convert.Convert; import cn.hutool.core.util.ObjectUtil; +import cn.hutool.http.HttpException; import cn.hutool.http.HttpUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.ruoyi.common.annotation.Log; @@ -49,7 +50,7 @@ import java.util.Map; * @author Lion Li */ @Validated -@Api(value = "OSS云存储控制器", tags = {"OSS云存储管理"}) +@Api(value = "OSS对象存储控制器", tags = {"OSS对象存储管理"}) @RequiredArgsConstructor(onConstructor_ = @Autowired) @RestController @RequestMapping("/system/oss") @@ -59,9 +60,9 @@ public class SysOssController extends BaseController { private final ISysConfigService iSysConfigService; /** - * 查询OSS云存储列表 + * 查询OSS对象存储列表 */ - @ApiOperation("查询OSS云存储列表") + @ApiOperation("查询OSS对象存储列表") @PreAuthorize("@ss.hasPermi('system:oss:list')") @GetMapping("/list") public TableDataInfo list(@Validated(QueryGroup.class) SysOssBo bo) { @@ -69,14 +70,14 @@ public class SysOssController extends BaseController { } /** - * 上传OSS云存储 + * 上传OSS对象存储 */ - @ApiOperation("上传OSS云存储") + @ApiOperation("上传OSS对象存储") @ApiImplicitParams({ @ApiImplicitParam(name = "file", value = "文件", dataType = "java.io.File", required = true), }) @PreAuthorize("@ss.hasPermi('system:oss:upload')") - @Log(title = "OSS云存储", businessType = BusinessType.INSERT) + @Log(title = "OSS对象存储", businessType = BusinessType.INSERT) @RepeatSubmit @PostMapping("/upload") public AjaxResult> upload(@RequestPart("file") MultipartFile file) { @@ -90,7 +91,7 @@ public class SysOssController extends BaseController { return AjaxResult.success(map); } - @ApiOperation("下载OSS云存储") + @ApiOperation("下载OSS对象存储") @PreAuthorize("@ss.hasPermi('system:oss:download')") @GetMapping("/download/{ossId}") public void download(@PathVariable Long ossId, HttpServletResponse response) throws IOException { @@ -101,18 +102,27 @@ public class SysOssController extends BaseController { response.reset(); response.addHeader("Access-Control-Allow-Origin", "*"); response.addHeader("Access-Control-Expose-Headers", "Content-Disposition"); - FileUtils.setAttachmentResponseHeader(response, URLEncoder.encode(sysOss.getOriginalName(), StandardCharsets.UTF_8.toString())); + FileUtils.setAttachmentResponseHeader(response, sysOss.getOriginalName()); response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE + "; charset=UTF-8"); - long data = HttpUtil.download(sysOss.getUrl(), response.getOutputStream(), false); + long data; + try { + data = HttpUtil.download(sysOss.getUrl(), response.getOutputStream(), false); + } catch (HttpException e) { + if (e.getMessage().contains("403")) { + throw new ServiceException("无读取权限, 请在对应的OSS开启'公有读'权限!"); + } else { + throw new ServiceException(e.getMessage()); + } + } response.setContentLength(Convert.toInt(data)); } /** - * 删除OSS云存储 + * 删除OSS对象存储 */ - @ApiOperation("删除OSS云存储") + @ApiOperation("删除OSS对象存储") @PreAuthorize("@ss.hasPermi('system:oss:remove')") - @Log(title = "OSS云存储" , businessType = BusinessType.DELETE) + @Log(title = "OSS对象存储" , businessType = BusinessType.DELETE) @DeleteMapping("/{ossIds}") public AjaxResult remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ossIds) { @@ -124,7 +134,7 @@ public class SysOssController extends BaseController { */ @ApiOperation("变更图片列表预览状态") @PreAuthorize("@ss.hasPermi('system:oss:edit')") - @Log(title = "OSS云存储" , businessType = BusinessType.UPDATE) + @Log(title = "OSS对象存储" , businessType = BusinessType.UPDATE) @PutMapping("/changePreviewListResource") public AjaxResult changePreviewListResource(@RequestBody String body) { Map map = JsonUtils.parseMap(body); diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java index 422355da0..66dfd0791 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java @@ -7,9 +7,9 @@ import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.domain.entity.SysUser; import com.ruoyi.common.core.domain.model.LoginUser; import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.core.service.TokenService; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.framework.web.service.TokenService; import com.ruoyi.system.domain.SysOss; import com.ruoyi.system.service.ISysOssService; import com.ruoyi.system.service.ISysUserService; diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRegisterController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRegisterController.java index 4c2cc7c66..64904a0cc 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRegisterController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRegisterController.java @@ -4,7 +4,7 @@ import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.domain.model.RegisterBody; import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.framework.web.service.SysRegisterService; +import com.ruoyi.system.service.SysRegisterService; import com.ruoyi.system.service.ISysConfigService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java index b1cd26380..e5f40326c 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java @@ -9,13 +9,13 @@ import com.ruoyi.common.core.domain.entity.SysUser; import com.ruoyi.common.core.domain.model.LoginUser; import com.ruoyi.common.core.page.TableDataInfo; import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.core.service.TokenService; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.poi.ExcelUtil; -import com.ruoyi.framework.web.service.SysPermissionService; -import com.ruoyi.framework.web.service.TokenService; import com.ruoyi.system.domain.SysUserRole; import com.ruoyi.system.service.ISysRoleService; import com.ruoyi.system.service.ISysUserService; +import com.ruoyi.system.service.SysPermissionService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; diff --git a/ruoyi-admin/src/main/resources/application-dev.yml b/ruoyi-admin/src/main/resources/application-dev.yml index 780499400..eb6962f1c 100644 --- a/ruoyi-admin/src/main/resources/application-dev.yml +++ b/ruoyi-admin/src/main/resources/application-dev.yml @@ -70,7 +70,7 @@ spring: config: multi-statement-allow: true ---- # redis 配置 +--- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉) spring: redis: # 地址 @@ -118,6 +118,67 @@ redisson: # DNS监测时间间隔,单位:毫秒 dnsMonitoringInterval: 5000 +#--- # redis 集群配置(单机与集群只能开启一个另一个需要注释掉) +#spring: +# redis: +# cluster: +# nodes: +# - 192.168.0.100:6379 +# - 192.168.0.101:6379 +# - 192.168.0.102:6379 +# # 密码 +# password: +# # 连接超时时间 +# timeout: 10s +# # 是否开启ssl +# ssl: false +# +#redisson: +# # 线程池数量 +# threads: 16 +# # Netty线程池数量 +# nettyThreads: 32 +# # 传输模式 +# transportMode: "NIO" +# # 集群配置 +# clusterServersConfig: +# # 客户端名称 +# clientName: ${ruoyi.name} +# # master最小空闲连接数 +# masterConnectionMinimumIdleSize: 32 +# # master连接池大小 +# masterConnectionPoolSize: 64 +# # slave最小空闲连接数 +# slaveConnectionMinimumIdleSize: 32 +# # slave连接池大小 +# slaveConnectionPoolSize: 64 +# # 连接空闲超时,单位:毫秒 +# idleConnectionTimeout: 10000 +# # ping连接间隔 +# pingConnectionInterval: 1000 +# # 命令等待超时,单位:毫秒 +# timeout: 3000 +# # 如果尝试在此限制之内发送成功,则开始启用 timeout 计时。 +# retryAttempts: 3 +# # 命令重试发送时间间隔,单位:毫秒 +# retryInterval: 1500 +# # 从可用服务器的内部列表中排除 Redis Slave 重新连接尝试的间隔。 +# failedSlaveReconnectionInterval: 3000 +# # 发布和订阅连接池最小空闲连接数 +# subscriptionConnectionMinimumIdleSize: 1 +# # 发布和订阅连接池大小 +# subscriptionConnectionPoolSize: 50 +# # 单个连接最大订阅数量 +# subscriptionsPerConnection: 5 +# # 扫描间隔 +# scanInterval: 1000 +# # DNS监测时间间隔,单位:毫秒 +# dnsMonitoringInterval: 5000 +# # 读取模式 +# readMode: "SLAVE" +# # 订阅模式 +# subscriptionMode: "MASTER" + --- # 监控配置 spring: boot: diff --git a/ruoyi-admin/src/main/resources/application-prod.yml b/ruoyi-admin/src/main/resources/application-prod.yml index 446d54d45..13a2da878 100644 --- a/ruoyi-admin/src/main/resources/application-prod.yml +++ b/ruoyi-admin/src/main/resources/application-prod.yml @@ -70,7 +70,7 @@ spring: config: multi-statement-allow: true ---- # redis 配置 +--- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉) spring: redis: # 地址 @@ -118,6 +118,67 @@ redisson: # DNS监测时间间隔,单位:毫秒 dnsMonitoringInterval: 5000 +#--- # redis 集群配置(单机与集群只能开启一个另一个需要注释掉) +#spring: +# redis: +# cluster: +# nodes: +# - 192.168.0.100:6379 +# - 192.168.0.101:6379 +# - 192.168.0.102:6379 +# # 密码 +# password: +# # 连接超时时间 +# timeout: 10s +# # 是否开启ssl +# ssl: false +# +#redisson: +# # 线程池数量 +# threads: 16 +# # Netty线程池数量 +# nettyThreads: 32 +# # 传输模式 +# transportMode: "NIO" +# # 集群配置 +# clusterServersConfig: +# # 客户端名称 +# clientName: ${ruoyi.name} +# # master最小空闲连接数 +# masterConnectionMinimumIdleSize: 32 +# # master连接池大小 +# masterConnectionPoolSize: 64 +# # slave最小空闲连接数 +# slaveConnectionMinimumIdleSize: 32 +# # slave连接池大小 +# slaveConnectionPoolSize: 64 +# # 连接空闲超时,单位:毫秒 +# idleConnectionTimeout: 10000 +# # ping连接间隔 +# pingConnectionInterval: 1000 +# # 命令等待超时,单位:毫秒 +# timeout: 3000 +# # 如果尝试在此限制之内发送成功,则开始启用 timeout 计时。 +# retryAttempts: 3 +# # 命令重试发送时间间隔,单位:毫秒 +# retryInterval: 1500 +# # 从可用服务器的内部列表中排除 Redis Slave 重新连接尝试的间隔。 +# failedSlaveReconnectionInterval: 3000 +# # 发布和订阅连接池最小空闲连接数 +# subscriptionConnectionMinimumIdleSize: 1 +# # 发布和订阅连接池大小 +# subscriptionConnectionPoolSize: 50 +# # 单个连接最大订阅数量 +# subscriptionsPerConnection: 5 +# # 扫描间隔 +# scanInterval: 1000 +# # DNS监测时间间隔,单位:毫秒 +# dnsMonitoringInterval: 5000 +# # 读取模式 +# readMode: "SLAVE" +# # 订阅模式 +# subscriptionMode: "MASTER" + --- # 监控配置 spring: boot: diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml index deffa273b..145469d31 100644 --- a/ruoyi-admin/src/main/resources/application.yml +++ b/ruoyi-admin/src/main/resources/application.yml @@ -106,10 +106,32 @@ token: # 令牌有效期(默认30分钟) expireTime: 30 +# security配置 +security: + # 登出路径 + logout-url: /logout + # 匿名路径 + anonymous: + - /login + - /register + - /captchaImage + # swagger 文档配置 + - /doc.html + - /swagger-resources/** + - /webjars/** + - /*/api-docs + # druid 监控配置 + - /druid/** + # actuator 监控配置 + - /actuator + - /actuator/** + # 用户放行 + permit-all: + # 重复提交 repeat-submit: # 全局间隔时间(毫秒) - intervalTime: 1000 + interval: 5000 # MyBatisPlus配置 # https://baomidou.com/config/ @@ -221,6 +243,11 @@ swagger: name: Lion Li email: crazylionli@163.com url: https://gitee.com/JavaLionLi/RuoYi-Vue-Plus + groups: + - name: 演示案例 + basePackage: com.ruoyi.demo + - name: 系统模块 + basePackage: com.ruoyi.admin # 防止XSS攻击 xss: @@ -244,7 +271,7 @@ thread-pool: # 线程池维护线程所允许的空闲时间 keepAliveSeconds: 300 # 线程池对拒绝任务(无线程可用)的处理策略 - # CALLER_RUNS_POLICY 等待 + # CALLER_RUNS_POLICY 调用方执行 # DISCARD_OLDEST_POLICY 放弃最旧的 # DISCARD_POLICY 丢弃 # ABORT_POLICY 中止 diff --git a/ruoyi-common/pom.xml b/ruoyi-common/pom.xml index 89f56b169..f4f7ab93e 100644 --- a/ruoyi-common/pom.xml +++ b/ruoyi-common/pom.xml @@ -5,7 +5,7 @@ ruoyi-vue-plus com.ruoyi - 3.1.0 + 3.2.0 4.0.0 @@ -131,6 +131,11 @@ knife4j-spring-boot-starter + + io.swagger + swagger-annotations + + org.springframework.boot spring-boot-starter-actuator @@ -147,22 +152,10 @@ redisson-spring-boot-starter - - - com.baomidou - dynamic-datasource-spring-boot-starter - - - - p6spy - p6spy - - com.baomidou lock4j-redisson-spring-boot-starter - diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Log.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Log.java index eb05dc7ce..ca02c6c4a 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Log.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Log.java @@ -38,4 +38,9 @@ public @interface Log * 是否保存请求的参数 */ public boolean isSaveRequestData() default true; + + /** + * 是否保存响应的参数 + */ + public boolean isSaveResponseData() default true; } diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/RepeatSubmit.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/RepeatSubmit.java index c2bbee476..512f4bf52 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/RepeatSubmit.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/RepeatSubmit.java @@ -20,10 +20,15 @@ import java.util.concurrent.TimeUnit; public @interface RepeatSubmit { /** - * 默认使用全局配置 + * 间隔时间(ms),小于此时间视为重复提交 */ - int intervalTime() default 0; + int interval() default 5000; TimeUnit timeUnit() default TimeUnit.MILLISECONDS; + /** + * 提示消息 + */ + String message() default "不允许重复提交,请稍后再试"; + } diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/GenConstants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/GenConstants.java index 1fa328e45..072fb403c 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/constant/GenConstants.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/GenConstants.java @@ -44,16 +44,21 @@ public class GenConstants public static final String[] COLUMNTYPE_NUMBER = { "tinyint", "smallint", "mediumint", "int", "number", "integer", "bit", "bigint", "float", "double", "decimal" }; + /** 页面不需要添加字段 */ + public static final String[] COLUMNNAME_NOT_ADD = { "create_by", "create_time", "del_flag", "update_by", + "update_time", "version" }; + /** 页面不需要编辑字段 */ - public static final String[] COLUMNNAME_NOT_EDIT = { "id", "create_by", "create_time", "del_flag" }; + public static final String[] COLUMNNAME_NOT_EDIT = { "create_by", "create_time", "del_flag", "update_by", + "update_time", "version" }; /** 页面不需要显示的列表字段 */ public static final String[] COLUMNNAME_NOT_LIST = { "id", "create_by", "create_time", "del_flag", "update_by", - "update_time" }; + "update_time", "version" }; /** 页面不需要查询字段 */ public static final String[] COLUMNNAME_NOT_QUERY = { "id", "create_by", "create_time", "del_flag", "update_by", - "update_time", "remark" }; + "update_time", "remark", "version" }; /** Entity基类字段 */ public static final String[] BASE_ENTITY = { "createBy", "createTime", "updateBy", "updateTime", "remark" }; diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java index 72d624a9c..a5a92f4d3 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java @@ -1,5 +1,6 @@ package com.ruoyi.common.core.domain; +import com.fasterxml.jackson.annotation.JsonIgnore; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.NoArgsConstructor; @@ -62,6 +63,7 @@ public class BaseEntity implements Serializable { /** * 请求参数 */ + @JsonIgnore @ApiModelProperty(value = "请求参数") private Map params = new HashMap<>(); diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/dto/OperLogDTO.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/dto/OperLogDTO.java new file mode 100644 index 000000000..f5132efe6 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/dto/OperLogDTO.java @@ -0,0 +1,107 @@ +package com.ruoyi.common.core.domain.dto; + +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.Date; + +/** + * 操作日志记录表 oper_log + * + * @author ruoyi + */ + +@Data +@NoArgsConstructor +@Accessors(chain = true) +public class OperLogDTO implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 日志主键 + */ + private Long operId; + + /** + * 操作模块 + */ + private String title; + + /** + * 业务类型(0其它 1新增 2修改 3删除) + */ + private Integer businessType; + + /** + * 业务类型数组 + */ + private Integer[] businessTypes; + + /** + * 请求方法 + */ + private String method; + + /** + * 请求方式 + */ + private String requestMethod; + + /** + * 操作类别(0其它 1后台用户 2手机端用户) + */ + private Integer operatorType; + + /** + * 操作人员 + */ + private String operName; + + /** + * 部门名称 + */ + private String deptName; + + /** + * 请求url + */ + private String operUrl; + + /** + * 操作地址 + */ + private String operIp; + + /** + * 操作地点 + */ + private String operLocation; + + /** + * 请求参数 + */ + private String operParam; + + /** + * 返回参数 + */ + private String jsonResult; + + /** + * 操作状态(0正常 1异常) + */ + private Integer status; + + /** + * 错误消息 + */ + private String errorMsg; + + /** + * 操作时间 + */ + private Date operTime; + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictData.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictData.java index e8bc0f156..89ee1c1bc 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictData.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictData.java @@ -90,7 +90,7 @@ public class SysDictData implements Serializable { * 状态(0正常 1停用) */ @ExcelProperty(value = "状态", converter = ExcelDictConvert.class) - @ExcelDictFormat(dictType = "sys_common_status") + @ExcelDictFormat(dictType = "sys_normal_disable") private String status; /** diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictType.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictType.java index 1108671d8..35d9a60fe 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictType.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictType.java @@ -57,7 +57,7 @@ public class SysDictType implements Serializable { * 状态(0正常 1停用) */ @ExcelProperty(value = "状态", converter = ExcelDictConvert.class) - @ExcelDictFormat(dictType = "sys_common_status") + @ExcelDictFormat(dictType = "sys_normal_disable") private String status; /** diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java index 6fc86418e..d2c5bbf94 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java @@ -66,6 +66,11 @@ public class SysMenu implements Serializable { @Size(min = 0, max = 200, message = "组件路径不能超过255个字符") private String component; + /** + * 路由参数 + */ + private String query; + /** * 是否为外链(0是 1否) */ diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java index 6e44a3b95..770998126 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java @@ -91,11 +91,6 @@ public class SysUser implements Serializable { return password; } - /** - * 盐加密 - */ - private String salt; - /** * 帐号状态(0正常 1停用) */ diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java deleted file mode 100644 index 23a25e045..000000000 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java +++ /dev/null @@ -1,260 +0,0 @@ -package com.ruoyi.common.core.redis; - -import com.google.common.collect.Lists; -import org.redisson.api.*; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.TimeUnit; -import java.util.function.Consumer; - -/** - * spring redis 工具类 - * - * @author shenxinquan - * @see com.ruoyi.common.utils.RedisUtils - * @deprecated 3.2.0 删除此类 - **/ -@SuppressWarnings(value = {"unchecked", "rawtypes"}) -@Component -@Deprecated -public class RedisCache { - - @Autowired - private RedissonClient redissonClient; - - /** - * 发布通道消息 - * - * @param channelKey 通道key - * @param msg 发送数据 - * @param consumer 自定义处理 - */ - public void publish(String channelKey, T msg, Consumer consumer) { - RTopic topic = redissonClient.getTopic(channelKey); - topic.publish(msg); - consumer.accept(msg); - } - - public void publish(String channelKey, T msg) { - RTopic topic = redissonClient.getTopic(channelKey); - topic.publish(msg); - } - - /** - * 订阅通道接收消息 - * - * @param channelKey 通道key - * @param clazz 消息类型 - * @param consumer 自定义处理 - */ - public void subscribe(String channelKey, Class clazz, Consumer consumer) { - RTopic topic = redissonClient.getTopic(channelKey); - topic.addListener(clazz, (channel, msg) -> consumer.accept(msg)); - } - - /** - * 缓存基本的对象,Integer、String、实体类等 - * - * @param key 缓存的键值 - * @param value 缓存的值 - */ - public void setCacheObject(final String key, final T value) { - redissonClient.getBucket(key).set(value); - } - - /** - * 缓存基本的对象,Integer、String、实体类等 - * - * @param key 缓存的键值 - * @param value 缓存的值 - * @param timeout 时间 - * @param timeUnit 时间颗粒度 - */ - public void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) { - RBucket result = redissonClient.getBucket(key); - result.set(value); - result.expire(timeout, timeUnit); - } - - /** - * 设置有效时间 - * - * @param key Redis键 - * @param timeout 超时时间 - * @return true=设置成功;false=设置失败 - */ - public boolean expire(final String key, final long timeout) { - return expire(key, timeout, TimeUnit.SECONDS); - } - - /** - * 设置有效时间 - * - * @param key Redis键 - * @param timeout 超时时间 - * @param unit 时间单位 - * @return true=设置成功;false=设置失败 - */ - public boolean expire(final String key, final long timeout, final TimeUnit unit) { - RBucket rBucket = redissonClient.getBucket(key); - return rBucket.expire(timeout, unit); - } - - /** - * 获得缓存的基本对象。 - * - * @param key 缓存键值 - * @return 缓存键值对应的数据 - */ - public T getCacheObject(final String key) { - RBucket rBucket = redissonClient.getBucket(key); - return rBucket.get(); - } - - /** - * 删除单个对象 - * - * @param key - */ - public boolean deleteObject(final String key) { - return redissonClient.getBucket(key).delete(); - } - - /* */ - - /** - * 删除集合对象 - * - * @param collection 多个对象 - * @return - */ - public void deleteObject(final Collection collection) { - RBatch batch = redissonClient.createBatch(); - collection.forEach(t->{ - batch.getBucket(t.toString()).deleteAsync(); - }); - batch.execute(); - } - - /** - * 缓存List数据 - * - * @param key 缓存的键值 - * @param dataList 待缓存的List数据 - * @return 缓存的对象 - */ - public boolean setCacheList(final String key, final List dataList) { - RList rList = redissonClient.getList(key); - return rList.addAll(dataList); - } - - /** - * 获得缓存的list对象 - * - * @param key 缓存的键值 - * @return 缓存键值对应的数据 - */ - public List getCacheList(final String key) { - RList rList = redissonClient.getList(key); - return rList.readAll(); - } - - /** - * 缓存Set - * - * @param key 缓存键值 - * @param dataSet 缓存的数据 - * @return 缓存数据的对象 - */ - public boolean setCacheSet(final String key, final Set dataSet) { - RSet rSet = redissonClient.getSet(key); - return rSet.addAll(dataSet); - } - - /** - * 获得缓存的set - * - * @param key - * @return - */ - public Set getCacheSet(final String key) { - RSet rSet = redissonClient.getSet(key); - return rSet.readAll(); - } - - /** - * 缓存Map - * - * @param key - * @param dataMap - */ - public void setCacheMap(final String key, final Map dataMap) { - if (dataMap != null) { - RMap rMap = redissonClient.getMap(key); - rMap.putAll(dataMap); - } - } - - /** - * 获得缓存的Map - * - * @param key - * @return - */ - public Map getCacheMap(final String key) { - RMap rMap = redissonClient.getMap(key); - return rMap.getAll(rMap.keySet()); - } - - /** - * 往Hash中存入数据 - * - * @param key Redis键 - * @param hKey Hash键 - * @param value 值 - */ - public void setCacheMapValue(final String key, final String hKey, final T value) { - RMap rMap = redissonClient.getMap(key); - rMap.put(hKey, value); - } - - /** - * 获取Hash中的数据 - * - * @param key Redis键 - * @param hKey Hash键 - * @return Hash中的对象 - */ - public T getCacheMapValue(final String key, final String hKey) { - RMap rMap = redissonClient.getMap(key); - return rMap.get(hKey); - } - - /** - * 获取多个Hash中的数据 - * - * @param key Redis键 - * @param hKeys Hash键集合 - * @return Hash对象集合 - */ - public Map getMultiCacheMapValue(final String key, final Set hKeys) { - RMap rMap = redissonClient.getMap(key); - return rMap.getAll(hKeys); - } - - /** - * 获得缓存的基本对象列表 - * - * @param pattern 字符串前缀 - * @return 对象列表 - */ - public Collection keys(final String pattern) { - Iterable iterable = redissonClient.getKeys().getKeysByPattern(pattern); - return Lists.newArrayList(iterable); - } -} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/service/LogininforService.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/service/LogininforService.java new file mode 100644 index 000000000..1bf34d526 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/service/LogininforService.java @@ -0,0 +1,9 @@ +package com.ruoyi.common.core.service; + +import javax.servlet.http.HttpServletRequest; + +public interface LogininforService { + + void recordLogininfor(String username, String status, String message, + HttpServletRequest request, Object... args); +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/service/OperLogService.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/service/OperLogService.java new file mode 100644 index 000000000..71e5647ee --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/service/OperLogService.java @@ -0,0 +1,9 @@ +package com.ruoyi.common.core.service; + +import com.ruoyi.common.core.domain.dto.OperLogDTO; +import org.springframework.scheduling.annotation.Async; + +public interface OperLogService { + @Async + void recordOper(OperLogDTO operLogDTO); +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/service/TokenService.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/service/TokenService.java new file mode 100644 index 000000000..d9a9f0acf --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/service/TokenService.java @@ -0,0 +1,69 @@ +package com.ruoyi.common.core.service; + +import com.ruoyi.common.core.domain.model.LoginUser; + +import javax.servlet.http.HttpServletRequest; + +/** + * token验证处理 + * + * @author Lion Li + */ +public interface TokenService { + + /** + * 获取用户身份信息 + * + * @return 用户信息 + */ + LoginUser getLoginUser(HttpServletRequest request); + + /** + * 设置用户身份信息 + */ + void setLoginUser(LoginUser loginUser); + + /** + * 删除用户身份信息 + */ + void delLoginUser(String token); + + /** + * 创建令牌 + * + * @param loginUser 用户信息 + * @return 令牌 + */ + String createToken(LoginUser loginUser); + + /** + * 验证令牌有效期,相差不足20分钟,自动刷新缓存 + * + * @param loginUser + * @return 令牌 + */ + void verifyToken(LoginUser loginUser); + + /** + * 刷新令牌有效期 + * + * @param loginUser 登录信息 + */ + void refreshToken(LoginUser loginUser); + + /** + * 设置用户代理信息 + * + * @param loginUser 登录信息 + */ + void setUserAgent(LoginUser loginUser); + + /** + * 从令牌中获取用户名 + * + * @param token 令牌 + * @return 用户名 + */ + String getUsernameFromToken(String token); + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/LimitType.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/LimitType.java index ad01659dc..02c2b3d30 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/enums/LimitType.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/LimitType.java @@ -16,5 +16,10 @@ public enum LimitType /** * 根据请求者IP进行限流 */ - IP + IP, + + /** + * 实例限流(集群多后端实例) + */ + CLUSTER } diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/ThreadPoolRejectedPolicy.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/ThreadPoolRejectedPolicy.java index 0c40f341c..5529bb336 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/enums/ThreadPoolRejectedPolicy.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/ThreadPoolRejectedPolicy.java @@ -15,7 +15,7 @@ import java.util.concurrent.ThreadPoolExecutor; @AllArgsConstructor public enum ThreadPoolRejectedPolicy { - CALLER_RUNS_POLICY("等待", ThreadPoolExecutor.CallerRunsPolicy.class), + CALLER_RUNS_POLICY("调用方执行", ThreadPoolExecutor.CallerRunsPolicy.class), DISCARD_OLDEST_POLICY("放弃最旧的", ThreadPoolExecutor.DiscardOldestPolicy.class), DISCARD_POLICY("丢弃", ThreadPoolExecutor.DiscardPolicy.class), ABORT_POLICY("中止", ThreadPoolExecutor.AbortPolicy.class); diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/TokenProperties.java b/ruoyi-common/src/main/java/com/ruoyi/common/properties/TokenProperties.java similarity index 91% rename from ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/TokenProperties.java rename to ruoyi-common/src/main/java/com/ruoyi/common/properties/TokenProperties.java index f695c1c62..927a9413d 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/TokenProperties.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/properties/TokenProperties.java @@ -1,4 +1,4 @@ -package com.ruoyi.framework.config.properties; +package com.ruoyi.common.properties; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/RedisUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/RedisUtils.java index 5fcfb9dee..769ee7153 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/RedisUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/RedisUtils.java @@ -25,6 +25,32 @@ public class RedisUtils { private static RedissonClient client = SpringUtils.getBean(RedissonClient.class); + /** + * 限流 + * + * @param key 限流key + * @param rateType 限流类型 + * @param rate 速率 + * @param rateInterval 速率间隔 + * @return -1 表示失败 + */ + public static long rateLimiter(String key, RateType rateType, int rate, int rateInterval) { + RRateLimiter rateLimiter = client.getRateLimiter(key); + rateLimiter.trySetRate(rateType, rate, rateInterval, RateIntervalUnit.SECONDS); + if (rateLimiter.tryAcquire()) { + return rateLimiter.availablePermits(); + } else { + return -1L; + } + } + + /** + * 获取实例id + */ + public static String getClientId() { + return client.getId(); + } + /** * 发布通道消息 * diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java index dc3aea2c0..3ab304c0c 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java @@ -35,6 +35,7 @@ public class FileUtils extends FileUtil .append(percentEncodedFileName); response.setHeader("Content-disposition", contentDispositionValue.toString()); + response.setHeader("download-filename", percentEncodedFileName); } /** diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java index 03c608af8..bd7522c51 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java @@ -27,6 +27,9 @@ public class AddressUtils { public static String getRealAddressByIP(String ip) { String address = UNKNOWN; + if (StringUtils.isBlank(ip)){ + return address; + } // 内网不查询 ip = "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : HtmlUtil.cleanHtmlTag(ip); if (NetUtil.isInnerIP(ip)) { diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java index 4060b1794..e3001c605 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java @@ -46,7 +46,7 @@ public class ExcelUtil { response.reset(); response.addHeader("Access-Control-Allow-Origin", "*"); response.addHeader("Access-Control-Expose-Headers", "Content-Disposition"); - FileUtils.setAttachmentResponseHeader(response, URLEncoder.encode(filename, StandardCharsets.UTF_8.toString())); + FileUtils.setAttachmentResponseHeader(response, filename); response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8"); ServletOutputStream os = response.getOutputStream(); EasyExcel.write(os, clazz) diff --git a/ruoyi-demo/pom.xml b/ruoyi-demo/pom.xml index 2eff3c2db..339cbde44 100644 --- a/ruoyi-demo/pom.xml +++ b/ruoyi-demo/pom.xml @@ -5,7 +5,7 @@ ruoyi-vue-plus com.ruoyi - 3.1.0 + 3.2.0 4.0.0 diff --git a/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisCacheController.java b/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisCacheController.java index 6877aed2d..5ef9d49cd 100644 --- a/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisCacheController.java +++ b/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisCacheController.java @@ -34,6 +34,9 @@ public class RedisCacheController { * 如果没有,就调用方法,然后把结果缓存起来 * 这个注解「一般用在查询方法上」 * + * 重点说明: 缓存注解严谨与其他筛选数据功能一起使用 + * 例如: 数据权限注解 会造成 缓存击穿 与 数据不一致问题 + * * cacheNames 为配置文件内 groupId */ @ApiOperation("测试 @Cacheable") diff --git a/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisRateLimiterController.java b/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisRateLimiterController.java new file mode 100644 index 000000000..33d75093b --- /dev/null +++ b/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisRateLimiterController.java @@ -0,0 +1,58 @@ +package com.ruoyi.demo.controller; + +import com.ruoyi.common.annotation.RateLimiter; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.enums.LimitType; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + + +/** + * 测试分布式限流样例 + * + * @author Lion Li + */ +@Api(value = "测试分布式限流样例", tags = {"测试分布式限流样例"}) +@Slf4j +@RestController +@RequestMapping("/demo/rateLimiter") +public class RedisRateLimiterController { + + /** + * 测试全局限流 + * 全局影响 + */ + @ApiOperation("测试全局限流") + @RateLimiter(count = 2, time = 10) + @GetMapping("/test") + public AjaxResult test(String value){ + return AjaxResult.success("操作成功",value); + } + + /** + * 测试请求IP限流 + * 同一IP请求受影响 + */ + @ApiOperation("测试请求IP限流") + @RateLimiter(count = 2, time = 10, limitType = LimitType.IP) + @GetMapping("/testip") + public AjaxResult testip(String value){ + return AjaxResult.success("操作成功",value); + } + + /** + * 测试集群实例限流 + * 启动两个后端服务互不影响 + */ + @ApiOperation("测试集群实例限流") + @RateLimiter(count = 2, time = 10, limitType = LimitType.CLUSTER) + @GetMapping("/testcluster") + public AjaxResult testcluster(String value){ + return AjaxResult.success("操作成功",value); + } + +} diff --git a/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestDemoController.java b/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestDemoController.java index 563704e05..a3a9d2224 100644 --- a/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestDemoController.java +++ b/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestDemoController.java @@ -96,7 +96,7 @@ public class TestDemoController extends BaseController { @ApiOperation("新增测试单表") @PreAuthorize("@ss.hasPermi('demo:demo:add')") @Log(title = "测试单表", businessType = BusinessType.INSERT) - @RepeatSubmit(intervalTime = 2, timeUnit = TimeUnit.SECONDS) + @RepeatSubmit(interval = 2, timeUnit = TimeUnit.SECONDS, message = "不允许重复提交") @PostMapping() public AjaxResult add(@Validated(AddGroup.class) @RequestBody TestDemoBo bo) { return toAjax(iTestDemoService.insertByBo(bo) ? 1 : 0); diff --git a/ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestDemoServiceImpl.java b/ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestDemoServiceImpl.java index fe103ef88..4280d509d 100644 --- a/ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestDemoServiceImpl.java +++ b/ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestDemoServiceImpl.java @@ -75,7 +75,11 @@ public class TestDemoServiceImpl extends ServicePlusImpl ruoyi-vue-plus com.ruoyi - 3.1.0 + 3.2.0 4.0.0 ruoyi-extend diff --git a/ruoyi-extend/ruoyi-monitor-admin/pom.xml b/ruoyi-extend/ruoyi-monitor-admin/pom.xml index dc0830373..d63b110b9 100644 --- a/ruoyi-extend/ruoyi-monitor-admin/pom.xml +++ b/ruoyi-extend/ruoyi-monitor-admin/pom.xml @@ -5,7 +5,7 @@ ruoyi-extend com.ruoyi - 3.1.0 + 3.2.0 4.0.0 jar diff --git a/ruoyi-framework/pom.xml b/ruoyi-framework/pom.xml index 918a4cc58..20176a0be 100644 --- a/ruoyi-framework/pom.xml +++ b/ruoyi-framework/pom.xml @@ -5,7 +5,7 @@ ruoyi-vue-plus com.ruoyi - 3.1.0 + 3.2.0 4.0.0 @@ -61,12 +61,23 @@ druid-spring-boot-starter + + + com.baomidou + dynamic-datasource-spring-boot-starter + + + + p6spy + p6spy + + com.ruoyi - ruoyi-system + ruoyi-common - + \ No newline at end of file diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java index a2c101e3d..e4a6cdcdb 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java @@ -9,14 +9,10 @@ import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.reflect.ReflectUtils; import org.aspectj.lang.JoinPoint; -import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; -import org.aspectj.lang.annotation.Pointcut; -import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; -import java.lang.reflect.Method; import java.util.Map; /** @@ -58,23 +54,13 @@ public class DataScopeAspect { */ public static final String DATA_SCOPE = "dataScope"; - // 配置织入点 - @Pointcut("@annotation(com.ruoyi.common.annotation.DataScope)") - public void dataScopePointCut() { - } - - @Before("dataScopePointCut()") - public void doBefore(JoinPoint point) throws Throwable { + @Before("@annotation(controllerDataScope)") + public void doBefore(JoinPoint point, DataScope controllerDataScope) throws Throwable { clearDataScope(point); - handleDataScope(point); + handleDataScope(point, controllerDataScope); } - protected void handleDataScope(final JoinPoint joinPoint) { - // 获得注解 - DataScope controllerDataScope = getAnnotationLog(joinPoint); - if (controllerDataScope == null) { - return; - } + protected void handleDataScope(final JoinPoint joinPoint, DataScope controllerDataScope) { // 获取当前的用户 LoginUser loginUser = SecurityUtils.getLoginUser(); if (StringUtils.isNotNull(loginUser)) { @@ -133,20 +119,6 @@ public class DataScopeAspect { } } - /** - * 是否存在注解,如果存在就获取 - */ - private DataScope getAnnotationLog(JoinPoint joinPoint) { - Signature signature = joinPoint.getSignature(); - MethodSignature methodSignature = (MethodSignature) signature; - Method method = methodSignature.getMethod(); - - if (method != null) { - return method.getAnnotation(DataScope.class); - } - return null; - } - /** * 拼接权限sql前先清空params.dataScope参数防止注入 */ diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java index 47e774265..55ec84595 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java @@ -6,33 +6,21 @@ import com.ruoyi.common.utils.StringUtils; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.annotation.Pointcut; -import org.aspectj.lang.reflect.MethodSignature; -import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; -import java.util.Objects; - /** * 多数据源处理 * - * @author ruoyi + * @author Lion Li */ @Aspect @Order(-500) @Component public class DataSourceAspect { - @Pointcut("@annotation(com.ruoyi.common.annotation.DataSource)" - + "|| @within(com.ruoyi.common.annotation.DataSource)") - public void dsPointCut() { - } - - @Around("dsPointCut()") - public Object around(ProceedingJoinPoint point) throws Throwable { - DataSource dataSource = getDataSource(point); - + @Around("@annotation(dataSource) || @within(dataSource)") + public Object around(ProceedingJoinPoint point, DataSource dataSource) throws Throwable { if (StringUtils.isNotNull(dataSource)) { DynamicDataSourceContextHolder.poll(); String source = dataSource.value().getSource(); @@ -47,16 +35,4 @@ public class DataSourceAspect { } } - /** - * 获取需要切换的数据源 - */ - public DataSource getDataSource(ProceedingJoinPoint point) { - MethodSignature signature = (MethodSignature) point.getSignature(); - DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class); - if (Objects.nonNull(dataSource)) { - return dataSource; - } - - return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class); - } } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java index e8c47a364..642076d9e 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java @@ -1,7 +1,9 @@ package com.ruoyi.framework.aspectj; import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.domain.dto.OperLogDTO; import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.core.service.OperLogService; import com.ruoyi.common.enums.BusinessStatus; import com.ruoyi.common.enums.HttpMethod; import com.ruoyi.common.utils.JsonUtils; @@ -9,17 +11,11 @@ import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.ServletUtils; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.spring.SpringUtils; -import com.ruoyi.framework.web.service.AsyncService; -import com.ruoyi.system.domain.SysOperLog; +import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.JoinPoint; -import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.annotation.Pointcut; -import org.aspectj.lang.reflect.MethodSignature; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import org.springframework.validation.BindingResult; import org.springframework.web.multipart.MultipartFile; @@ -27,81 +23,58 @@ import org.springframework.web.servlet.HandlerMapping; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import java.lang.reflect.Method; import java.util.Collection; import java.util.Map; /** * 操作日志记录处理 * - * @author ruoyi + * @author Lion Li */ +@Slf4j @Aspect @Component -public class LogAspect -{ - private static final Logger log = LoggerFactory.getLogger(LogAspect.class); - - // 配置织入点 - @Pointcut("@annotation(com.ruoyi.common.annotation.Log)") - public void logPointCut() - { - } +public class LogAspect { /** * 处理完请求后执行 * * @param joinPoint 切点 */ - @AfterReturning(pointcut = "logPointCut()", returning = "jsonResult") - public void doAfterReturning(JoinPoint joinPoint, Object jsonResult) - { - handleLog(joinPoint, null, jsonResult); + @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult") + public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult) { + handleLog(joinPoint, controllerLog, null, jsonResult); } /** * 拦截异常操作 * * @param joinPoint 切点 - * @param e 异常 + * @param e 异常 */ - @AfterThrowing(value = "logPointCut()", throwing = "e") - public void doAfterThrowing(JoinPoint joinPoint, Exception e) - { - handleLog(joinPoint, e, null); + @AfterThrowing(value = "@annotation(controllerLog)", throwing = "e") + public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e) { + handleLog(joinPoint, controllerLog, e, null); } - protected void handleLog(final JoinPoint joinPoint, final Exception e, Object jsonResult) - { - try - { - // 获得注解 - Log controllerLog = getAnnotationLog(joinPoint); - if (controllerLog == null) - { - return; - } + protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) { + try { // 获取当前的用户 LoginUser loginUser = SecurityUtils.getLoginUser(); // *========数据库日志=========*// - SysOperLog operLog = new SysOperLog(); + OperLogDTO operLog = new OperLogDTO(); operLog.setStatus(BusinessStatus.SUCCESS.ordinal()); // 请求的地址 String ip = ServletUtils.getClientIP(); operLog.setOperIp(ip); - // 返回参数 - operLog.setJsonResult(JsonUtils.toJsonString(jsonResult)); - operLog.setOperUrl(ServletUtils.getRequest().getRequestURI()); - if (loginUser != null) - { + if (loginUser != null) { operLog.setOperName(loginUser.getUsername()); } - if (e != null) - { + if (e != null) { operLog.setStatus(BusinessStatus.FAIL.ordinal()); operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000)); } @@ -112,12 +85,10 @@ public class LogAspect // 设置请求方式 operLog.setRequestMethod(ServletUtils.getRequest().getMethod()); // 处理设置注解上的参数 - getControllerMethodDescription(joinPoint, controllerLog, operLog); + getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult); // 保存数据库 - SpringUtils.getBean(AsyncService.class).recordOper(operLog); - } - catch (Exception exp) - { + SpringUtils.getBean(OperLogService.class).recordOper(operLog); + } catch (Exception exp) { // 记录本地异常日志 log.error("==前置通知异常=="); log.error("异常信息:{}", exp.getMessage()); @@ -128,12 +99,11 @@ public class LogAspect /** * 获取注解中对方法的描述信息 用于Controller层注解 * - * @param log 日志 + * @param log 日志 * @param operLog 操作日志 * @throws Exception */ - public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOperLog operLog) throws Exception - { + public void getControllerMethodDescription(JoinPoint joinPoint, Log log, OperLogDTO operLog, Object jsonResult) throws Exception { // 设置action动作 operLog.setBusinessType(log.businessType().ordinal()); // 设置标题 @@ -141,11 +111,14 @@ public class LogAspect // 设置操作人类别 operLog.setOperatorType(log.operatorType().ordinal()); // 是否需要保存request,参数和值 - if (log.isSaveRequestData()) - { + if (log.isSaveRequestData()) { // 获取参数的信息,传入到数据库中。 setRequestValue(joinPoint, operLog); } + // 是否需要保存response,参数和值 + if (log.isSaveResponseData() && StringUtils.isNotNull(jsonResult)) { + operLog.setJsonResult(StringUtils.substring(JsonUtils.toJsonString(jsonResult), 0, 2000)); + } } /** @@ -154,50 +127,32 @@ public class LogAspect * @param operLog 操作日志 * @throws Exception 异常 */ - private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog) throws Exception - { + private void setRequestValue(JoinPoint joinPoint, OperLogDTO operLog) throws Exception { String requestMethod = operLog.getRequestMethod(); - if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) - { + if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) { String params = argsArrayToString(joinPoint.getArgs()); operLog.setOperParam(StringUtils.substring(params, 0, 2000)); - } - else - { + } else { Map paramsMap = (Map) ServletUtils.getRequest().getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE); operLog.setOperParam(StringUtils.substring(paramsMap.toString(), 0, 2000)); } } - /** - * 是否存在注解,如果存在就获取 - */ - private Log getAnnotationLog(JoinPoint joinPoint) throws Exception - { - Signature signature = joinPoint.getSignature(); - MethodSignature methodSignature = (MethodSignature) signature; - Method method = methodSignature.getMethod(); - - if (method != null) - { - return method.getAnnotation(Log.class); - } - return null; - } - /** * 参数拼装 */ - private String argsArrayToString(Object[] paramsArray) - { + private String argsArrayToString(Object[] paramsArray) { StringBuilder params = new StringBuilder(); - if (paramsArray != null && paramsArray.length > 0) - { - for (Object o : paramsArray) { - if (StringUtils.isNotNull(o) && !isFilterObject(o)) { - params.append(JsonUtils.toJsonString(o)).append(" "); - } - } + if (paramsArray != null && paramsArray.length > 0) { + for (Object o : paramsArray) { + if (StringUtils.isNotNull(o) && !isFilterObject(o)) { + try { + params.append(JsonUtils.toJsonString(o)).append(" "); + } catch (Exception e) { + e.printStackTrace(); + } + } + } } return params.toString().trim(); } @@ -209,27 +164,21 @@ public class LogAspect * @return 如果是需要过滤的对象,则返回true;否则返回false。 */ @SuppressWarnings("rawtypes") - public boolean isFilterObject(final Object o) - { + public boolean isFilterObject(final Object o) { Class clazz = o.getClass(); - if (clazz.isArray()) - { + if (clazz.isArray()) { return clazz.getComponentType().isAssignableFrom(MultipartFile.class); - } - else if (Collection.class.isAssignableFrom(clazz)) - { + } else if (Collection.class.isAssignableFrom(clazz)) { Collection collection = (Collection) o; - for (Object value : collection) { - return value instanceof MultipartFile; - } - } - else if (Map.class.isAssignableFrom(clazz)) - { + for (Object value : collection) { + return value instanceof MultipartFile; + } + } else if (Map.class.isAssignableFrom(clazz)) { Map map = (Map) o; - for (Object value : map.entrySet()) { - Map.Entry entry = (Map.Entry) value; - return entry.getValue() instanceof MultipartFile; - } + for (Object value : map.entrySet()) { + Map.Entry entry = (Map.Entry) value; + return entry.getValue() instanceof MultipartFile; + } } return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse || o instanceof BindingResult; diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java index 1e21e376d..dc140e22b 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java @@ -3,114 +3,63 @@ package com.ruoyi.framework.aspectj; import com.ruoyi.common.annotation.RateLimiter; import com.ruoyi.common.enums.LimitType; import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.RedisUtils; import com.ruoyi.common.utils.ServletUtils; -import com.ruoyi.common.utils.StringUtils; +import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.JoinPoint; -import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; -import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.data.redis.core.script.RedisScript; +import org.redisson.api.RateType; import org.springframework.stereotype.Component; import java.lang.reflect.Method; -import java.util.Collections; -import java.util.List; /** * 限流处理 * - * @author ruoyi + * @author Lion Li */ +@Slf4j @Aspect @Component -public class RateLimiterAspect -{ - private static final Logger log = LoggerFactory.getLogger(RateLimiterAspect.class); +public class RateLimiterAspect { - private RedisTemplate redisTemplate; - - private RedisScript limitScript; - - @Autowired - public void setRedisTemplate1(RedisTemplate redisTemplate) - { - this.redisTemplate = redisTemplate; - } - - @Autowired - public void setLimitScript(RedisScript limitScript) - { - this.limitScript = limitScript; - } - - // 配置织入点 - @Pointcut("@annotation(com.ruoyi.common.annotation.RateLimiter)") - public void rateLimiterPointCut() - { - } - - @Before("rateLimiterPointCut()") - public void doBefore(JoinPoint point) throws Throwable - { - RateLimiter rateLimiter = getAnnotationRateLimiter(point); - String key = rateLimiter.key(); + @Before("@annotation(rateLimiter)") + public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable { int time = rateLimiter.time(); int count = rateLimiter.count(); - String combineKey = getCombineKey(rateLimiter, point); - List keys = Collections.singletonList(combineKey); - try - { - Long number = redisTemplate.execute(limitScript, keys, count, time); - if (StringUtils.isNull(number) || number.intValue() > count) - { + try { + RateType rateType = RateType.OVERALL; + if (rateLimiter.limitType() == LimitType.CLUSTER) { + rateType = RateType.PER_CLIENT; + } + long number = RedisUtils.rateLimiter(combineKey, rateType, count, time); + if (number == -1) { throw new ServiceException("访问过于频繁,请稍后再试"); } - log.info("限制请求'{}',当前请求'{}',缓存key'{}'", count, number.intValue(), key); - } - catch (ServiceException e) - { + log.info("限制令牌 => {}, 剩余令牌 => {}, 缓存key => '{}'", count, number, combineKey); + } catch (ServiceException e) { throw e; - } - catch (Exception e) - { + } catch (Exception e) { throw new RuntimeException("服务器限流异常,请稍后再试"); } } - /** - * 是否存在注解,如果存在就获取 - */ - private RateLimiter getAnnotationRateLimiter(JoinPoint joinPoint) - { - Signature signature = joinPoint.getSignature(); - MethodSignature methodSignature = (MethodSignature) signature; - Method method = methodSignature.getMethod(); - - if (method != null) - { - return method.getAnnotation(RateLimiter.class); - } - return null; - } - - public String getCombineKey(RateLimiter rateLimiter, JoinPoint point) - { - StringBuffer stringBuffer = new StringBuffer(rateLimiter.key()); - if (rateLimiter.limitType() == LimitType.IP) - { - stringBuffer.append(ServletUtils.getClientIP()); + public String getCombineKey(RateLimiter rateLimiter, JoinPoint point) { + StringBuilder stringBuffer = new StringBuilder(rateLimiter.key()); + if (rateLimiter.limitType() == LimitType.IP) { + // 获取请求ip + stringBuffer.append(ServletUtils.getClientIP()).append("-"); + } else if (rateLimiter.limitType() == LimitType.CLUSTER){ + // 获取客户端实例id + stringBuffer.append(RedisUtils.getClientId()).append("-"); } MethodSignature signature = (MethodSignature) point.getSignature(); Method method = signature.getMethod(); Class targetClass = method.getDeclaringClass(); - stringBuffer.append("-").append(targetClass.getName()).append("- ").append(method.getName()); + stringBuffer.append(targetClass.getName()).append("-").append(method.getName()); return stringBuffer.toString(); } } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RepeatSubmitAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RepeatSubmitAspect.java new file mode 100644 index 000000000..41116d880 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RepeatSubmitAspect.java @@ -0,0 +1,69 @@ +package com.ruoyi.framework.aspectj; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.crypto.SecureUtil; +import com.baomidou.lock.LockInfo; +import com.baomidou.lock.LockTemplate; +import com.ruoyi.common.annotation.RepeatSubmit; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.properties.TokenProperties; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.config.properties.RepeatSubmitProperties; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; + +/** + * 防止重复提交 + * + * @author Lion Li + */ +@Slf4j +@RequiredArgsConstructor(onConstructor_ = @Autowired) +@Aspect +@Component +public class RepeatSubmitAspect { + + private final TokenProperties tokenProperties; + private final RepeatSubmitProperties repeatSubmitProperties; + private final LockTemplate lockTemplate; + + @Before("@annotation(repeatSubmit)") + public void doBefore(JoinPoint point, RepeatSubmit repeatSubmit) throws Throwable { + // 如果注解不为0 则使用注解数值 + long interval = repeatSubmitProperties.getInterval(); + if (repeatSubmit.interval() > 0) { + interval = repeatSubmit.timeUnit().toMillis(repeatSubmit.interval()); + } + if (interval < 1000) { + throw new ServiceException("重复提交间隔时间不能小于'1'秒"); + } + HttpServletRequest request = ServletUtils.getRequest(); + String nowParams = StrUtil.join(",", point.getArgs()); + + // 请求地址(作为存放cache的key值) + String url = request.getRequestURI(); + + // 唯一值(没有消息头则使用请求地址) + String submitKey = request.getHeader(tokenProperties.getHeader()); + if (StringUtils.isEmpty(submitKey)) { + submitKey = url; + } + submitKey = SecureUtil.md5(submitKey + ":" + nowParams); + // 唯一标识(指定key + 消息头) + String cacheRepeatKey = Constants.REPEAT_SUBMIT_KEY + submitKey; + LockInfo lock = lockTemplate.lock(cacheRepeatKey, interval, interval / 2); + if (lock == null) { + throw new ServiceException(repeatSubmit.message()); + } + } + +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/AsyncConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/AsyncConfig.java index e30f83c79..95f3c9b73 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/AsyncConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/AsyncConfig.java @@ -1,5 +1,6 @@ package com.ruoyi.framework.config; +import cn.hutool.core.util.ArrayUtil; import com.ruoyi.common.exception.ServiceException; import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; import org.springframework.beans.factory.annotation.Autowired; @@ -41,10 +42,13 @@ public class AsyncConfig extends AsyncConfigurerSupport { public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return (throwable, method, objects) -> { throwable.printStackTrace(); - throw new ServiceException( - "Exception message - " + throwable.getMessage() - + ", Method name - " + method.getName() - + ", Parameter value - " + Arrays.toString(objects)); + StringBuilder sb = new StringBuilder(); + sb.append("Exception message - ").append(throwable.getMessage()) + .append(", Method name - ").append(method.getName()); + if (ArrayUtil.isNotEmpty(objects)) { + sb.append(", Parameter value - ").append(Arrays.toString(objects)); + } + throw new ServiceException(sb.toString()); }; } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java index 867cd40b5..c1c303a26 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java @@ -1,6 +1,6 @@ package com.ruoyi.framework.config; -import cn.hutool.core.util.StrUtil; +import cn.hutool.core.util.ObjectUtil; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.framework.config.properties.RedissonProperties; import lombok.extern.slf4j.Slf4j; @@ -18,7 +18,6 @@ import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.data.redis.core.script.DefaultRedisScript; import java.io.IOException; import java.util.HashMap; @@ -58,23 +57,52 @@ public class RedisConfig extends CachingConfigurerSupport { .setTransportMode(redissonProperties.getTransportMode()); RedissonProperties.SingleServerConfig singleServerConfig = redissonProperties.getSingleServerConfig(); - // 使用单机模式 - config.useSingleServer() - .setAddress(prefix + redisProperties.getHost() + ":" + redisProperties.getPort()) - .setConnectTimeout(((Long) redisProperties.getTimeout().toMillis()).intValue()) - .setDatabase(redisProperties.getDatabase()) - .setPassword(StringUtils.isNotBlank(redisProperties.getPassword()) ? redisProperties.getPassword() : null) - .setTimeout(singleServerConfig.getTimeout()) - .setRetryAttempts(singleServerConfig.getRetryAttempts()) - .setRetryInterval(singleServerConfig.getRetryInterval()) - .setSubscriptionsPerConnection(singleServerConfig.getSubscriptionsPerConnection()) - .setClientName(singleServerConfig.getClientName()) - .setIdleConnectionTimeout(singleServerConfig.getIdleConnectionTimeout()) - .setSubscriptionConnectionMinimumIdleSize(singleServerConfig.getSubscriptionConnectionMinimumIdleSize()) - .setSubscriptionConnectionPoolSize(singleServerConfig.getSubscriptionConnectionPoolSize()) - .setConnectionMinimumIdleSize(singleServerConfig.getConnectionMinimumIdleSize()) - .setConnectionPoolSize(singleServerConfig.getConnectionPoolSize()) - .setDnsMonitoringInterval(singleServerConfig.getDnsMonitoringInterval()); + if (ObjectUtil.isNotNull(singleServerConfig)) { + // 使用单机模式 + config.useSingleServer() + .setAddress(prefix + redisProperties.getHost() + ":" + redisProperties.getPort()) + .setConnectTimeout(((Long) redisProperties.getTimeout().toMillis()).intValue()) + .setDatabase(redisProperties.getDatabase()) + .setPassword(StringUtils.isNotBlank(redisProperties.getPassword()) ? redisProperties.getPassword() : null) + .setTimeout(singleServerConfig.getTimeout()) + .setRetryAttempts(singleServerConfig.getRetryAttempts()) + .setRetryInterval(singleServerConfig.getRetryInterval()) + .setSubscriptionsPerConnection(singleServerConfig.getSubscriptionsPerConnection()) + .setClientName(singleServerConfig.getClientName()) + .setIdleConnectionTimeout(singleServerConfig.getIdleConnectionTimeout()) + .setSubscriptionConnectionMinimumIdleSize(singleServerConfig.getSubscriptionConnectionMinimumIdleSize()) + .setSubscriptionConnectionPoolSize(singleServerConfig.getSubscriptionConnectionPoolSize()) + .setConnectionMinimumIdleSize(singleServerConfig.getConnectionMinimumIdleSize()) + .setConnectionPoolSize(singleServerConfig.getConnectionPoolSize()) + .setDnsMonitoringInterval(singleServerConfig.getDnsMonitoringInterval()); + } + + RedissonProperties.ClusterServersConfig clusterServersConfig = redissonProperties.getClusterServersConfig(); + if (ObjectUtil.isNotNull(clusterServersConfig)) { + // 使用集群模式 + config.useClusterServers() + .setConnectTimeout(((Long) redisProperties.getTimeout().toMillis()).intValue()) + .setPassword(StringUtils.isNotBlank(redisProperties.getPassword()) ? redisProperties.getPassword() : null) + .setTimeout(clusterServersConfig.getTimeout()) + .setRetryAttempts(clusterServersConfig.getRetryAttempts()) + .setRetryInterval(clusterServersConfig.getRetryInterval()) + .setSubscriptionsPerConnection(clusterServersConfig.getSubscriptionsPerConnection()) + .setClientName(clusterServersConfig.getClientName()) + .setIdleConnectionTimeout(clusterServersConfig.getIdleConnectionTimeout()) + .setPingConnectionInterval(clusterServersConfig.getPingConnectionInterval()) + .setSubscriptionConnectionMinimumIdleSize(clusterServersConfig.getSubscriptionConnectionMinimumIdleSize()) + .setSubscriptionConnectionPoolSize(clusterServersConfig.getSubscriptionConnectionPoolSize()) + .setMasterConnectionMinimumIdleSize(clusterServersConfig.getMasterConnectionMinimumIdleSize()) + .setMasterConnectionPoolSize(clusterServersConfig.getMasterConnectionPoolSize()) + .setSlaveConnectionMinimumIdleSize(clusterServersConfig.getSlaveConnectionMinimumIdleSize()) + .setSlaveConnectionPoolSize(clusterServersConfig.getSlaveConnectionPoolSize()) + .setDnsMonitoringInterval(clusterServersConfig.getDnsMonitoringInterval()) + .setFailedSlaveReconnectionInterval(clusterServersConfig.getFailedSlaveReconnectionInterval()) + .setScanInterval(clusterServersConfig.getScanInterval()) + .setReadMode(clusterServersConfig.getReadMode()) + .setSubscriptionMode(clusterServersConfig.getSubscriptionMode()) + .setNodeAddresses(redisProperties.getCluster().getNodes()); + } RedissonClient redissonClient = Redisson.create(config); log.info("初始化 redis 配置"); return redissonClient; @@ -95,32 +123,4 @@ public class RedisConfig extends CachingConfigurerSupport { return new RedissonSpringCacheManager(redissonClient, config, JsonJacksonCodec.INSTANCE); } - @Bean - public DefaultRedisScript limitScript() { - DefaultRedisScript redisScript = new DefaultRedisScript<>(); - redisScript.setScriptText(limitScriptText()); - redisScript.setResultType(Long.class); - return redisScript; - } - - /** - * 限流脚本 - */ - private String limitScriptText() { - return StrUtil.builder() - .append("local key = KEYS[1]\n") - .append("local count = tonumber(ARGV[1])\n") - .append("local time = tonumber(ARGV[2])\n") - .append("local current = redis.call('get', key);\n") - .append("if current and tonumber(current) > count then\n") - .append(" return current;\n") - .append("end\n") - .append("current = redis.call('incr', key)\n") - .append("if tonumber(current) == 1 then\n") - .append(" redis.call('expire', key, time)\n") - .append("end\n") - .append("return current;") - .toString(); - } - } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java index 870af3f9a..ddf4c70b3 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java @@ -1,52 +1,35 @@ package com.ruoyi.framework.config; -import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; -import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; /** * 通用配置 * - * @author ruoyi + * @author Lion Li */ @Configuration -public class ResourcesConfig implements WebMvcConfigurer -{ - @Autowired - private RepeatSubmitInterceptor repeatSubmitInterceptor; +public class ResourcesConfig implements WebMvcConfigurer { @Override - public void addResourceHandlers(ResourceHandlerRegistry registry) - { - } - - /** - * 自定义拦截规则 - */ - @Override - public void addInterceptors(InterceptorRegistry registry) - { - registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**"); + public void addResourceHandlers(ResourceHandlerRegistry registry) { } /** * 跨域配置 */ @Bean - public CorsFilter corsFilter() - { + public CorsFilter corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); CorsConfiguration config = new CorsConfiguration(); config.setAllowCredentials(true); // 设置访问源地址 - config.addAllowedOriginPattern("*"); + config.addAllowedOriginPattern("*"); // 设置访问源请求头 config.addAllowedHeader("*"); // 设置访问源请求方法 diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java index bd6ee8f36..bb8b7f231 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java @@ -1,5 +1,6 @@ package com.ruoyi.framework.config; +import com.ruoyi.framework.config.properties.SecurityProperties; import com.ruoyi.framework.security.filter.JwtAuthenticationTokenFilter; import com.ruoyi.framework.security.handle.AuthenticationEntryPointImpl; import com.ruoyi.framework.security.handle.LogoutSuccessHandlerImpl; @@ -20,7 +21,7 @@ import org.springframework.web.filter.CorsFilter; /** * spring security配置 - * + * * @author ruoyi */ @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) @@ -31,7 +32,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter */ @Autowired private UserDetailsService userDetailsService; - + /** * 认证失败处理类 */ @@ -49,13 +50,16 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter */ @Autowired private JwtAuthenticationTokenFilter authenticationTokenFilter; - + /** * 跨域过滤器 */ @Autowired private CorsFilter corsFilter; + @Autowired + private SecurityProperties securityProperties; + /** * 解决 无法直接注入 AuthenticationManager * @@ -96,8 +100,6 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() // 过滤请求 .authorizeRequests() - // 对于登录login 注册register 验证码captchaImage 允许匿名访问 - .antMatchers("/login", "/register", "/captchaImage").anonymous() .antMatchers( HttpMethod.GET, "/", @@ -106,19 +108,13 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter "/**/*.css", "/**/*.js" ).permitAll() - .antMatchers("/doc.html").anonymous() - .antMatchers("/swagger-resources/**").anonymous() - .antMatchers("/webjars/**").anonymous() - .antMatchers("/*/api-docs").anonymous() - .antMatchers("/druid/**").anonymous() - // Spring Boot Actuator 的安全配置 - .antMatchers("/actuator").anonymous() - .antMatchers("/actuator/**").anonymous() + .antMatchers(securityProperties.getAnonymous()).anonymous() + .antMatchers(securityProperties.getPermitAll()).permitAll() // 除上面外的所有请求全部需要鉴权认证 .anyRequest().authenticated() .and() .headers().frameOptions().disable(); - httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler); + httpSecurity.logout().logoutUrl(securityProperties.getLogoutUrl()).logoutSuccessHandler(logoutSuccessHandler); // 添加JWT filter httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); // 添加CORS filter diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ServerConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ServerConfig.java deleted file mode 100644 index b5b7de316..000000000 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ServerConfig.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.ruoyi.framework.config; - -import javax.servlet.http.HttpServletRequest; -import org.springframework.stereotype.Component; -import com.ruoyi.common.utils.ServletUtils; - -/** - * 服务相关配置 - * - * @author ruoyi - */ -@Component -public class ServerConfig -{ - /** - * 获取完整的请求路径,包括:域名,端口,上下文访问路径 - * - * @return 服务地址 - */ - public String getUrl() - { - HttpServletRequest request = ServletUtils.getRequest(); - return getDomain(request); - } - - public static String getDomain(HttpServletRequest request) - { - StringBuffer url = request.getRequestURL(); - String contextPath = request.getServletContext().getContextPath(); - return url.delete(url.length() - request.getRequestURI().length(), url.length()).append(contextPath).toString(); - } -} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SwaggerConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SwaggerConfig.java index 2c65ac69d..4da3b9949 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SwaggerConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SwaggerConfig.java @@ -1,11 +1,12 @@ package com.ruoyi.framework.config; import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j; +import com.ruoyi.common.properties.TokenProperties; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.spring.SpringUtils; import com.ruoyi.framework.config.properties.SwaggerProperties; -import io.swagger.annotations.ApiOperation; import io.swagger.models.auth.In; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; @@ -15,6 +16,7 @@ import springfox.documentation.spi.DocumentationType; import springfox.documentation.spi.service.contexts.SecurityContext; import springfox.documentation.spring.web.plugins.Docket; +import javax.annotation.PostConstruct; import java.util.ArrayList; import java.util.List; @@ -27,82 +29,92 @@ import java.util.List; @EnableKnife4j public class SwaggerConfig { - @Autowired - private SwaggerProperties swaggerProperties; + @Autowired + private SwaggerProperties swaggerProperties; - /** - * 创建API - */ - @Bean - public Docket createRestApi() { - return new Docket(DocumentationType.OAS_30) - .enable(swaggerProperties.getEnabled()) - // 用来创建该API的基本信息,展示在文档的页面中(自定义展示的信息) - .apiInfo(apiInfo()) - // 设置哪些接口暴露给Swagger展示 - .select() - // 扫描所有有注解的api,用这种方式更灵活 - .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) - // 扫描指定包中的swagger注解 - // .apis(RequestHandlerSelectors.basePackage("com.ruoyi.project.tool.swagger")) - // 扫描所有 .apis(RequestHandlerSelectors.any()) - .paths(PathSelectors.any()) - .build() - /* 设置安全模式,swagger可以设置访问token */ - .securitySchemes(securitySchemes()) - .securityContexts(securityContexts()) - .pathMapping(swaggerProperties.getPathMapping()); - } + @Autowired + private TokenProperties tokenProperties; - /** - * 安全模式,这里指定token通过Authorization头请求头传递 - */ - private List securitySchemes() { - List apiKeyList = new ArrayList(); - apiKeyList.add(new ApiKey("Authorization", "Authorization", In.HEADER.toValue())); - return apiKeyList; - } + /** + * 创建API + */ + @PostConstruct + public void createRestApi() { + for (SwaggerProperties.Groups group : swaggerProperties.getGroups()) { + String basePackage = group.getBasePackage(); + Docket docket = new Docket(DocumentationType.OAS_30) + .enable(swaggerProperties.getEnabled()) + // 用来创建该API的基本信息,展示在文档的页面中(自定义展示的信息) + .apiInfo(apiInfo()) + // 设置哪些接口暴露给Swagger展示 + .select() + // 扫描所有有注解的api,用这种方式更灵活 + //.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) + // 扫描指定包中的swagger注解 + .apis(RequestHandlerSelectors.basePackage(basePackage)) + // 扫描所有 .apis(RequestHandlerSelectors.any()) + .paths(PathSelectors.any()) + .build() + .groupName(group.getName()) + // 设置安全模式,swagger可以设置访问token + .securitySchemes(securitySchemes()) + .securityContexts(securityContexts()) + .pathMapping(swaggerProperties.getPathMapping()); + String beanName = StringUtils.substringAfterLast(basePackage, ".") + "Docket"; + SpringUtils.registerBean(beanName, docket); + } + } - /** - * 安全上下文 - */ - private List securityContexts() { - List securityContexts = new ArrayList<>(); - securityContexts.add( - SecurityContext.builder() - .securityReferences(defaultAuth()) - .operationSelector(o -> o.requestMappingPattern().matches("/.*")) - .build()); - return securityContexts; - } + /** + * 安全模式,这里指定token通过Authorization头请求头传递 + */ + private List securitySchemes() { + List apiKeyList = new ArrayList(); + String header = tokenProperties.getHeader(); + apiKeyList.add(new ApiKey(header, header, In.HEADER.toValue())); + return apiKeyList; + } - /** - * 默认的安全上引用 - */ - private List defaultAuth() { - AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything"); - AuthorizationScope[] authorizationScopes = new AuthorizationScope[1]; - authorizationScopes[0] = authorizationScope; - List securityReferences = new ArrayList<>(); - securityReferences.add(new SecurityReference("Authorization", authorizationScopes)); - return securityReferences; - } + /** + * 安全上下文 + */ + private List securityContexts() { + List securityContexts = new ArrayList<>(); + securityContexts.add( + SecurityContext.builder() + .securityReferences(defaultAuth()) + .operationSelector(o -> o.requestMappingPattern().matches("/.*")) + .build()); + return securityContexts; + } - /** - * 添加摘要信息 - */ - private ApiInfo apiInfo() { - // 用ApiInfoBuilder进行定制 - SwaggerProperties.Contact contact = swaggerProperties.getContact(); - return new ApiInfoBuilder() - // 设置标题 - .title(swaggerProperties.getTitle()) - // 描述 - .description(swaggerProperties.getDescription()) - // 作者信息 - .contact(new Contact(contact.getName(), contact.getUrl(), contact.getEmail())) - // 版本 - .version(swaggerProperties.getVersion()) - .build(); - } + /** + * 默认的安全上引用 + */ + private List defaultAuth() { + AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything"); + AuthorizationScope[] authorizationScopes = new AuthorizationScope[1]; + authorizationScopes[0] = authorizationScope; + List securityReferences = new ArrayList<>(); + securityReferences.add(new SecurityReference(tokenProperties.getHeader(), authorizationScopes)); + return securityReferences; + } + + /** + * 添加摘要信息 + */ + private ApiInfo apiInfo() { + // 用ApiInfoBuilder进行定制 + SwaggerProperties.Contact contact = swaggerProperties.getContact(); + return new ApiInfoBuilder() + // 设置标题 + .title(swaggerProperties.getTitle()) + // 描述 + .description(swaggerProperties.getDescription()) + // 作者信息 + .contact(new Contact(contact.getName(), contact.getUrl(), contact.getEmail())) + // 版本 + .version(swaggerProperties.getVersion()) + .build(); + } } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/RedissonProperties.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/RedissonProperties.java index b6e289d8d..4516da269 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/RedissonProperties.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/RedissonProperties.java @@ -2,6 +2,8 @@ package com.ruoyi.framework.config.properties; import lombok.Data; import lombok.NoArgsConstructor; +import org.redisson.config.ReadMode; +import org.redisson.config.SubscriptionMode; import org.redisson.config.TransportMode; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @@ -38,6 +40,11 @@ public class RedissonProperties { */ private SingleServerConfig singleServerConfig; + /** + * 集群服务配置 + */ + private ClusterServersConfig clusterServersConfig; + /** * 缓存组 */ @@ -104,6 +111,102 @@ public class RedissonProperties { } + @Data + @NoArgsConstructor + public static class ClusterServersConfig { + + /** + * 客户端名称 + */ + private String clientName; + + /** + * master最小空闲连接数 + */ + private int masterConnectionMinimumIdleSize; + + /** + * master连接池大小 + */ + private int masterConnectionPoolSize; + + /** + * slave最小空闲连接数 + */ + private int slaveConnectionMinimumIdleSize; + + /** + * slave连接池大小 + */ + private int slaveConnectionPoolSize; + + /** + * 连接空闲超时,单位:毫秒 + */ + private int idleConnectionTimeout; + + /** + * ping超时 + */ + private int pingConnectionInterval; + + /** + * 命令等待超时,单位:毫秒 + */ + private int timeout; + + /** + * 如果尝试在此限制之内发送成功,则开始启用 timeout 计时。 + */ + private int retryAttempts; + + /** + * 命令重试发送时间间隔,单位:毫秒 + */ + private int retryInterval; + + /** + * 错误重试次数 + */ + private int failedSlaveReconnectionInterval; + + /** + * 发布和订阅连接池最小空闲连接数 + */ + private int subscriptionConnectionMinimumIdleSize; + + /** + * 发布和订阅连接池大小 + */ + private int subscriptionConnectionPoolSize; + + /** + * 单个连接最大订阅数量 + */ + private int subscriptionsPerConnection; + + /** + * 扫描间隔 + */ + private int scanInterval; + + /** + * DNS监测时间间隔,单位:毫秒 + */ + private int dnsMonitoringInterval; + + /** + * 读取模式 + */ + private ReadMode readMode; + + /** + * 订阅模式 + */ + private SubscriptionMode subscriptionMode; + + } + @Data @NoArgsConstructor public static class CacheGroup { diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/RepeatSubmitProperties.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/RepeatSubmitProperties.java index b948f8340..fa65b28c3 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/RepeatSubmitProperties.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/RepeatSubmitProperties.java @@ -17,6 +17,6 @@ public class RepeatSubmitProperties { /** * 间隔时间(毫秒) */ - private int intervalTime; + private int interval; } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/SecurityProperties.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/SecurityProperties.java new file mode 100644 index 000000000..c83ffccbe --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/SecurityProperties.java @@ -0,0 +1,32 @@ +package com.ruoyi.framework.config.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * Security 配置属性 + * + * @author Lion Li + */ +@Data +@Component +@ConfigurationProperties(prefix = "security") +public class SecurityProperties { + + /** + * 退出登录url + */ + private String logoutUrl; + + /** + * 匿名放行路径 + */ + private String[] anonymous; + + /** + * 用户任意访问放行路径 + */ + private String[] permitAll; + +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/SwaggerProperties.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/SwaggerProperties.java index ece75efef..9c4cd238c 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/SwaggerProperties.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/SwaggerProperties.java @@ -5,6 +5,8 @@ import lombok.NoArgsConstructor; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; +import java.util.List; + /** * swagger 配置属性 * @@ -41,23 +43,46 @@ public class SwaggerProperties { */ private Contact contact; + /** + * 组配置 + */ + private List groups; + @Data @NoArgsConstructor - public static class Contact{ + public static class Contact { /** * 联系人 - **/ + */ private String name; + /** * 联系人url - **/ + */ private String url; + /** * 联系人email - **/ + */ private String email; } + @Data + @NoArgsConstructor + public static class Groups { + + /** + * 组名 + */ + private String name; + + /** + * 基础包路径 + */ + private String basePackage; + + } + } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java deleted file mode 100644 index 9af0a830b..000000000 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.ruoyi.framework.interceptor; - -import com.ruoyi.common.annotation.RepeatSubmit; -import com.ruoyi.common.core.domain.AjaxResult; -import com.ruoyi.common.utils.JsonUtils; -import com.ruoyi.common.utils.ServletUtils; -import org.springframework.stereotype.Component; -import org.springframework.web.method.HandlerMethod; -import org.springframework.web.servlet.HandlerInterceptor; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.lang.reflect.Method; - -/** - * 防止重复提交拦截器 - * - * 移除继承 HandlerInterceptorAdapter 过期类 - * 改为实现 HandlerInterceptor 接口(官方推荐写法) - * - * @author Lion Li - */ -@Component -public abstract class RepeatSubmitInterceptor implements HandlerInterceptor { - - @Override - public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) - throws Exception { - if (handler instanceof HandlerMethod) { - HandlerMethod handlerMethod = (HandlerMethod) handler; - Method method = handlerMethod.getMethod(); - RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class); - if (annotation != null) { - if (this.isRepeatSubmit(annotation, request)) { - AjaxResult ajaxResult = AjaxResult.error("不允许重复提交,请稍后再试"); - ServletUtils.renderString(response, JsonUtils.toJsonString(ajaxResult)); - return false; - } - } - return true; - } else { - return HandlerInterceptor.super.preHandle(request, response, handler); - } - } - - /** - * 验证是否重复提交由子类实现具体的防重复提交的规则 - */ - public abstract boolean isRepeatSubmit(RepeatSubmit annotation, HttpServletRequest request); -} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java deleted file mode 100644 index b2256c935..000000000 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java +++ /dev/null @@ -1,114 +0,0 @@ -package com.ruoyi.framework.interceptor.impl; - -import cn.hutool.core.convert.Convert; -import cn.hutool.core.io.IoUtil; -import com.ruoyi.common.annotation.RepeatSubmit; -import com.ruoyi.common.constant.Constants; -import com.ruoyi.common.filter.RepeatedlyRequestWrapper; -import com.ruoyi.common.utils.JsonUtils; -import com.ruoyi.common.utils.RedisUtils; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.framework.config.properties.RepeatSubmitProperties; -import com.ruoyi.framework.config.properties.TokenProperties; -import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import javax.servlet.http.HttpServletRequest; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -/** - * 判断请求url和数据是否和上一次相同, - * 如果和上次相同,则是重复提交表单。 - * - * @author Lion Li - */ -@Slf4j -@RequiredArgsConstructor(onConstructor_ = @Autowired) -@Component -public class SameUrlDataInterceptor extends RepeatSubmitInterceptor { - public final String REPEAT_PARAMS = "repeatParams"; - - public final String REPEAT_TIME = "repeatTime"; - - private final TokenProperties tokenProperties; - private final RepeatSubmitProperties repeatSubmitProperties; - - - @SuppressWarnings("unchecked") - @Override - public boolean isRepeatSubmit(RepeatSubmit repeatSubmit, HttpServletRequest request) { - // 如果注解不为0 则使用注解数值 - long intervalTime = repeatSubmitProperties.getIntervalTime(); - if (repeatSubmit.intervalTime() > 0) { - intervalTime = repeatSubmit.timeUnit().toMillis(repeatSubmit.intervalTime()); - } - String nowParams = ""; - if (request instanceof RepeatedlyRequestWrapper) { - RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request; - try { - nowParams = IoUtil.readUtf8(repeatedlyRequest.getInputStream()); - } catch (IOException e) { - log.warn("读取流出现问题!"); - } - } - - // body参数为空,获取Parameter的数据 - if (StringUtils.isEmpty(nowParams)) { - nowParams = JsonUtils.toJsonString(request.getParameterMap()); - } - Map nowDataMap = new HashMap(); - nowDataMap.put(REPEAT_PARAMS, nowParams); - nowDataMap.put(REPEAT_TIME, System.currentTimeMillis()); - - // 请求地址(作为存放cache的key值) - String url = request.getRequestURI(); - - // 唯一值(没有消息头则使用请求地址) - String submitKey = request.getHeader(tokenProperties.getHeader()); - if (StringUtils.isEmpty(submitKey)) { - submitKey = url; - } - - // 唯一标识(指定key + 消息头) - String cacheRepeatKey = Constants.REPEAT_SUBMIT_KEY + submitKey; - - Object sessionObj = RedisUtils.getCacheObject(cacheRepeatKey); - if (sessionObj != null) { - Map sessionMap = (Map) sessionObj; - if (sessionMap.containsKey(url)) { - Map preDataMap = (Map) sessionMap.get(url); - if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap, intervalTime)) { - return true; - } - } - } - Map cacheMap = new HashMap(); - cacheMap.put(url, nowDataMap); - RedisUtils.setCacheObject(cacheRepeatKey, cacheMap, Convert.toInt(intervalTime), TimeUnit.MILLISECONDS); - return false; - } - - /** - * 判断参数是否相同 - */ - private boolean compareParams(Map nowMap, Map preMap) { - String nowParams = (String) nowMap.get(REPEAT_PARAMS); - String preParams = (String) preMap.get(REPEAT_PARAMS); - return nowParams.equals(preParams); - } - - /** - * 判断两次间隔时间 - */ - private boolean compareTime(Map nowMap, Map preMap, long intervalTime) { - long time1 = (Long) nowMap.get(REPEAT_TIME); - long time2 = (Long) preMap.get(REPEAT_TIME); - return (time1 - time2) < intervalTime; - } -} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/mybatisplus/CreateAndUpdateMetaObjectHandler.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/mybatisplus/CreateAndUpdateMetaObjectHandler.java index ae36fc3b4..feb73a458 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/mybatisplus/CreateAndUpdateMetaObjectHandler.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/mybatisplus/CreateAndUpdateMetaObjectHandler.java @@ -57,7 +57,7 @@ public class CreateAndUpdateMetaObjectHandler implements MetaObjectHandler { try { loginUser = SecurityUtils.getLoginUser(); } catch (Exception e) { - log.error("自动注入警告 => 用户未登录"); + log.warn("自动注入警告 => 用户未登录"); return null; } return loginUser.getUsername(); diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java index 188e24fdb..967ef6464 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java @@ -1,9 +1,9 @@ package com.ruoyi.framework.security.filter; import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.core.service.TokenService; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.framework.web.service.TokenService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; @@ -33,8 +33,7 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter throws ServletException, IOException { LoginUser loginUser = tokenService.getLoginUser(request); - if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication())) - { + if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication())) { tokenService.verifyToken(loginUser); UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities()); authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java index 371a66121..857cbe376 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java @@ -4,11 +4,11 @@ import cn.hutool.http.HttpStatus; import com.ruoyi.common.constant.Constants; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.core.service.LogininforService; +import com.ruoyi.common.core.service.TokenService; import com.ruoyi.common.utils.JsonUtils; import com.ruoyi.common.utils.ServletUtils; import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.framework.web.service.AsyncService; -import com.ruoyi.framework.web.service.TokenService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.security.core.Authentication; @@ -31,7 +31,7 @@ public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler { private TokenService tokenService; @Autowired - private AsyncService asyncService; + private LogininforService asyncService; /** * 退出处理 diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/AsyncService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/AsyncService.java deleted file mode 100644 index 3199cdd3d..000000000 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/AsyncService.java +++ /dev/null @@ -1,97 +0,0 @@ -package com.ruoyi.framework.web.service; - -import com.ruoyi.common.utils.StringUtils; -import cn.hutool.http.useragent.UserAgent; -import cn.hutool.http.useragent.UserAgentUtil; -import com.ruoyi.common.constant.Constants; -import com.ruoyi.common.utils.ServletUtils; -import com.ruoyi.common.utils.ip.AddressUtils; -import com.ruoyi.system.domain.SysLogininfor; -import com.ruoyi.system.domain.SysOperLog; -import com.ruoyi.system.service.ISysLogininforService; -import com.ruoyi.system.service.ISysOperLogService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.scheduling.annotation.Async; -import org.springframework.stereotype.Component; - -import javax.servlet.http.HttpServletRequest; - -/** - * 异步工厂(产生任务用) - * - * @author Lion Li - */ -@Slf4j(topic = "sys-user") -@Async -@Component -public class AsyncService { - - @Autowired - private ISysLogininforService iSysLogininforService; - - @Autowired - private ISysOperLogService iSysOperLogService; - - /** - * 记录登录信息 - * - * @param username 用户名 - * @param status 状态 - * @param message 消息 - * @param args 列表 - */ - public void recordLogininfor(final String username, final String status, final String message, - HttpServletRequest request, final Object... args) { - final UserAgent userAgent = UserAgentUtil.parse(request.getHeader("User-Agent")); - final String ip = ServletUtils.getClientIP(request); - - String address = AddressUtils.getRealAddressByIP(ip); - StringBuilder s = new StringBuilder(); - s.append(getBlock(ip)); - s.append(address); - s.append(getBlock(username)); - s.append(getBlock(status)); - s.append(getBlock(message)); - // 打印信息到日志 - log.info(s.toString(), args); - // 获取客户端操作系统 - String os = userAgent.getOs().getName(); - // 获取客户端浏览器 - String browser = userAgent.getBrowser().getName(); - // 封装对象 - SysLogininfor logininfor = new SysLogininfor(); - logininfor.setUserName(username); - logininfor.setIpaddr(ip); - logininfor.setLoginLocation(address); - logininfor.setBrowser(browser); - logininfor.setOs(os); - logininfor.setMsg(message); - // 日志状态 - if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER)) { - logininfor.setStatus(Constants.SUCCESS); - } else if (Constants.LOGIN_FAIL.equals(status)) { - logininfor.setStatus(Constants.FAIL); - } - // 插入数据 - iSysLogininforService.insertLogininfor(logininfor); - } - - /** - * 操作日志记录 - * - * @param operLog 操作日志信息 - */ - public void recordOper(final SysOperLog operLog) { - // 远程查询操作地点 - operLog.setOperLocation(AddressUtils.getRealAddressByIP(operLog.getOperIp())); - iSysOperLogService.insertOperlog(operLog); - } - - private String getBlock(Object msg) { - if (msg == null) { - msg = ""; - } - return "[" + msg.toString() + "]"; - } -} diff --git a/ruoyi-generator/pom.xml b/ruoyi-generator/pom.xml index d5bdc7e53..e19cbb293 100644 --- a/ruoyi-generator/pom.xml +++ b/ruoyi-generator/pom.xml @@ -5,7 +5,7 @@ ruoyi-vue-plus com.ruoyi - 3.1.0 + 3.2.0 4.0.0 diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/util/GenUtils.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/util/GenUtils.java index a64ac3e10..bfa7598d9 100644 --- a/ruoyi-generator/src/main/java/com/ruoyi/generator/util/GenUtils.java +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/util/GenUtils.java @@ -33,8 +33,7 @@ public class GenUtils /** * 初始化列属性字段 */ - public static void initColumnField(GenTableColumn column, GenTable table) - { + public static void initColumnField(GenTableColumn column, GenTable table) { String dataType = getDbType(column.getColumnType()); String columnName = column.getColumnName(); column.setTableId(table.getTableId()); @@ -44,48 +43,48 @@ public class GenUtils // 设置默认类型 column.setJavaType(GenConstants.TYPE_STRING); - if (arraysContains(GenConstants.COLUMNTYPE_STR, dataType) || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType)) - { + if (arraysContains(GenConstants.COLUMNTYPE_STR, dataType) || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType)) { // 字符串长度超过500设置为文本域 Integer columnLength = getColumnLength(column.getColumnType()); String htmlType = columnLength >= 500 || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType) ? GenConstants.HTML_TEXTAREA : GenConstants.HTML_INPUT; column.setHtmlType(htmlType); - } - else if (arraysContains(GenConstants.COLUMNTYPE_TIME, dataType)) - { + } else if (arraysContains(GenConstants.COLUMNTYPE_TIME, dataType)) { column.setJavaType(GenConstants.TYPE_DATE); column.setHtmlType(GenConstants.HTML_DATETIME); - } - else if (arraysContains(GenConstants.COLUMNTYPE_NUMBER, dataType)) - { + } else if (arraysContains(GenConstants.COLUMNTYPE_NUMBER, dataType)) { column.setHtmlType(GenConstants.HTML_INPUT); // 如果是浮点型 统一用BigDecimal String[] str = StringUtils.split(StringUtils.substringBetween(column.getColumnType(), "(", ")"), ","); - if (str != null && str.length == 2 && Integer.parseInt(str[1]) > 0) - { + if (str != null && str.length == 2 && Integer.parseInt(str[1]) > 0) { column.setJavaType(GenConstants.TYPE_BIGDECIMAL); } // 如果是整形 - else if (str != null && str.length == 1 && Integer.parseInt(str[0]) <= 10) - { + else if (str != null && str.length == 1 && Integer.parseInt(str[0]) <= 10) { column.setJavaType(GenConstants.TYPE_INTEGER); } // 长整形 - else - { + else { column.setJavaType(GenConstants.TYPE_LONG); } } // 插入字段(默认所有字段都需要插入) - column.setIsInsert(GenConstants.REQUIRE); - + // 主键不需要添加 + if (!arraysContains(GenConstants.COLUMNNAME_NOT_ADD, columnName) && !column.isPk()) { + column.setIsInsert(GenConstants.REQUIRE); + } // 编辑字段 - if (!arraysContains(GenConstants.COLUMNNAME_NOT_EDIT, columnName) && !column.isPk()) + // 编辑需要主键 + if (!arraysContains(GenConstants.COLUMNNAME_NOT_EDIT, columnName)) { column.setIsEdit(GenConstants.REQUIRE); } + // 编辑需要的设置必选 + if (!arraysContains(GenConstants.COLUMNNAME_NOT_EDIT, columnName)) + { + column.setIsRequired(GenConstants.REQUIRE); + } // 列表字段 if (!arraysContains(GenConstants.COLUMNNAME_NOT_LIST, columnName) && !column.isPk()) { diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityUtils.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityUtils.java index 098a91892..38762275d 100644 --- a/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityUtils.java +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityUtils.java @@ -16,7 +16,7 @@ import java.util.Map; /** * 模板处理工具类 - * + * * @author ruoyi */ public class VelocityUtils @@ -61,6 +61,7 @@ public class VelocityUtils velocityContext.put("permissionPrefix", getPermissionPrefix(moduleName, businessName)); velocityContext.put("columns", genTable.getColumns()); velocityContext.put("table", genTable); + velocityContext.put("dicts", getDicts(genTable)); setMenuVelocityContext(velocityContext, genTable); if (GenConstants.TPL_TREE.equals(tplCategory)) { @@ -245,7 +246,7 @@ public class VelocityUtils /** * 根据列类型获取导入包 - * + * * @param genTable 业务表对象 * @return 返回需要导入的包列表 */ @@ -273,6 +274,27 @@ public class VelocityUtils return importList; } + /** + * 根据列类型获取字典组 + * + * @param genTable 业务表对象 + * @return 返回字典组 + */ + public static String getDicts(GenTable genTable) + { + List columns = genTable.getColumns(); + List dicts = new ArrayList(); + for (GenTableColumn column : columns) + { + if (!column.isSuperColumn() && StringUtils.isNotEmpty(column.getDictType()) && StringUtils.equalsAny( + column.getHtmlType(), new String[] { GenConstants.HTML_SELECT, GenConstants.HTML_RADIO })) + { + dicts.add("'" + column.getDictType() + "'"); + } + } + return StringUtils.join(dicts, ", "); + } + /** * 获取权限前缀 * diff --git a/ruoyi-generator/src/main/resources/vm/java/mapper.java.vm b/ruoyi-generator/src/main/resources/vm/java/mapper.java.vm index 6f8ff7b9f..af2edb670 100644 --- a/ruoyi-generator/src/main/resources/vm/java/mapper.java.vm +++ b/ruoyi-generator/src/main/resources/vm/java/mapper.java.vm @@ -2,8 +2,6 @@ package ${packageName}.mapper; import ${packageName}.domain.${ClassName}; import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus; -import com.ruoyi.common.core.mybatisplus.cache.MybatisPlusRedisCache; -import org.apache.ibatis.annotations.CacheNamespace; /** * ${functionName}Mapper接口 diff --git a/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm b/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm index 32a9ac018..b9a45c675 100644 --- a/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm +++ b/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm @@ -79,7 +79,12 @@ public class ${ClassName}ServiceImpl extends ServicePlusImpl<${ClassName}Mapper, public Boolean insertByBo(${ClassName}Bo bo) { ${ClassName} add = BeanUtil.toBean(bo, ${ClassName}.class); validEntityBeforeSave(add); - return save(add); + boolean flag = save(add); +#set($pk=$pkColumn.javaField.substring(0,1).toUpperCase() + ${pkColumn.javaField.substring(1)}) + if (flag) { + bo.set$pk(add.get$pk()); + } + return flag; } @Override diff --git a/ruoyi-generator/src/main/resources/vm/java/vo.java.vm b/ruoyi-generator/src/main/resources/vm/java/vo.java.vm index e97cd2467..489723ef3 100644 --- a/ruoyi-generator/src/main/resources/vm/java/vo.java.vm +++ b/ruoyi-generator/src/main/resources/vm/java/vo.java.vm @@ -27,14 +27,8 @@ public class ${ClassName}Vo { private static final long serialVersionUID = 1L; - /** - * $pkColumn.columnComment - */ - @ApiModelProperty("$pkColumn.columnComment") - private ${pkColumn.javaType} ${pkColumn.javaField}; - #foreach ($column in $columns) -#if($column.isList && $column.isPk!=1) +#if($column.isList) /** * $column.columnComment */ diff --git a/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm b/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm index 89852509d..c159016e2 100644 --- a/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm +++ b/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm @@ -25,10 +25,10 @@ @@ -108,7 +108,7 @@ #elseif($column.list && $column.dictType && "" != $column.dictType) #elseif($column.list && "" != $javaField) @@ -183,10 +183,10 @@ @@ -201,10 +201,10 @@ - {{dict.dictLabel}} + v-for="dict in dict.type.${dictType}" + :key="dict.value" + :label="dict.value"> + {{dict.label}} @@ -218,11 +218,11 @@ {{dict.dictLabel}} + >{{dict.label}} #elseif($column.htmlType == "radio" && $dictType) @@ -263,6 +263,9 @@ import "@riophae/vue-treeselect/dist/vue-treeselect.css"; export default { name: "${BusinessName}", +#if(${dicts} != '') + dicts: [${dicts}], +#end components: { Treeselect }, @@ -283,16 +286,7 @@ export default { // 是否显示弹出层 open: false, #foreach ($column in $columns) -#set($parentheseIndex=$column.columnComment.indexOf("(")) -#if($parentheseIndex != -1) -#set($comment=$column.columnComment.substring(0, $parentheseIndex)) -#else -#set($comment=$column.columnComment) -#end -#if(${column.dictType} && ${column.dictType} != '') - // $comment字典 - ${column.javaField}Options: [], -#elseif($column.htmlType == "datetime" && $column.queryType == "BETWEEN") +#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN") #set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) // $comment时间范围 daterange${AttrName}: [], @@ -330,13 +324,6 @@ export default { }, created() { this.getList(); -#foreach ($column in $columns) -#if(${column.dictType} && ${column.dictType} != '') - this.getDicts("${column.dictType}").then(response => { - this.${column.javaField}Options = response.data; - }); -#end -#end }, methods: { /** 查询${functionName}列表 */ @@ -464,7 +451,7 @@ export default { #end if (this.form.${pkColumn.javaField} != null) { update${BusinessName}(this.form).then(response => { - this.msgSuccess("修改成功"); + this.#[[$modal]]#.msgSuccess("修改成功"); this.open = false; this.getList(); }).finally(() => { @@ -472,7 +459,7 @@ export default { }); } else { add${BusinessName}(this.form).then(response => { - this.msgSuccess("新增成功"); + this.#[[$modal]]#.msgSuccess("新增成功"); this.open = false; this.getList(); }).finally(() => { @@ -484,19 +471,15 @@ export default { }, /** 删除按钮操作 */ handleDelete(row) { - this.$confirm('是否确认删除${functionName}编号为"' + row.${pkColumn.javaField} + '"的数据项?', "警告", { - confirmButtonText: "确定", - cancelButtonText: "取消", - type: "warning" - }).then(() => { - this.loading = true; - return del${BusinessName}(row.${pkColumn.javaField}); - }).then(() => { - this.loading = false; - this.getList(); - this.msgSuccess("删除成功"); + this.#[[$modal]]#.confirm('是否确认删除${functionName}编号为"' + row.${pkColumn.javaField} + '"的数据项?').then(() => { + this.loading = true; + return del${BusinessName}(row.${pkColumn.javaField}); + }).then(() => { + this.loading = false; + this.getList(); + this.#[[$modal]]#.msgSuccess("删除成功"); }).finally(() => { - this.loading = false; + this.loading = false; }); } } diff --git a/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm b/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm index 4b055faa0..05a8baf2c 100644 --- a/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm +++ b/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm @@ -25,10 +25,10 @@ @@ -137,7 +137,7 @@ #elseif($column.list && $column.dictType && "" != $column.dictType) #elseif($column.list && "" != $javaField) @@ -163,7 +163,7 @@ - + @@ -223,10 +223,10 @@ - {{dict.dictLabel}} + v-for="dict in dict.type.${dictType}" + :key="dict.value" + :label="dict.value"> + {{dict.label}} @@ -240,11 +240,11 @@ {{dict.dictLabel}} + >{{dict.label}} #elseif($column.htmlType == "radio" && $dictType) @@ -315,6 +315,9 @@ import { list${BusinessName}, get${BusinessName}, del${BusinessName}, add${Busin export default { name: "${BusinessName}", +#if(${dicts} != '') + dicts: [${dicts}], +#end data() { return { // 按钮loading @@ -348,16 +351,7 @@ export default { // 是否显示弹出层 open: false, #foreach ($column in $columns) -#set($parentheseIndex=$column.columnComment.indexOf("(")) -#if($parentheseIndex != -1) -#set($comment=$column.columnComment.substring(0, $parentheseIndex)) -#else -#set($comment=$column.columnComment) -#end -#if(${column.dictType} && ${column.dictType} != '') - // $comment字典 - ${column.javaField}Options: [], -#elseif($column.htmlType == "datetime" && $column.queryType == "BETWEEN") +#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN") #set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) // $comment时间范围 daterange${AttrName}: [], @@ -397,13 +391,6 @@ export default { }, created() { this.getList(); -#foreach ($column in $columns) -#if(${column.dictType} && ${column.dictType} != '') - this.getDicts("${column.dictType}").then(response => { - this.${column.javaField}Options = response.data; - }); -#end -#end }, methods: { /** 查询${functionName}列表 */ @@ -519,7 +506,7 @@ export default { #end if (this.form.${pkColumn.javaField} != null) { update${BusinessName}(this.form).then(response => { - this.msgSuccess("修改成功"); + this.#[[$modal]]#.msgSuccess("修改成功"); this.open = false; this.getList(); }).finally(() => { @@ -527,7 +514,7 @@ export default { }); } else { add${BusinessName}(this.form).then(response => { - this.msgSuccess("新增成功"); + this.#[[$modal]]#.msgSuccess("新增成功"); this.open = false; this.getList(); }).finally(() => { @@ -540,19 +527,15 @@ export default { /** 删除按钮操作 */ handleDelete(row) { const ${pkColumn.javaField}s = row.${pkColumn.javaField} || this.ids; - this.$confirm('是否确认删除${functionName}编号为"' + ${pkColumn.javaField}s + '"的数据项?', "警告", { - confirmButtonText: "确定", - cancelButtonText: "取消", - type: "warning" - }).then(() => { - this.loading = true; - return del${BusinessName}(${pkColumn.javaField}s); - }).then(() => { - this.loading = false; - this.getList(); - this.msgSuccess("删除成功"); + this.#[[$modal]]#.confirm('是否确认删除${functionName}编号为"' + ${pkColumn.javaField}s + '"的数据项?').then(() => { + this.loading = true; + return del${BusinessName}(${pkColumn.javaField}s); + }).then(() => { + this.loading = false; + this.getList(); + this.#[[$modal]]#.msgSuccess("删除成功"); }).finally(() => { - this.loading = false; + this.loading = false; }); }, #if($table.sub) @@ -574,7 +557,7 @@ export default { /** ${subTable.functionName}删除按钮操作 */ handleDelete${subClassName}() { if (this.checked${subClassName}.length == 0) { - this.msgError("请先选择要删除的${subTable.functionName}数据"); + this.#[[$modal]]#.msgError("请先选择要删除的${subTable.functionName}数据"); } else { const ${subclassName}List = this.${subclassName}List; const checked${subClassName} = this.checked${subClassName}; @@ -590,7 +573,7 @@ export default { #end /** 导出按钮操作 */ handleExport() { - this.downLoadExcel('/${moduleName}/${businessName}/export', this.queryParams); + this.#[[$download]]#.excel('/${moduleName}/${businessName}/export', this.queryParams); } } }; diff --git a/ruoyi-oss/pom.xml b/ruoyi-oss/pom.xml index 51111c1c7..6c93df8e0 100644 --- a/ruoyi-oss/pom.xml +++ b/ruoyi-oss/pom.xml @@ -5,7 +5,7 @@ ruoyi-vue-plus com.ruoyi - 3.1.0 + 3.2.0 4.0.0 diff --git a/ruoyi-oss/src/main/java/com/ruoyi/oss/constant/CloudConstant.java b/ruoyi-oss/src/main/java/com/ruoyi/oss/constant/CloudConstant.java index ab7c09626..7bba1f4db 100644 --- a/ruoyi-oss/src/main/java/com/ruoyi/oss/constant/CloudConstant.java +++ b/ruoyi-oss/src/main/java/com/ruoyi/oss/constant/CloudConstant.java @@ -4,7 +4,7 @@ import java.util.Arrays; import java.util.List; /** - * 云存储常量 + * 对象存储常量 * * @author Lion Li */ @@ -16,7 +16,7 @@ public class CloudConstant { public static final String SYS_OSS_KEY = "sys_oss:"; /** - * 云存储配置KEY + * 对象存储配置KEY */ public static final String CLOUD_STORAGE_CONFIG_KEY = "CloudStorageConfig"; diff --git a/ruoyi-oss/src/main/java/com/ruoyi/oss/enumd/CloudServiceEnumd.java b/ruoyi-oss/src/main/java/com/ruoyi/oss/enumd/CloudServiceEnumd.java index 11a571c26..ac8ad8184 100644 --- a/ruoyi-oss/src/main/java/com/ruoyi/oss/enumd/CloudServiceEnumd.java +++ b/ruoyi-oss/src/main/java/com/ruoyi/oss/enumd/CloudServiceEnumd.java @@ -9,7 +9,7 @@ import lombok.AllArgsConstructor; import lombok.Getter; /** - * 云存储服务商枚举 + * 对象存储服务商枚举 * * @author Lion Li */ diff --git a/ruoyi-oss/src/main/java/com/ruoyi/oss/properties/CloudStorageProperties.java b/ruoyi-oss/src/main/java/com/ruoyi/oss/properties/CloudStorageProperties.java index 1c62ae2ca..9d3f15afb 100644 --- a/ruoyi-oss/src/main/java/com/ruoyi/oss/properties/CloudStorageProperties.java +++ b/ruoyi-oss/src/main/java/com/ruoyi/oss/properties/CloudStorageProperties.java @@ -5,7 +5,7 @@ import lombok.Data; import java.util.Date; /** - * OSS云存储 配置属性 + * OSS对象存储 配置属性 * * @author Lion Li */ diff --git a/ruoyi-oss/src/main/java/com/ruoyi/oss/service/ICloudStorageStrategy.java b/ruoyi-oss/src/main/java/com/ruoyi/oss/service/ICloudStorageStrategy.java index f91fc7af6..96ea53992 100644 --- a/ruoyi-oss/src/main/java/com/ruoyi/oss/service/ICloudStorageStrategy.java +++ b/ruoyi-oss/src/main/java/com/ruoyi/oss/service/ICloudStorageStrategy.java @@ -5,7 +5,7 @@ import com.ruoyi.oss.entity.UploadResult; import java.io.InputStream; /** - * 云存储策略 + * 对象存储策略 * * @author Lion Li */ diff --git a/ruoyi-oss/src/main/java/com/ruoyi/oss/service/abstractd/AbstractCloudStorageStrategy.java b/ruoyi-oss/src/main/java/com/ruoyi/oss/service/abstractd/AbstractCloudStorageStrategy.java index 1d7060fbb..8f5e9625d 100644 --- a/ruoyi-oss/src/main/java/com/ruoyi/oss/service/abstractd/AbstractCloudStorageStrategy.java +++ b/ruoyi-oss/src/main/java/com/ruoyi/oss/service/abstractd/AbstractCloudStorageStrategy.java @@ -11,7 +11,7 @@ import com.ruoyi.oss.service.ICloudStorageStrategy; import java.io.InputStream; /** - * 云存储策略(支持七牛、阿里云、腾讯云、minio) + * 对象存储策略(支持七牛、阿里云、腾讯云、minio) * * @author Lion Li */ diff --git a/ruoyi-quartz/pom.xml b/ruoyi-quartz/pom.xml index aa4faf5ee..ee682f717 100644 --- a/ruoyi-quartz/pom.xml +++ b/ruoyi-quartz/pom.xml @@ -5,7 +5,7 @@ ruoyi-vue-plus com.ruoyi - 3.1.0 + 3.2.0 4.0.0 @@ -37,4 +37,4 @@ - + \ No newline at end of file diff --git a/ruoyi-system/pom.xml b/ruoyi-system/pom.xml index 8e536cce2..e7c12ef14 100644 --- a/ruoyi-system/pom.xml +++ b/ruoyi-system/pom.xml @@ -5,7 +5,7 @@ ruoyi-vue-plus com.ruoyi - 3.1.0 + 3.2.0 4.0.0 @@ -31,4 +31,4 @@ - + \ No newline at end of file diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOss.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOss.java index 7b7639c38..d090c9297 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOss.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOss.java @@ -9,7 +9,7 @@ import java.io.Serializable; import java.util.Date; /** - * OSS云存储对象 + * OSS对象存储对象 * * @author Lion Li */ @@ -23,7 +23,7 @@ public class SysOss implements Serializable { /** - * 云存储主键 + * 对象存储主键 */ @TableId(value = "oss_id", type = IdType.AUTO) private Long ossId; diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOssConfig.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOssConfig.java index 9f0e28e4a..3a6fb835a 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOssConfig.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOssConfig.java @@ -9,7 +9,7 @@ import java.util.Date; import java.math.BigDecimal; /** - * 云存储配置对象 sys_oss_config + * 对象存储配置对象 sys_oss_config * * @author ruoyi * @date 2021-08-11 diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysOssBo.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysOssBo.java index a30ff3222..e41f94ff6 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysOssBo.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysOssBo.java @@ -7,13 +7,13 @@ import lombok.Data; import lombok.EqualsAndHashCode; /** - * OSS云存储分页查询对象 sys_oss + * OSS对象存储分页查询对象 sys_oss * * @author Lion Li */ @Data @EqualsAndHashCode(callSuper = true) -@ApiModel("OSS云存储分页查询对象") +@ApiModel("OSS对象存储分页查询对象") public class SysOssBo extends BaseEntity { /** diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysOssConfigBo.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysOssConfigBo.java index 3e8defc94..44bc7a205 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysOssConfigBo.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysOssConfigBo.java @@ -13,7 +13,7 @@ import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; /** - * 云存储配置业务对象 sys_oss_config + * 对象存储配置业务对象 sys_oss_config * * @author Lion Li * @author 孤舟烟雨 @@ -22,7 +22,7 @@ import javax.validation.constraints.Size; @Data @EqualsAndHashCode(callSuper = true) -@ApiModel("云存储配置业务对象") +@ApiModel("对象存储配置业务对象") public class SysOssConfigBo extends BaseEntity { /** diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/RouterVo.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/RouterVo.java index 455563ad5..387adb447 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/RouterVo.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/RouterVo.java @@ -41,6 +41,11 @@ public class RouterVo { */ private String component; + /** + * 路由参数:如 {"id": 1, "name": "ry"} + */ + private String query; + /** * 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面 */ diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysOssConfigVo.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysOssConfigVo.java index 58e3900f1..79b2b1766 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysOssConfigVo.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysOssConfigVo.java @@ -8,14 +8,14 @@ import lombok.Data; /** - * 云存储配置视图对象 sys_oss_config + * 对象存储配置视图对象 sys_oss_config * * @author Lion Li * @author 孤舟烟雨 * @date 2021-08-13 */ @Data -@ApiModel("云存储配置视图对象") +@ApiModel("对象存储配置视图对象") @ExcelIgnoreUnannotated public class SysOssConfigVo { diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysOssVo.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysOssVo.java index 8bc59b2cd..26e0151df 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysOssVo.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysOssVo.java @@ -7,20 +7,20 @@ import lombok.Data; import java.util.Date; /** - * OSS云存储视图对象 sys_oss + * OSS对象存储视图对象 sys_oss * * @author Lion Li */ @Data -@ApiModel("OSS云存储视图对象") +@ApiModel("OSS对象存储视图对象") public class SysOssVo { private static final long serialVersionUID = 1L; /** - * 云存储主键 + * 对象存储主键 */ - @ApiModelProperty("云存储主键") + @ApiModelProperty("对象存储主键") private Long ossId; /** diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOssConfigMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOssConfigMapper.java index 9546e7321..db63709ba 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOssConfigMapper.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOssConfigMapper.java @@ -4,7 +4,7 @@ import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus; import com.ruoyi.system.domain.SysOssConfig; /** - * 云存储配置Mapper接口 + * 对象存储配置Mapper接口 * * @author Lion Li * @author 孤舟烟雨 diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOssConfigService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOssConfigService.java index f1ece65cd..2748078f4 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOssConfigService.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOssConfigService.java @@ -9,7 +9,7 @@ import com.ruoyi.system.domain.vo.SysOssConfigVo; import java.util.Collection; /** - * 云存储配置Service接口 + * 对象存储配置Service接口 * * @author Lion Li * @author 孤舟烟雨 @@ -29,15 +29,15 @@ public interface ISysOssConfigService extends IServicePlus authorities = loginUser.getPermissions(); - for (String permission : permissions.split(PERMISSION_DELIMETER)) - { - if (permission != null && hasPermissions(authorities, permission)) - { + for (String permission : permissions.split(PERMISSION_DELIMETER)) { + if (permission != null && hasPermissions(authorities, permission)) { return true; } } @@ -92,22 +86,17 @@ public class PermissionService * @param role 角色字符串 * @return 用户是否具备某角色 */ - public boolean hasRole(String role) - { - if (StringUtils.isEmpty(role)) - { + public boolean hasRole(String role) { + if (StringUtils.isEmpty(role)) { return false; } LoginUser loginUser = SecurityUtils.getLoginUser(); - if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles())) - { + if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles())) { return false; } - for (SysRole sysRole : loginUser.getUser().getRoles()) - { + for (SysRole sysRole : loginUser.getUser().getRoles()) { String roleKey = sysRole.getRoleKey(); - if (SUPER_ADMIN.equals(roleKey) || roleKey.equals(StringUtils.trim(role))) - { + if (SUPER_ADMIN.equals(roleKey) || roleKey.equals(StringUtils.trim(role))) { return true; } } @@ -120,8 +109,7 @@ public class PermissionService * @param role 角色名称 * @return 用户是否不具备某角色 */ - public boolean lacksRole(String role) - { + public boolean lacksRole(String role) { return hasRole(role) != true; } @@ -131,21 +119,16 @@ public class PermissionService * @param roles 以 ROLE_NAMES_DELIMETER 为分隔符的角色列表 * @return 用户是否具有以下任意一个角色 */ - public boolean hasAnyRoles(String roles) - { - if (StringUtils.isEmpty(roles)) - { + public boolean hasAnyRoles(String roles) { + if (StringUtils.isEmpty(roles)) { return false; } LoginUser loginUser = SecurityUtils.getLoginUser(); - if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles())) - { + if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles())) { return false; } - for (String role : roles.split(ROLE_DELIMETER)) - { - if (hasRole(role)) - { + for (String role : roles.split(ROLE_DELIMETER)) { + if (hasRole(role)) { return true; } } @@ -156,11 +139,10 @@ public class PermissionService * 判断是否包含权限 * * @param permissions 权限列表 - * @param permission 权限字符串 + * @param permission 权限字符串 * @return 用户是否具备某权限 */ - private boolean hasPermissions(Set permissions, String permission) - { + private boolean hasPermissions(Set permissions, String permission) { return permissions.contains(ALL_PERMISSION) || permissions.contains(StringUtils.trim(permission)); } } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/SysLoginService.java similarity index 53% rename from ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java rename to ruoyi-system/src/main/java/com/ruoyi/system/service/SysLoginService.java index 7df8c26d5..0d2218ecb 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/SysLoginService.java @@ -1,8 +1,10 @@ -package com.ruoyi.framework.web.service; +package com.ruoyi.system.service; import com.ruoyi.common.constant.Constants; import com.ruoyi.common.core.domain.entity.SysUser; import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.core.service.LogininforService; +import com.ruoyi.common.core.service.TokenService; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.exception.user.CaptchaException; import com.ruoyi.common.exception.user.CaptchaExpireException; @@ -11,14 +13,12 @@ import com.ruoyi.common.utils.DateUtils; import com.ruoyi.common.utils.MessageUtils; import com.ruoyi.common.utils.RedisUtils; import com.ruoyi.common.utils.ServletUtils; -import com.ruoyi.system.service.ISysConfigService; -import com.ruoyi.system.service.ISysUserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; -import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; @@ -28,66 +28,57 @@ import javax.servlet.http.HttpServletRequest; * * @author ruoyi */ -@Component -public class SysLoginService -{ +@Service +public class SysLoginService { @Autowired private TokenService tokenService; @Resource private AuthenticationManager authenticationManager; - @Autowired + @Autowired private ISysUserService userService; - @Autowired - private ISysConfigService configService; + @Autowired + private ISysConfigService configService; - @Autowired - private AsyncService asyncService; + @Autowired + private LogininforService asyncService; /** * 登录验证 * * @param username 用户名 * @param password 密码 - * @param code 验证码 - * @param uuid 唯一标识 + * @param code 验证码 + * @param uuid 唯一标识 * @return 结果 */ - public String login(String username, String password, String code, String uuid) - { - HttpServletRequest request = ServletUtils.getRequest(); - boolean captchaOnOff = configService.selectCaptchaOnOff(); + public String login(String username, String password, String code, String uuid) { + HttpServletRequest request = ServletUtils.getRequest(); + boolean captchaOnOff = configService.selectCaptchaOnOff(); // 验证码开关 - if (captchaOnOff) - { + if (captchaOnOff) { validateCaptcha(username, code, uuid, request); } // 用户验证 Authentication authentication = null; - try - { + try { // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername authentication = authenticationManager .authenticate(new UsernamePasswordAuthenticationToken(username, password)); - } - catch (Exception e) - { - if (e instanceof BadCredentialsException) - { - asyncService.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"), request); + } catch (Exception e) { + if (e instanceof BadCredentialsException) { + asyncService.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"), request); throw new UserPasswordNotMatchException(); - } - else - { - asyncService.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage(), request); + } else { + asyncService.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage(), request); throw new ServiceException(e.getMessage()); } } - asyncService.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"), request); + asyncService.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"), request); LoginUser loginUser = (LoginUser) authentication.getPrincipal(); - recordLoginInfo(loginUser.getUser()); + recordLoginInfo(loginUser.getUserId()); // 生成token return tokenService.createToken(loginUser); } @@ -96,32 +87,34 @@ public class SysLoginService * 校验验证码 * * @param username 用户名 - * @param code 验证码 - * @param uuid 唯一标识 + * @param code 验证码 + * @param uuid 唯一标识 * @return 结果 */ public void validateCaptcha(String username, String code, String uuid, HttpServletRequest request) { - String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid; - String captcha = RedisUtils.getCacheObject(verifyKey); + String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid; + String captcha = RedisUtils.getCacheObject(verifyKey); RedisUtils.deleteObject(verifyKey); - if (captcha == null) { - asyncService.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"), request); - throw new CaptchaExpireException(); - } - if (!code.equalsIgnoreCase(captcha)) { - asyncService.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"), request); - throw new CaptchaException(); - } + if (captcha == null) { + asyncService.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"), request); + throw new CaptchaExpireException(); + } + if (!code.equalsIgnoreCase(captcha)) { + asyncService.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"), request); + throw new CaptchaException(); + } } /** * 记录登录信息 + * + * @param userId 用户ID */ - public void recordLoginInfo(SysUser user) - { - user.setLoginIp(ServletUtils.getClientIP()); - user.setLoginDate(DateUtils.getNowDate()); - user.setUpdateBy(user.getUserName()); - userService.updateUserProfile(user); + public void recordLoginInfo(Long userId) { + SysUser sysUser = new SysUser(); + sysUser.setUserId(userId); + sysUser.setLoginIp(ServletUtils.getClientIP()); + sysUser.setLoginDate(DateUtils.getNowDate()); + userService.updateUserProfile(sysUser); } } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysPermissionService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/SysPermissionService.java similarity index 66% rename from ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysPermissionService.java rename to ruoyi-system/src/main/java/com/ruoyi/system/service/SysPermissionService.java index feb803875..095b3b566 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysPermissionService.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/SysPermissionService.java @@ -1,21 +1,20 @@ -package com.ruoyi.framework.web.service; +package com.ruoyi.system.service; + +import com.ruoyi.common.core.domain.entity.SysUser; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; import java.util.HashSet; import java.util.Set; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; -import com.ruoyi.common.core.domain.entity.SysUser; -import com.ruoyi.system.service.ISysMenuService; -import com.ruoyi.system.service.ISysRoleService; /** * 用户权限处理 - * + * * @author ruoyi */ -@Component -public class SysPermissionService -{ +@Service +public class SysPermissionService { + @Autowired private ISysRoleService roleService; @@ -24,20 +23,16 @@ public class SysPermissionService /** * 获取角色数据权限 - * + * * @param user 用户信息 * @return 角色权限信息 */ - public Set getRolePermission(SysUser user) - { + public Set getRolePermission(SysUser user) { Set roles = new HashSet(); // 管理员拥有所有权限 - if (user.isAdmin()) - { + if (user.isAdmin()) { roles.add("admin"); - } - else - { + } else { roles.addAll(roleService.selectRolePermissionByUserId(user.getUserId())); } return roles; @@ -45,20 +40,16 @@ public class SysPermissionService /** * 获取菜单数据权限 - * + * * @param user 用户信息 * @return 菜单权限信息 */ - public Set getMenuPermission(SysUser user) - { + public Set getMenuPermission(SysUser user) { Set perms = new HashSet(); // 管理员拥有所有权限 - if (user.isAdmin()) - { + if (user.isAdmin()) { perms.add("*:*:*"); - } - else - { + } else { perms.addAll(menuService.selectMenuPermsByUserId(user.getUserId())); } return perms; diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysRegisterService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/SysRegisterService.java similarity index 66% rename from ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysRegisterService.java rename to ruoyi-system/src/main/java/com/ruoyi/system/service/SysRegisterService.java index 149392f21..045c680c3 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysRegisterService.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/SysRegisterService.java @@ -1,84 +1,67 @@ -package com.ruoyi.framework.web.service; +package com.ruoyi.system.service; import com.ruoyi.common.constant.Constants; import com.ruoyi.common.constant.UserConstants; import com.ruoyi.common.core.domain.entity.SysUser; import com.ruoyi.common.core.domain.model.RegisterBody; +import com.ruoyi.common.core.service.LogininforService; import com.ruoyi.common.exception.user.CaptchaException; import com.ruoyi.common.exception.user.CaptchaExpireException; import com.ruoyi.common.utils.*; -import com.ruoyi.system.service.ISysConfigService; -import com.ruoyi.system.service.ISysUserService; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; /** * 注册校验方法 * * @author ruoyi */ -@Component -public class SysRegisterService -{ +@Service +public class SysRegisterService { + @Autowired private ISysUserService userService; @Autowired private ISysConfigService configService; - @Autowired - private AsyncService asyncService; + @Autowired + private LogininforService asyncService; /** * 注册 */ - public String register(RegisterBody registerBody) - { + public String register(RegisterBody registerBody) { String msg = "", username = registerBody.getUsername(), password = registerBody.getPassword(); boolean captchaOnOff = configService.selectCaptchaOnOff(); // 验证码开关 - if (captchaOnOff) - { + if (captchaOnOff) { validateCaptcha(username, registerBody.getCode(), registerBody.getUuid()); } - if (StringUtils.isEmpty(username)) - { + if (StringUtils.isEmpty(username)) { msg = "用户名不能为空"; - } - else if (StringUtils.isEmpty(password)) - { + } else if (StringUtils.isEmpty(password)) { msg = "用户密码不能为空"; - } - else if (username.length() < UserConstants.USERNAME_MIN_LENGTH - || username.length() > UserConstants.USERNAME_MAX_LENGTH) - { + } else if (username.length() < UserConstants.USERNAME_MIN_LENGTH + || username.length() > UserConstants.USERNAME_MAX_LENGTH) { msg = "账户长度必须在2到20个字符之间"; - } - else if (password.length() < UserConstants.PASSWORD_MIN_LENGTH - || password.length() > UserConstants.PASSWORD_MAX_LENGTH) - { + } else if (password.length() < UserConstants.PASSWORD_MIN_LENGTH + || password.length() > UserConstants.PASSWORD_MAX_LENGTH) { msg = "密码长度必须在5到20个字符之间"; - } - else if (UserConstants.NOT_UNIQUE.equals(userService.checkUserNameUnique(username))) - { + } else if (UserConstants.NOT_UNIQUE.equals(userService.checkUserNameUnique(username))) { msg = "保存用户'" + username + "'失败,注册账号已存在"; - } - else - { + } else { SysUser sysUser = new SysUser(); sysUser.setUserName(username); sysUser.setNickName(username); sysUser.setPassword(SecurityUtils.encryptPassword(registerBody.getPassword())); boolean regFlag = userService.registerUser(sysUser); - if (!regFlag) - { + if (!regFlag) { msg = "注册失败,请联系系统管理人员"; - } - else - { - asyncService.recordLogininfor(username, Constants.REGISTER, + } else { + asyncService.recordLogininfor(username, Constants.REGISTER, MessageUtils.message("user.register.success"), ServletUtils.getRequest()); } } @@ -89,21 +72,18 @@ public class SysRegisterService * 校验验证码 * * @param username 用户名 - * @param code 验证码 - * @param uuid 唯一标识 + * @param code 验证码 + * @param uuid 唯一标识 * @return 结果 */ - public void validateCaptcha(String username, String code, String uuid) - { + public void validateCaptcha(String username, String code, String uuid) { String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid; String captcha = RedisUtils.getCacheObject(verifyKey); RedisUtils.deleteObject(verifyKey); - if (captcha == null) - { + if (captcha == null) { throw new CaptchaExpireException(); } - if (!code.equalsIgnoreCase(captcha)) - { + if (!code.equalsIgnoreCase(captcha)) { throw new CaptchaException(); } } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysLogininforServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysLogininforServiceImpl.java index 8b5661928..ede22718d 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysLogininforServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysLogininforServiceImpl.java @@ -1,15 +1,24 @@ package com.ruoyi.system.service.impl; +import cn.hutool.http.useragent.UserAgent; +import cn.hutool.http.useragent.UserAgentUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.ruoyi.common.constant.Constants; import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl; import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.core.service.LogininforService; import com.ruoyi.common.utils.PageUtils; +import com.ruoyi.common.utils.ServletUtils; import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.ip.AddressUtils; import com.ruoyi.system.domain.SysLogininfor; import com.ruoyi.system.mapper.SysLogininforMapper; import com.ruoyi.system.service.ISysLogininforService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; +import javax.servlet.http.HttpServletRequest; import java.util.Arrays; import java.util.Date; import java.util.List; @@ -20,8 +29,62 @@ import java.util.Map; * * @author ruoyi */ +@Slf4j @Service -public class SysLogininforServiceImpl extends ServicePlusImpl implements ISysLogininforService { +public class SysLogininforServiceImpl extends ServicePlusImpl implements ISysLogininforService, LogininforService { + + /** + * 记录登录信息 + * + * @param username 用户名 + * @param status 状态 + * @param message 消息 + * @param args 列表 + */ + @Async + @Override + public void recordLogininfor(final String username, final String status, final String message, + HttpServletRequest request, final Object... args) { + final UserAgent userAgent = UserAgentUtil.parse(request.getHeader("User-Agent")); + final String ip = ServletUtils.getClientIP(request); + + String address = AddressUtils.getRealAddressByIP(ip); + StringBuilder s = new StringBuilder(); + s.append(getBlock(ip)); + s.append(address); + s.append(getBlock(username)); + s.append(getBlock(status)); + s.append(getBlock(message)); + // 打印信息到日志 + log.info(s.toString(), args); + // 获取客户端操作系统 + String os = userAgent.getOs().getName(); + // 获取客户端浏览器 + String browser = userAgent.getBrowser().getName(); + // 封装对象 + SysLogininfor logininfor = new SysLogininfor(); + logininfor.setUserName(username); + logininfor.setIpaddr(ip); + logininfor.setLoginLocation(address); + logininfor.setBrowser(browser); + logininfor.setOs(os); + logininfor.setMsg(message); + // 日志状态 + if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER)) { + logininfor.setStatus(Constants.SUCCESS); + } else if (Constants.LOGIN_FAIL.equals(status)) { + logininfor.setStatus(Constants.FAIL); + } + // 插入数据 + insertLogininfor(logininfor); + } + + private String getBlock(Object msg) { + if (msg == null) { + msg = ""; + } + return "[" + msg.toString() + "]"; + } @Override public TableDataInfo selectPageLogininforList(SysLogininfor logininfor) { diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java index a1a9422a2..a32c68055 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java @@ -135,6 +135,7 @@ public class SysMenuServiceImpl extends ServicePlusImpl cMenus = menu.getChildren(); if (!cMenus.isEmpty() && UserConstants.TYPE_DIR.equals(menu.getMenuType())) { diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOperLogServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOperLogServiceImpl.java index ff518bdbf..f4c39f4a4 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOperLogServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOperLogServiceImpl.java @@ -1,14 +1,19 @@ package com.ruoyi.system.service.impl; +import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.util.ArrayUtil; -import com.ruoyi.common.utils.StringUtils; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl; import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.core.service.OperLogService; +import com.ruoyi.common.core.domain.dto.OperLogDTO; import com.ruoyi.common.utils.PageUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.ip.AddressUtils; import com.ruoyi.system.domain.SysOperLog; import com.ruoyi.system.mapper.SysOperLogMapper; import com.ruoyi.system.service.ISysOperLogService; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import java.util.Arrays; @@ -22,7 +27,21 @@ import java.util.Map; * @author ruoyi */ @Service -public class SysOperLogServiceImpl extends ServicePlusImpl implements ISysOperLogService { +public class SysOperLogServiceImpl extends ServicePlusImpl implements ISysOperLogService, OperLogService { + + /** + * 操作日志记录 + * + * @param operLogDTO 操作日志信息 + */ + @Async + @Override + public void recordOper(final OperLogDTO operLogDTO) { + SysOperLog operLog = BeanUtil.toBean(operLogDTO, SysOperLog.class); + // 远程查询操作地点 + operLog.setOperLocation(AddressUtils.getRealAddressByIP(operLog.getOperIp())); + insertOperlog(operLog); + } @Override public TableDataInfo selectPageOperLogList(SysOperLog operLog) { diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOssConfigServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOssConfigServiceImpl.java index 0b11f838d..848f4d63f 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOssConfigServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOssConfigServiceImpl.java @@ -31,7 +31,7 @@ import java.util.Collection; import java.util.List; /** - * 云存储配置Service业务层处理 + * 对象存储配置Service业务层处理 * * @author Lion Li * @author 孤舟烟雨 diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TokenServiceImpl.java similarity index 93% rename from ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java rename to ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TokenServiceImpl.java index cb8878e86..f54130a9a 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TokenServiceImpl.java @@ -1,20 +1,21 @@ -package com.ruoyi.framework.web.service; +package com.ruoyi.system.service.impl; import cn.hutool.core.util.IdUtil; import cn.hutool.http.useragent.UserAgent; import cn.hutool.http.useragent.UserAgentUtil; import com.ruoyi.common.constant.Constants; import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.core.service.TokenService; +import com.ruoyi.common.properties.TokenProperties; import com.ruoyi.common.utils.RedisUtils; import com.ruoyi.common.utils.ServletUtils; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.ip.AddressUtils; -import com.ruoyi.framework.config.properties.TokenProperties; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; import javax.servlet.http.HttpServletRequest; import java.util.HashMap; @@ -26,8 +27,8 @@ import java.util.concurrent.TimeUnit; * * @author Lion Li */ -@Component -public class TokenService { +@Service +public class TokenServiceImpl implements TokenService { protected static final long MILLIS_SECOND = 1000; @@ -43,6 +44,7 @@ public class TokenService { * * @return 用户信息 */ + @Override public LoginUser getLoginUser(HttpServletRequest request) { // 获取请求携带的令牌 String token = getToken(request); @@ -64,6 +66,7 @@ public class TokenService { /** * 设置用户身份信息 */ + @Override public void setLoginUser(LoginUser loginUser) { if (StringUtils.isNotNull(loginUser) && StringUtils.isNotEmpty(loginUser.getToken())) { refreshToken(loginUser); @@ -73,6 +76,7 @@ public class TokenService { /** * 删除用户身份信息 */ + @Override public void delLoginUser(String token) { if (StringUtils.isNotEmpty(token)) { String userKey = getTokenKey(token); @@ -86,6 +90,7 @@ public class TokenService { * @param loginUser 用户信息 * @return 令牌 */ + @Override public String createToken(LoginUser loginUser) { String token = IdUtil.fastUUID(); loginUser.setToken(token); @@ -103,6 +108,7 @@ public class TokenService { * @param loginUser * @return 令牌 */ + @Override public void verifyToken(LoginUser loginUser) { long expireTime = loginUser.getExpireTime(); long currentTime = System.currentTimeMillis(); @@ -116,6 +122,7 @@ public class TokenService { * * @param loginUser 登录信息 */ + @Override public void refreshToken(LoginUser loginUser) { loginUser.setLoginTime(System.currentTimeMillis()); loginUser.setExpireTime(loginUser.getLoginTime() + tokenProperties.getExpireTime() * MILLIS_MINUTE); @@ -129,6 +136,7 @@ public class TokenService { * * @param loginUser 登录信息 */ + @Override public void setUserAgent(LoginUser loginUser) { UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent")); String ip = ServletUtils.getClientIP(); @@ -170,6 +178,7 @@ public class TokenService { * @param token 令牌 * @return 用户名 */ + @Override public String getUsernameFromToken(String token) { Claims claims = parseToken(token); return claims.getSubject(); diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/UserDetailsServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/UserDetailsServiceImpl.java similarity index 76% rename from ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/UserDetailsServiceImpl.java rename to ruoyi-system/src/main/java/com/ruoyi/system/service/impl/UserDetailsServiceImpl.java index c8b1c7b41..6b1c1e0f8 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/UserDetailsServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/UserDetailsServiceImpl.java @@ -1,28 +1,27 @@ -package com.ruoyi.framework.web.service; +package com.ruoyi.system.service.impl; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.core.userdetails.UsernameNotFoundException; -import org.springframework.stereotype.Service; import com.ruoyi.common.core.domain.entity.SysUser; import com.ruoyi.common.core.domain.model.LoginUser; import com.ruoyi.common.enums.UserStatus; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.system.service.ISysUserService; +import com.ruoyi.system.service.SysPermissionService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; /** * 用户验证处理 * * @author ruoyi */ +@Slf4j @Service -public class UserDetailsServiceImpl implements UserDetailsService -{ - private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class); +public class UserDetailsServiceImpl implements UserDetailsService { @Autowired private ISysUserService userService; @@ -31,21 +30,15 @@ public class UserDetailsServiceImpl implements UserDetailsService private SysPermissionService permissionService; @Override - public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException - { + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { SysUser user = userService.selectUserByUserName(username); - if (StringUtils.isNull(user)) - { + if (StringUtils.isNull(user)) { log.info("登录用户:{} 不存在.", username); throw new ServiceException("登录用户:" + username + " 不存在"); - } - else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) - { + } else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) { log.info("登录用户:{} 已被删除.", username); throw new ServiceException("对不起,您的账号:" + username + " 已被删除"); - } - else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) - { + } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { log.info("登录用户:{} 已被停用.", username); throw new ServiceException("对不起,您的账号:" + username + " 已停用"); } @@ -53,8 +46,7 @@ public class UserDetailsServiceImpl implements UserDetailsService return createLoginUser(user); } - public UserDetails createLoginUser(SysUser user) - { + public UserDetails createLoginUser(SysUser user) { return new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user)); } } diff --git a/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml index ca4ef6530..756eb53f7 100644 --- a/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml +++ b/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml @@ -12,7 +12,8 @@ - + + @@ -33,6 +34,7 @@ order_num, path, component, + query, is_frame, is_cache, menu_type, @@ -51,7 +53,8 @@ m.menu_name, m.path, m.component, - m.visible, + m.query, + m.visible, m.status, ifnull(m.perms, '') as perms, m.is_frame, @@ -67,7 +70,7 @@