commit
cf8613ab97
|
|
@ -2,7 +2,7 @@
|
|||
<configuration default="false" name="ruoyi-auth" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
|
||||
<deployment type="dockerfile">
|
||||
<settings>
|
||||
<option name="imageTag" value="ruoyi/ruoyi-auth:2.1.1" />
|
||||
<option name="imageTag" value="ruoyi/ruoyi-auth:2.1.2" />
|
||||
<option name="buildOnly" value="true" />
|
||||
<option name="sourceFilePath" value="ruoyi-auth/Dockerfile" />
|
||||
</settings>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<configuration default="false" name="ruoyi-gateway" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
|
||||
<deployment type="dockerfile">
|
||||
<settings>
|
||||
<option name="imageTag" value="ruoyi/ruoyi-gateway:2.1.1" />
|
||||
<option name="imageTag" value="ruoyi/ruoyi-gateway:2.1.2" />
|
||||
<option name="buildOnly" value="true" />
|
||||
<option name="sourceFilePath" value="ruoyi-gateway/Dockerfile" />
|
||||
</settings>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<configuration default="false" name="ruoyi-gen" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
|
||||
<deployment type="dockerfile">
|
||||
<settings>
|
||||
<option name="imageTag" value="ruoyi/ruoyi-gen:2.1.1" />
|
||||
<option name="imageTag" value="ruoyi/ruoyi-gen:2.1.2" />
|
||||
<option name="buildOnly" value="true" />
|
||||
<option name="sourceFilePath" value="ruoyi-modules/ruoyi-gen/Dockerfile" />
|
||||
</settings>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<configuration default="false" name="ruoyi-job" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
|
||||
<deployment type="dockerfile">
|
||||
<settings>
|
||||
<option name="imageTag" value="ruoyi/ruoyi-job:2.1.1" />
|
||||
<option name="imageTag" value="ruoyi/ruoyi-job:2.1.2" />
|
||||
<option name="buildOnly" value="true" />
|
||||
<option name="sourceFilePath" value="ruoyi-modules/ruoyi-job/Dockerfile" />
|
||||
</settings>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<configuration default="false" name="ruoyi-monitor" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
|
||||
<deployment type="dockerfile">
|
||||
<settings>
|
||||
<option name="imageTag" value="ruoyi/ruoyi-monitor:2.1.1" />
|
||||
<option name="imageTag" value="ruoyi/ruoyi-monitor:2.1.2" />
|
||||
<option name="buildOnly" value="true" />
|
||||
<option name="sourceFilePath" value="ruoyi-visual/ruoyi-monitor/Dockerfile" />
|
||||
</settings>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<configuration default="false" name="ruoyi-nacos" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
|
||||
<deployment type="dockerfile">
|
||||
<settings>
|
||||
<option name="imageTag" value="ruoyi/ruoyi-nacos:2.1.1" />
|
||||
<option name="imageTag" value="ruoyi/ruoyi-nacos:2.1.2" />
|
||||
<option name="buildOnly" value="true" />
|
||||
<option name="sourceFilePath" value="ruoyi-visual/ruoyi-nacos/Dockerfile" />
|
||||
</settings>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<configuration default="false" name="ruoyi-powerjob-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
|
||||
<deployment type="dockerfile">
|
||||
<settings>
|
||||
<option name="imageTag" value="ruoyi/ruoyi-powerjob-server:2.1.1" />
|
||||
<option name="imageTag" value="ruoyi/ruoyi-powerjob-server:2.1.2" />
|
||||
<option name="buildOnly" value="true" />
|
||||
<option name="sourceFilePath" value="ruoyi-visual/ruoyi-powerjob-server/Dockerfile" />
|
||||
</settings>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<configuration default="false" name="ruoyi-resource" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
|
||||
<deployment type="dockerfile">
|
||||
<settings>
|
||||
<option name="imageTag" value="ruoyi/ruoyi-resource:2.1.1" />
|
||||
<option name="imageTag" value="ruoyi/ruoyi-resource:2.1.2" />
|
||||
<option name="buildOnly" value="true" />
|
||||
<option name="sourceFilePath" value="ruoyi-modules/ruoyi-resource/Dockerfile" />
|
||||
</settings>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<configuration default="false" name="ruoyi-seata-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
|
||||
<deployment type="dockerfile">
|
||||
<settings>
|
||||
<option name="imageTag" value="ruoyi/ruoyi-seata-server:2.1.1" />
|
||||
<option name="imageTag" value="ruoyi/ruoyi-seata-server:2.1.2" />
|
||||
<option name="buildOnly" value="true" />
|
||||
<option name="sourceFilePath" value="ruoyi-visual/ruoyi-seata-server/Dockerfile" />
|
||||
</settings>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<configuration default="false" name="ruoyi-sentinel-dashboard" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
|
||||
<deployment type="dockerfile">
|
||||
<settings>
|
||||
<option name="imageTag" value="ruoyi/ruoyi-sentinel-dashboard:2.1.1" />
|
||||
<option name="imageTag" value="ruoyi/ruoyi-sentinel-dashboard:2.1.2" />
|
||||
<option name="buildOnly" value="true" />
|
||||
<option name="sourceFilePath" value="ruoyi-visual/ruoyi-sentinel-dashboard/Dockerfile" />
|
||||
</settings>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<configuration default="false" name="ruoyi-system" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
|
||||
<deployment type="dockerfile">
|
||||
<settings>
|
||||
<option name="imageTag" value="ruoyi/ruoyi-system:2.1.1" />
|
||||
<option name="imageTag" value="ruoyi/ruoyi-system:2.1.2" />
|
||||
<option name="buildOnly" value="true" />
|
||||
<option name="sourceFilePath" value="ruoyi-modules/ruoyi-system/Dockerfile" />
|
||||
</settings>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
[](https://gitee.com/dromara/RuoYi-Cloud-Plus/blob/master/LICENSE)
|
||||
[](https://www.jetbrains.com/?from=RuoYi-Cloud-Plus)
|
||||
<br>
|
||||
[](https://gitee.com/dromara/RuoYi-Cloud-Plus)
|
||||
[](https://gitee.com/dromara/RuoYi-Cloud-Plus)
|
||||
[]()
|
||||
[]()
|
||||
[]()
|
||||
|
|
@ -42,7 +42,7 @@
|
|||
| Web容器 | 采用 Undertow 基于 XNIO 的高性能容器 | 采用 Tomcat |
|
||||
| 权限认证 | 采用 Sa-Token、Jwt 静态使用功能齐全 低耦合 高扩展 | Spring Security 配置繁琐扩展性极差 |
|
||||
| 权限注解 | 采用 Sa-Token 支持注解 登录校验、角色校验、权限校验、二级认证校验、HttpBasic校验、忽略校验<br/>角色与权限校验支持多种条件 如 `AND` `OR` 或 `权限 OR 角色` 等复杂表达式 | 只支持是否存在匹配 |
|
||||
| 关系数据库支持 | 原生支持 MySQL、Oracle、PostgreSQL、SQLServer<br/>可同时使用异构切换 | 支持 Mysql、Oracle 不支持同时使用、不支持异构切换 |
|
||||
| 关系数据库支持 | 原生支持 MySQL、Oracle、PostgreSQL、SQLServer<br/>可同时使用异构切换(支持其他 mybatis-plus 支持的所有数据库 只需要增加jdbc依赖即可使用 达梦金仓等均有成功案例) | 支持 Mysql、Oracle 不支持同时使用、不支持异构切换 |
|
||||
| 缓存数据库 | 支持 Redis 5-7 支持大部分新功能特性 如 分布式限流、分布式队列 | Redis 简单 get set 支持 |
|
||||
| Redis客户端 | 采用 Redisson Redis官方推荐 基于Netty的客户端工具<br/>支持Redis 90%以上的命令 底层优化规避很多不正确的用法 例如: keys被转换为scan<br/>支持单机、哨兵、单主集群、多主集群等模式 | Lettuce + RedisTemplate 支持模式少 工具使用繁琐<br/>连接池采用 common-pool Bug多经常性出问题 |
|
||||
| 缓存注解 | 采用 Spring-Cache 注解 对其扩展了实现支持了更多功能<br/>例如 过期时间 最大空闲时间 组最大长度等 只需一个注解即可完成数据自动缓存 | 需手动编写Redis代码逻辑 |
|
||||
|
|
|
|||
|
|
@ -194,8 +194,11 @@ api-decrypt:
|
|||
enabled: true
|
||||
# AES 加密头标识
|
||||
headerFlag: encrypt-key
|
||||
# 公私钥 非对称算法的公私钥 如:SM2,RSA 使用者请自行更换
|
||||
publicKey: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==
|
||||
# 响应加密公钥 非对称算法的公私钥 如:SM2,RSA 使用者请自行更换
|
||||
# 对应前端解密私钥 MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAmc3CuPiGL/LcIIm7zryCEIbl1SPzBkr75E2VMtxegyZ1lYRD+7TZGAPkvIsBcaMs6Nsy0L78n2qh+lIZMpLH8wIDAQABAkEAk82Mhz0tlv6IVCyIcw/s3f0E+WLmtPFyR9/WtV3Y5aaejUkU60JpX4m5xNR2VaqOLTZAYjW8Wy0aXr3zYIhhQQIhAMfqR9oFdYw1J9SsNc+CrhugAvKTi0+BF6VoL6psWhvbAiEAxPPNTmrkmrXwdm/pQQu3UOQmc2vCZ5tiKpW10CgJi8kCIFGkL6utxw93Ncj4exE/gPLvKcT+1Emnoox+O9kRXss5AiAMtYLJDaLEzPrAWcZeeSgSIzbL+ecokmFKSDDcRske6QIgSMkHedwND1olF8vlKsJUGK3BcdtM8w4Xq7BpSBwsloE=
|
||||
publicKey: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJnNwrj4hi/y3CCJu868ghCG5dUj8wZK++RNlTLcXoMmdZWEQ/u02RgD5LyLAXGjLOjbMtC+/J9qofpSGTKSx/MCAwEAAQ==
|
||||
# 请求解密私钥 非对称算法的公私钥 如:SM2,RSA 使用者请自行更换
|
||||
# 对应前端加密公钥 MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==
|
||||
privateKey: MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKNPuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gAkM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWowcSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99EcvDQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthhYhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3UP8iWi1Qw0Y=
|
||||
|
||||
# 接口文档配置
|
||||
|
|
@ -249,3 +252,4 @@ tenant:
|
|||
- sys_user_post
|
||||
- sys_user_role
|
||||
- sys_client
|
||||
- sys_oss_config
|
||||
|
|
|
|||
|
|
@ -46,7 +46,5 @@ spring:
|
|||
idleTimeout: 600000
|
||||
# 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认30分钟
|
||||
maxLifetime: 1800000
|
||||
# 连接测试query(配置检测连接是否有效)
|
||||
connectionTestQuery: SELECT 1
|
||||
# 多久检查一次连接的活性
|
||||
keepaliveTime: 30000
|
||||
|
|
|
|||
|
|
@ -18,8 +18,6 @@ spring:
|
|||
# url: ${datasource.system-oracle.url}
|
||||
# username: ${datasource.system-oracle.username}
|
||||
# password: ${datasource.system-oracle.password}
|
||||
# hikari:
|
||||
# connectionTestQuery: SELECT 1 FROM DUAL
|
||||
# postgres:
|
||||
# type: ${spring.datasource.type}
|
||||
# driverClassName: org.postgresql.Driver
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ powerjob:
|
|||
enabled: true
|
||||
# 需要先在 powerjob 登录页执行应用注册后才能使用
|
||||
app-name: ruoyi-worker
|
||||
enable-test-mode: false
|
||||
allow-lazy-connect-server: false
|
||||
max-appended-wf-context-length: 4096
|
||||
max-result-length: 4096
|
||||
# 29203 端口 随着主应用端口飘逸 避免集群冲突
|
||||
|
|
|
|||
|
|
@ -17,8 +17,6 @@ spring:
|
|||
# url: ${datasource.system-oracle.url}
|
||||
# username: ${datasource.system-oracle.username}
|
||||
# password: ${datasource.system-oracle.password}
|
||||
# hikari:
|
||||
# connectionTestQuery: SELECT 1 FROM DUAL
|
||||
# postgres:
|
||||
# type: ${spring.datasource.type}
|
||||
# driverClassName: org.postgresql.Driver
|
||||
|
|
|
|||
|
|
@ -17,8 +17,6 @@ spring:
|
|||
# url: ${datasource.system-oracle.url}
|
||||
# username: ${datasource.system-oracle.username}
|
||||
# password: ${datasource.system-oracle.password}
|
||||
# hikari:
|
||||
# connectionTestQuery: SELECT 1 FROM DUAL
|
||||
# postgres:
|
||||
# type: ${spring.datasource.type}
|
||||
# driverClassName: org.postgresql.Driver
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ services:
|
|||
network_mode: "host"
|
||||
|
||||
nacos:
|
||||
image: ruoyi/ruoyi-nacos:2.1.1
|
||||
image: ruoyi/ruoyi-nacos:2.1.2
|
||||
container_name: nacos
|
||||
ports:
|
||||
- "8848:8848"
|
||||
|
|
@ -96,7 +96,7 @@ services:
|
|||
network_mode: "host"
|
||||
|
||||
seata-server:
|
||||
image: ruoyi/ruoyi-seata-server:2.1.1
|
||||
image: ruoyi/ruoyi-seata-server:2.1.2
|
||||
container_name: seata-server
|
||||
ports:
|
||||
- "7091:7091"
|
||||
|
|
@ -135,7 +135,7 @@ services:
|
|||
network_mode: "host"
|
||||
|
||||
sentinel:
|
||||
image: ruoyi/ruoyi-sentinel-dashboard:2.1.1
|
||||
image: ruoyi/ruoyi-sentinel-dashboard:2.1.2
|
||||
container_name: sentinel
|
||||
environment:
|
||||
TZ: Asia/Shanghai
|
||||
|
|
@ -150,7 +150,7 @@ services:
|
|||
network_mode: "host"
|
||||
|
||||
ruoyi-monitor:
|
||||
image: ruoyi/ruoyi-monitor:2.1.1
|
||||
image: ruoyi/ruoyi-monitor:2.1.2
|
||||
container_name: ruoyi-monitor
|
||||
environment:
|
||||
# 时区上海
|
||||
|
|
@ -166,7 +166,7 @@ services:
|
|||
network_mode: "host"
|
||||
|
||||
ruoyi-powerjob-server:
|
||||
image: ruoyi/ruoyi-powerjob-server:2.1.1
|
||||
image: ruoyi/ruoyi-powerjob-server:2.1.2
|
||||
container_name: ruoyi-powerjob-server
|
||||
environment:
|
||||
# 时区上海
|
||||
|
|
@ -181,7 +181,7 @@ services:
|
|||
network_mode: "host"
|
||||
|
||||
ruoyi-gateway:
|
||||
image: ruoyi/ruoyi-gateway:2.1.1
|
||||
image: ruoyi/ruoyi-gateway:2.1.2
|
||||
container_name: ruoyi-gateway
|
||||
environment:
|
||||
# 时区上海
|
||||
|
|
@ -197,7 +197,7 @@ services:
|
|||
network_mode: "host"
|
||||
|
||||
ruoyi-auth:
|
||||
image: ruoyi/ruoyi-auth:2.1.1
|
||||
image: ruoyi/ruoyi-auth:2.1.2
|
||||
container_name: ruoyi-auth
|
||||
environment:
|
||||
# 时区上海
|
||||
|
|
@ -213,7 +213,7 @@ services:
|
|||
network_mode: "host"
|
||||
|
||||
ruoyi-system:
|
||||
image: ruoyi/ruoyi-system:2.1.1
|
||||
image: ruoyi/ruoyi-system:2.1.2
|
||||
container_name: ruoyi-system
|
||||
environment:
|
||||
# 时区上海
|
||||
|
|
@ -229,7 +229,7 @@ services:
|
|||
network_mode: "host"
|
||||
|
||||
ruoyi-gen:
|
||||
image: ruoyi/ruoyi-gen:2.1.1
|
||||
image: ruoyi/ruoyi-gen:2.1.2
|
||||
container_name: ruoyi-gen
|
||||
environment:
|
||||
# 时区上海
|
||||
|
|
@ -245,7 +245,7 @@ services:
|
|||
network_mode: "host"
|
||||
|
||||
ruoyi-job:
|
||||
image: ruoyi/ruoyi-job:2.1.1
|
||||
image: ruoyi/ruoyi-job:2.1.2
|
||||
container_name: ruoyi-job
|
||||
environment:
|
||||
# 时区上海
|
||||
|
|
@ -261,7 +261,7 @@ services:
|
|||
network_mode: "host"
|
||||
|
||||
ruoyi-resource:
|
||||
image: ruoyi/ruoyi-resource:2.1.1
|
||||
image: ruoyi/ruoyi-resource:2.1.2
|
||||
container_name: ruoyi-resource
|
||||
environment:
|
||||
# 时区上海
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ http {
|
|||
}
|
||||
|
||||
# 解决 powerjob 代理之后静态文件无法访问的问题 请勿修改乱动
|
||||
location .*\.(js|css|jpg|png|svg|woff|ttf|ico)?$ {
|
||||
location ~ ^/(js|css|jpg|png|svg|woff|ttf|ico|img)/ {
|
||||
proxy_pass http://powerjob-server;
|
||||
}
|
||||
|
||||
|
|
|
|||
24
pom.xml
24
pom.xml
|
|
@ -13,14 +13,14 @@
|
|||
<description>RuoYi-Cloud-Plus微服务系统</description>
|
||||
|
||||
<properties>
|
||||
<revision>2.1.1</revision>
|
||||
<revision>2.1.2</revision>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<java.version>17</java.version>
|
||||
<spring-boot.version>3.1.5</spring-boot.version>
|
||||
<spring-boot.version>3.1.7</spring-boot.version>
|
||||
<spring-cloud.version>2022.0.4</spring-cloud.version>
|
||||
<spring-boot-admin.version>3.1.7</spring-boot-admin.version>
|
||||
<spring-boot.mybatis>3.0.2</spring-boot.mybatis>
|
||||
<spring-boot-admin.version>3.1.8</spring-boot-admin.version>
|
||||
<spring-boot.mybatis>3.0.3</spring-boot.mybatis>
|
||||
<mybatis.version>3.5.13</mybatis.version>
|
||||
<mybatis-plus.version>3.5.4</mybatis-plus.version>
|
||||
<p6spy.version>3.9.1</p6spy.version>
|
||||
|
|
@ -30,29 +30,29 @@
|
|||
<springdoc.version>2.2.0</springdoc.version>
|
||||
<therapi-javadoc.version>0.15.0</therapi-javadoc.version>
|
||||
<poi.version>5.2.3</poi.version>
|
||||
<easyexcel.version>3.3.2</easyexcel.version>
|
||||
<easyexcel.version>3.3.3</easyexcel.version>
|
||||
<hutool.version>5.8.22</hutool.version>
|
||||
<redisson.version>3.24.1</redisson.version>
|
||||
<redisson.version>3.24.3</redisson.version>
|
||||
<lock4j.version>2.2.5</lock4j.version>
|
||||
<powerjob.version>4.3.3</powerjob.version>
|
||||
<powerjob.version>4.3.6</powerjob.version>
|
||||
<satoken.version>1.37.0</satoken.version>
|
||||
<lombok.version>1.18.30</lombok.version>
|
||||
<logstash.version>7.4</logstash.version>
|
||||
<easy-es.version>1.1.1</easy-es.version>
|
||||
<easy-es.version>2.0.0-beta4</easy-es.version>
|
||||
<elasticsearch.version>7.14.0</elasticsearch.version>
|
||||
<skywalking-toolkit.version>8.16.0</skywalking-toolkit.version>
|
||||
<bouncycastle.version>1.76</bouncycastle.version>
|
||||
<alibaba-ttl.version>2.14.2</alibaba-ttl.version>
|
||||
<alibaba-ttl.version>2.14.4</alibaba-ttl.version>
|
||||
<mapstruct-plus.version>1.3.5</mapstruct-plus.version>
|
||||
<mapstruct-plus.lombok.version>0.2.0</mapstruct-plus.lombok.version>
|
||||
<justauth.version>1.16.5</justauth.version>
|
||||
<justauth.version>1.16.6</justauth.version>
|
||||
<!-- 离线IP地址定位库 -->
|
||||
<ip2region.version>2.7.0</ip2region.version>
|
||||
<!-- 临时修复 fastjson 漏洞 -->
|
||||
<fastjson.version>1.2.83</fastjson.version>
|
||||
|
||||
<!-- OSS 配置 -->
|
||||
<aws-java-sdk-s3.version>1.12.540</aws-java-sdk-s3.version>
|
||||
<aws-java-sdk-s3.version>1.12.600</aws-java-sdk-s3.version>
|
||||
<okhttp.version>4.10.0</okhttp.version>
|
||||
|
||||
<!-- SMS 配置 -->
|
||||
|
|
@ -293,7 +293,7 @@
|
|||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.easy-es</groupId>
|
||||
<groupId>org.dromara.easy-es</groupId>
|
||||
<artifactId>easy-es-boot-starter</artifactId>
|
||||
<version>${easy-es.version}</version>
|
||||
</dependency>
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
</description>
|
||||
|
||||
<properties>
|
||||
<revision>2.1.1</revision>
|
||||
<revision>2.1.2</revision>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
package org.dromara.resource.api;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.resource.api.domain.RemoteFile;
|
||||
|
||||
/**
|
||||
* 文件服务(降级处理)
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Slf4j
|
||||
public class RemoteFileServiceMock implements RemoteFileService {
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
*
|
||||
* @param file 文件信息
|
||||
* @return 结果
|
||||
*/
|
||||
public RemoteFile upload(String name, String originalFilename, String contentType, byte[] file) {
|
||||
log.warn("服务调用异常 -> 降级处理");
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过ossId查询对应的url
|
||||
*
|
||||
* @param ossIds ossId串逗号分隔
|
||||
* @return url串逗号分隔
|
||||
*/
|
||||
public String selectUrlByIds(String ossIds) {
|
||||
log.warn("服务调用异常 -> 降级处理");
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package org.dromara.resource.api;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 消息服务
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class RemoteMessageServiceStub implements RemoteMessageService {
|
||||
|
||||
private final RemoteMessageService remoteMessageService;
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
*
|
||||
* @param sessionKey session主键 一般为用户id
|
||||
* @param message 消息文本
|
||||
*/
|
||||
public void sendMessage(Long sessionKey, String message) {
|
||||
try {
|
||||
remoteMessageService.sendMessage(sessionKey, message);
|
||||
} catch (Exception e) {
|
||||
log.warn("websocket 功能未开启或服务未找到");
|
||||
}
|
||||
}
|
||||
|
||||
public void publishAll(String message) {
|
||||
try {
|
||||
remoteMessageService.publishAll(message);
|
||||
} catch (Exception e) {
|
||||
log.warn("websocket 功能未开启或服务未找到");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,8 @@ package org.dromara.system.api;
|
|||
import org.dromara.system.api.domain.bo.RemoteSocialBo;
|
||||
import org.dromara.system.api.domain.vo.RemoteSocialVo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 社会化关系服务
|
||||
*
|
||||
|
|
@ -13,7 +15,7 @@ public interface RemoteSocialService {
|
|||
/**
|
||||
* 根据 authId 查询用户信息
|
||||
*/
|
||||
RemoteSocialVo selectByAuthId(String authId);
|
||||
List<RemoteSocialVo> selectByAuthId(String authId);
|
||||
|
||||
/**
|
||||
* 保存社会化关系
|
||||
|
|
|
|||
|
|
@ -73,6 +73,14 @@ public interface RemoteUserService {
|
|||
*/
|
||||
String selectUserNameById(Long userId);
|
||||
|
||||
/**
|
||||
* 通过用户ID查询用户昵称
|
||||
*
|
||||
* @param userId 用户id
|
||||
* @return 结果
|
||||
*/
|
||||
String selectNicknameById(Long userId);
|
||||
|
||||
/**
|
||||
* 更新用户信息
|
||||
*
|
||||
|
|
|
|||
|
|
@ -9,15 +9,15 @@ RUN mkdir -p /ruoyi/auth/logs \
|
|||
|
||||
WORKDIR /ruoyi/auth
|
||||
|
||||
ENV SERVER_PORT=9210 LANG=C.UTF-8 LC_ALL=C.UTF-8
|
||||
ENV SERVER_PORT=9210 LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS=""
|
||||
|
||||
EXPOSE ${SERVER_PORT}
|
||||
|
||||
ADD ./target/ruoyi-auth.jar ./app.jar
|
||||
|
||||
ENTRYPOINT ["java", \
|
||||
"-Djava.security.egd=file:/dev/./urandom", \
|
||||
"-Dserver.port=${SERVER_PORT}", \
|
||||
# "-Dskywalking.agent.service_name=ruoyi-auth", \
|
||||
# "-javaagent:/ruoyi/skywalking/agent/skywalking-agent.jar", \
|
||||
"-jar", "app.jar"]
|
||||
ENTRYPOINT java -Djava.security.egd=file:/dev/./urandom -Dserver.port=${SERVER_PORT} \
|
||||
#-Dskywalking.agent.service_name=ruoyi-auth \
|
||||
#-javaagent:/ruoyi/skywalking/agent/skywalking-agent.jar \
|
||||
-jar app.jar \
|
||||
-XX:+HeapDumpOnOutOfMemoryError -Xlog:gc*,:time,tags,level -XX:+UseZGC ${JAVA_OPTS}
|
||||
|
||||
|
|
|
|||
|
|
@ -65,6 +65,11 @@
|
|||
<artifactId>ruoyi-common-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-ratelimiter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-encrypt</artifactId>
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ import org.dromara.common.core.domain.R;
|
|||
import org.dromara.common.core.utils.SpringUtils;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.core.utils.reflect.ReflectUtils;
|
||||
import org.dromara.common.ratelimiter.annotation.RateLimiter;
|
||||
import org.dromara.common.ratelimiter.enums.LimitType;
|
||||
import org.dromara.common.redis.utils.RedisUtils;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
|
|
@ -42,6 +44,7 @@ public class CaptchaController {
|
|||
/**
|
||||
* 生成验证码
|
||||
*/
|
||||
@RateLimiter(time = 60, count = 10, limitType = LimitType.IP)
|
||||
@GetMapping("/code")
|
||||
public R<CaptchaVo> getCode() {
|
||||
CaptchaVo captchaVo = new CaptchaVo();
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import org.dromara.common.core.constant.UserConstants;
|
|||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.core.domain.model.LoginBody;
|
||||
import org.dromara.common.core.utils.*;
|
||||
import org.dromara.common.encrypt.annotation.ApiEncrypt;
|
||||
import org.dromara.common.json.utils.JsonUtils;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.dromara.common.social.config.properties.SocialLoginConfigProperties;
|
||||
|
|
@ -34,7 +35,6 @@ import org.dromara.system.api.RemoteSocialService;
|
|||
import org.dromara.system.api.RemoteTenantService;
|
||||
import org.dromara.system.api.domain.vo.RemoteClientVo;
|
||||
import org.dromara.system.api.domain.vo.RemoteTenantVo;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.net.URL;
|
||||
|
|
@ -48,7 +48,6 @@ import java.util.concurrent.TimeUnit;
|
|||
* @author Lion Li
|
||||
*/
|
||||
@Slf4j
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
public class TokenController {
|
||||
|
|
@ -65,7 +64,7 @@ public class TokenController {
|
|||
private final RemoteClientService remoteClientService;
|
||||
@DubboReference
|
||||
private final RemoteSocialService remoteSocialService;
|
||||
@DubboReference
|
||||
@DubboReference(stub = "true")
|
||||
private final RemoteMessageService remoteMessageService;
|
||||
|
||||
/**
|
||||
|
|
@ -74,8 +73,9 @@ public class TokenController {
|
|||
* @param body 登录信息
|
||||
* @return 结果
|
||||
*/
|
||||
@ApiEncrypt
|
||||
@PostMapping("/login")
|
||||
public R<LoginVo> login(@Validated @RequestBody String body) {
|
||||
public R<LoginVo> login(@RequestBody String body) {
|
||||
LoginBody loginBody = JsonUtils.parseObject(body, LoginBody.class);
|
||||
ValidatorUtils.validate(loginBody);
|
||||
// 授权类型和客户端id
|
||||
|
|
@ -167,6 +167,7 @@ public class TokenController {
|
|||
/**
|
||||
* 用户注册
|
||||
*/
|
||||
@ApiEncrypt
|
||||
@PostMapping("register")
|
||||
public R<Void> register(@RequestBody RegisterBody registerBody) {
|
||||
if (!remoteConfigService.selectRegisterEnabled(registerBody.getTenantId())) {
|
||||
|
|
|
|||
|
|
@ -6,19 +6,26 @@ import cn.dev33.satoken.stp.SaLoginModel;
|
|||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.http.useragent.UserAgent;
|
||||
import cn.hutool.http.useragent.UserAgentUtil;
|
||||
import org.dromara.common.core.constant.CacheConstants;
|
||||
import org.dromara.common.core.enums.UserType;
|
||||
import org.dromara.common.core.utils.ServletUtils;
|
||||
import org.dromara.common.core.utils.ip.AddressUtils;
|
||||
import org.dromara.common.redis.utils.RedisUtils;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.dromara.system.api.domain.SysUserOnline;
|
||||
import org.dromara.system.api.model.LoginUser;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.dubbo.config.annotation.DubboReference;
|
||||
import org.dromara.common.core.constant.CacheConstants;
|
||||
import org.dromara.common.core.constant.Constants;
|
||||
import org.dromara.common.core.utils.MessageUtils;
|
||||
import org.dromara.common.core.utils.ServletUtils;
|
||||
import org.dromara.common.core.utils.SpringUtils;
|
||||
import org.dromara.common.core.utils.ip.AddressUtils;
|
||||
import org.dromara.common.log.event.LogininforEvent;
|
||||
import org.dromara.common.redis.utils.RedisUtils;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.dromara.resource.api.RemoteMessageService;
|
||||
import org.dromara.system.api.RemoteUserService;
|
||||
import org.dromara.system.api.domain.SysUserOnline;
|
||||
import org.dromara.system.api.model.LoginUser;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
/**
|
||||
* 用户行为 侦听器的实现
|
||||
|
|
@ -31,39 +38,49 @@ import java.time.Duration;
|
|||
public class UserActionListener implements SaTokenListener {
|
||||
|
||||
private final SaTokenConfig tokenConfig;
|
||||
private final ScheduledExecutorService scheduledExecutorService;
|
||||
@DubboReference
|
||||
private RemoteUserService remoteUserService;
|
||||
@DubboReference
|
||||
private RemoteMessageService remoteMessageService;
|
||||
|
||||
/**
|
||||
* 每次登录时触发
|
||||
*/
|
||||
@Override
|
||||
public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginModel loginModel) {
|
||||
UserType userType = UserType.getUserType(loginId.toString());
|
||||
if (userType == UserType.SYS_USER) {
|
||||
UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent"));
|
||||
String ip = ServletUtils.getClientIP();
|
||||
LoginUser user = LoginHelper.getLoginUser();
|
||||
SysUserOnline userOnline = new SysUserOnline();
|
||||
userOnline.setIpaddr(ip);
|
||||
userOnline.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
|
||||
userOnline.setBrowser(userAgent.getBrowser().getName());
|
||||
userOnline.setOs(userAgent.getOs().getName());
|
||||
userOnline.setLoginTime(System.currentTimeMillis());
|
||||
userOnline.setTokenId(tokenValue);
|
||||
userOnline.setUserName(user.getUsername());
|
||||
userOnline.setClientKey(user.getClientKey());
|
||||
userOnline.setDeviceType(user.getDeviceType());
|
||||
if (ObjectUtil.isNotNull(user.getDeptName())) {
|
||||
userOnline.setDeptName(user.getDeptName());
|
||||
}
|
||||
if(tokenConfig.getTimeout() == -1) {
|
||||
RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, userOnline);
|
||||
} else {
|
||||
RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, userOnline, Duration.ofSeconds(tokenConfig.getTimeout()));
|
||||
}
|
||||
log.info("user doLogin, useId:{}, token:{}", loginId, tokenValue);
|
||||
} else if (userType == UserType.APP_USER) {
|
||||
// app端 自行根据业务编写
|
||||
UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent"));
|
||||
String ip = ServletUtils.getClientIP();
|
||||
LoginUser user = LoginHelper.getLoginUser();
|
||||
SysUserOnline userOnline = new SysUserOnline();
|
||||
userOnline.setIpaddr(ip);
|
||||
userOnline.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
|
||||
userOnline.setBrowser(userAgent.getBrowser().getName());
|
||||
userOnline.setOs(userAgent.getOs().getName());
|
||||
userOnline.setLoginTime(System.currentTimeMillis());
|
||||
userOnline.setTokenId(tokenValue);
|
||||
userOnline.setUserName(user.getUsername());
|
||||
userOnline.setClientKey(user.getClientKey());
|
||||
userOnline.setDeviceType(user.getDeviceType());
|
||||
if (ObjectUtil.isNotNull(user.getDeptName())) {
|
||||
userOnline.setDeptName(user.getDeptName());
|
||||
}
|
||||
if (tokenConfig.getTimeout() == -1) {
|
||||
RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, userOnline);
|
||||
} else {
|
||||
RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, userOnline, Duration.ofSeconds(tokenConfig.getTimeout()));
|
||||
}
|
||||
// 记录登录日志
|
||||
LogininforEvent logininforEvent = new LogininforEvent();
|
||||
logininforEvent.setTenantId(user.getTenantId());
|
||||
logininforEvent.setUsername(user.getUsername());
|
||||
logininforEvent.setStatus(Constants.LOGIN_SUCCESS);
|
||||
logininforEvent.setMessage(MessageUtils.message("user.login.success"));
|
||||
logininforEvent.setRequest(ServletUtils.getRequest());
|
||||
SpringUtils.context().publishEvent(logininforEvent);
|
||||
// 更新登录信息
|
||||
remoteUserService.recordLoginInfo(user.getUserId(), ServletUtils.getClientIP());
|
||||
log.info("user doLogin, useId:{}, token:{}", loginId, tokenValue);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import cn.dev33.satoken.exception.NotLoginException;
|
|||
import cn.dev33.satoken.secure.BCrypt;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
|
@ -18,7 +19,7 @@ import org.dromara.common.core.constant.TenantConstants;
|
|||
import org.dromara.common.core.enums.LoginType;
|
||||
import org.dromara.common.core.enums.TenantStatus;
|
||||
import org.dromara.common.core.enums.UserType;
|
||||
import org.dromara.common.core.exception.CaptchaException;
|
||||
import org.dromara.common.core.exception.user.CaptchaException;
|
||||
import org.dromara.common.core.exception.user.CaptchaExpireException;
|
||||
import org.dromara.common.core.exception.user.UserException;
|
||||
import org.dromara.common.core.utils.MessageUtils;
|
||||
|
|
@ -43,6 +44,7 @@ import org.springframework.stereotype.Service;
|
|||
|
||||
import java.time.Duration;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
|
|
@ -83,13 +85,13 @@ public class SysLoginService {
|
|||
bo.setUserName(authUserData.getUsername());
|
||||
bo.setNickName(authUserData.getNickname());
|
||||
// 查询是否已经绑定用户
|
||||
RemoteSocialVo vo = remoteSocialService.selectByAuthId(authId);
|
||||
if (ObjectUtil.isEmpty(vo)) {
|
||||
List<RemoteSocialVo> list = remoteSocialService.selectByAuthId(authId);
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
// 没有绑定用户, 新增用户信息
|
||||
remoteSocialService.insertByBo(bo);
|
||||
} else {
|
||||
// 更新用户信息
|
||||
bo.setId(vo.getId());
|
||||
bo.setId(list.get(0).getId());
|
||||
remoteSocialService.updateByBo(bo);
|
||||
}
|
||||
}
|
||||
|
|
@ -100,6 +102,9 @@ public class SysLoginService {
|
|||
public void logout() {
|
||||
try {
|
||||
LoginUser loginUser = LoginHelper.getLoginUser();
|
||||
if (ObjectUtil.isNull(loginUser)) {
|
||||
return;
|
||||
}
|
||||
if (TenantHelper.isEnable() && LoginHelper.isSuperAdmin()) {
|
||||
// 超级管理员 登出清除动态租户
|
||||
TenantHelper.clearDynamic();
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import org.dromara.common.core.constant.GlobalConstants;
|
|||
import org.dromara.common.core.enums.LoginType;
|
||||
import org.dromara.common.core.exception.user.CaptchaExpireException;
|
||||
import org.dromara.common.core.utils.MessageUtils;
|
||||
import org.dromara.common.core.utils.ServletUtils;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.core.utils.ValidatorUtils;
|
||||
import org.dromara.common.json.utils.JsonUtils;
|
||||
|
|
@ -63,9 +62,6 @@ public class EmailAuthStrategy implements IAuthStrategy {
|
|||
// 生成token
|
||||
LoginHelper.login(loginUser, model);
|
||||
|
||||
loginService.recordLogininfor(loginUser.getTenantId(), loginUser.getUsername(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"));
|
||||
remoteUserService.recordLoginInfo(loginUser.getUserId(), ServletUtils.getClientIP());
|
||||
|
||||
LoginVo loginVo = new LoginVo();
|
||||
loginVo.setAccessToken(StpUtil.getTokenValue());
|
||||
loginVo.setExpireIn(StpUtil.getTokenTimeout());
|
||||
|
|
|
|||
|
|
@ -14,10 +14,9 @@ import org.dromara.auth.service.SysLoginService;
|
|||
import org.dromara.common.core.constant.Constants;
|
||||
import org.dromara.common.core.constant.GlobalConstants;
|
||||
import org.dromara.common.core.enums.LoginType;
|
||||
import org.dromara.common.core.exception.CaptchaException;
|
||||
import org.dromara.common.core.exception.user.CaptchaException;
|
||||
import org.dromara.common.core.exception.user.CaptchaExpireException;
|
||||
import org.dromara.common.core.utils.MessageUtils;
|
||||
import org.dromara.common.core.utils.ServletUtils;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.core.utils.ValidatorUtils;
|
||||
import org.dromara.common.json.utils.JsonUtils;
|
||||
|
|
@ -74,9 +73,6 @@ public class PasswordAuthStrategy implements IAuthStrategy {
|
|||
// 生成token
|
||||
LoginHelper.login(loginUser, model);
|
||||
|
||||
loginService.recordLogininfor(loginUser.getTenantId(), username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"));
|
||||
remoteUserService.recordLoginInfo(loginUser.getUserId(), ServletUtils.getClientIP());
|
||||
|
||||
LoginVo loginVo = new LoginVo();
|
||||
loginVo.setAccessToken(StpUtil.getTokenValue());
|
||||
loginVo.setExpireIn(StpUtil.getTokenTimeout());
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import org.dromara.common.core.constant.GlobalConstants;
|
|||
import org.dromara.common.core.enums.LoginType;
|
||||
import org.dromara.common.core.exception.user.CaptchaExpireException;
|
||||
import org.dromara.common.core.utils.MessageUtils;
|
||||
import org.dromara.common.core.utils.ServletUtils;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.core.utils.ValidatorUtils;
|
||||
import org.dromara.common.json.utils.JsonUtils;
|
||||
|
|
@ -63,9 +62,6 @@ public class SmsAuthStrategy implements IAuthStrategy {
|
|||
// 生成token
|
||||
LoginHelper.login(loginUser, model);
|
||||
|
||||
loginService.recordLogininfor(loginUser.getTenantId(), loginUser.getUsername(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"));
|
||||
remoteUserService.recordLoginInfo(loginUser.getUserId(), ServletUtils.getClientIP());
|
||||
|
||||
LoginVo loginVo = new LoginVo();
|
||||
loginVo.setAccessToken(StpUtil.getTokenValue());
|
||||
loginVo.setExpireIn(StpUtil.getTokenTimeout());
|
||||
|
|
|
|||
|
|
@ -2,9 +2,8 @@ package org.dromara.auth.service.impl;
|
|||
|
||||
import cn.dev33.satoken.stp.SaLoginModel;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import cn.hutool.http.Method;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
|
@ -16,10 +15,7 @@ import org.dromara.auth.domain.vo.LoginVo;
|
|||
import org.dromara.auth.form.SocialLoginBody;
|
||||
import org.dromara.auth.service.IAuthStrategy;
|
||||
import org.dromara.auth.service.SysLoginService;
|
||||
import org.dromara.common.core.constant.Constants;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.core.utils.MessageUtils;
|
||||
import org.dromara.common.core.utils.ServletUtils;
|
||||
import org.dromara.common.core.utils.ValidatorUtils;
|
||||
import org.dromara.common.json.utils.JsonUtils;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
|
|
@ -32,6 +28,9 @@ import org.dromara.system.api.domain.vo.RemoteSocialVo;
|
|||
import org.dromara.system.api.model.LoginUser;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 第三方授权策略
|
||||
*
|
||||
|
|
@ -53,7 +52,6 @@ public class SocialAuthStrategy implements IAuthStrategy {
|
|||
/**
|
||||
* 登录-第三方授权登录
|
||||
*
|
||||
* @param clientId 客户端id
|
||||
* @param body 登录信息
|
||||
* @param client 客户端信息
|
||||
*/
|
||||
|
|
@ -78,18 +76,17 @@ public class SocialAuthStrategy implements IAuthStrategy {
|
|||
.executeAsync();
|
||||
}
|
||||
|
||||
RemoteSocialVo socialVo = remoteSocialService.selectByAuthId(authUserData.getSource() + authUserData.getUuid());
|
||||
if (!ObjectUtil.isNotNull(socialVo)) {
|
||||
List<RemoteSocialVo> list = remoteSocialService.selectByAuthId(authUserData.getSource() + authUserData.getUuid());
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
throw new ServiceException("你还没有绑定第三方账号,绑定后才可以登录!");
|
||||
}
|
||||
// 验证授权表里面的租户id是否包含当前租户id
|
||||
String tenantId = socialVo.getTenantId();
|
||||
if (ObjectUtil.isNotNull(socialVo) && StrUtil.isNotBlank(tenantId)
|
||||
&& !tenantId.contains(loginBody.getTenantId())) {
|
||||
Optional<RemoteSocialVo> opt = list.stream().filter(x -> x.getTenantId().equals(loginBody.getTenantId())).findAny();
|
||||
if (opt.isEmpty()) {
|
||||
throw new ServiceException("对不起,你没有权限登录当前租户!");
|
||||
}
|
||||
RemoteSocialVo socialVo = opt.get();
|
||||
|
||||
LoginUser loginUser = remoteUserService.getUserInfo(socialVo.getUserId(), tenantId);
|
||||
LoginUser loginUser = remoteUserService.getUserInfo(socialVo.getUserId(), socialVo.getTenantId());
|
||||
loginUser.setClientKey(client.getClientKey());
|
||||
loginUser.setDeviceType(client.getDeviceType());
|
||||
SaLoginModel model = new SaLoginModel();
|
||||
|
|
@ -102,9 +99,6 @@ public class SocialAuthStrategy implements IAuthStrategy {
|
|||
// 生成token
|
||||
LoginHelper.login(loginUser, model);
|
||||
|
||||
loginService.recordLogininfor(loginUser.getTenantId(), socialVo.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"));
|
||||
remoteUserService.recordLoginInfo(loginUser.getUserId(), ServletUtils.getClientIP());
|
||||
|
||||
LoginVo loginVo = new LoginVo();
|
||||
loginVo.setAccessToken(StpUtil.getTokenValue());
|
||||
loginVo.setExpireIn(StpUtil.getTokenTimeout());
|
||||
|
|
|
|||
|
|
@ -9,9 +9,6 @@ import org.dromara.auth.domain.vo.LoginVo;
|
|||
import org.dromara.auth.form.XcxLoginBody;
|
||||
import org.dromara.auth.service.IAuthStrategy;
|
||||
import org.dromara.auth.service.SysLoginService;
|
||||
import org.dromara.common.core.constant.Constants;
|
||||
import org.dromara.common.core.utils.MessageUtils;
|
||||
import org.dromara.common.core.utils.ServletUtils;
|
||||
import org.dromara.common.core.utils.ValidatorUtils;
|
||||
import org.dromara.common.json.utils.JsonUtils;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
|
|
@ -61,9 +58,6 @@ public class XcxAuthStrategy implements IAuthStrategy {
|
|||
// 生成token
|
||||
LoginHelper.login(loginUser, model);
|
||||
|
||||
loginService.recordLogininfor(loginUser.getTenantId(), loginUser.getUsername(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"));
|
||||
remoteUserService.recordLoginInfo(loginUser.getUserId(), ServletUtils.getClientIP());
|
||||
|
||||
LoginVo loginVo = new LoginVo();
|
||||
loginVo.setAccessToken(StpUtil.getTokenValue());
|
||||
loginVo.setExpireIn(StpUtil.getTokenTimeout());
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
<module>ruoyi-common-seata</module>
|
||||
<module>ruoyi-common-loadbalancer</module>
|
||||
<module>ruoyi-common-oss</module>
|
||||
<module>ruoyi-common-ratelimiter</module>
|
||||
<module>ruoyi-common-idempotent</module>
|
||||
<module>ruoyi-common-mail</module>
|
||||
<module>ruoyi-common-sms</module>
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
</description>
|
||||
|
||||
<properties>
|
||||
<revision>2.1.1</revision>
|
||||
<revision>2.1.2</revision>
|
||||
<spring-cloud-alibaba.version>2022.0.0.0</spring-cloud-alibaba.version>
|
||||
<sentinel.version>1.8.6</sentinel.version>
|
||||
<seata.version>1.7.1</seata.version>
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
</description>
|
||||
|
||||
<properties>
|
||||
<revision>2.1.1</revision>
|
||||
<revision>2.1.2</revision>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
|
|
@ -118,6 +118,13 @@
|
|||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 限流 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-ratelimiter</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-idempotent</artifactId>
|
||||
|
|
|
|||
|
|
@ -28,16 +28,6 @@
|
|||
<artifactId>spring-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-annotation</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mybatis</groupId>
|
||||
<artifactId>mybatis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Hibernate Validator -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
|
@ -81,12 +71,6 @@
|
|||
<artifactId>hutool-extra</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-json</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
|
|
|
|||
|
|
@ -40,6 +40,11 @@ public interface CacheNames {
|
|||
*/
|
||||
String SYS_USER_NAME = "sys_user_name#30d";
|
||||
|
||||
/**
|
||||
* 用户名称
|
||||
*/
|
||||
String SYS_NICKNAME = "sys_nickname#30d";
|
||||
|
||||
/**
|
||||
* 部门
|
||||
*/
|
||||
|
|
@ -53,7 +58,7 @@ public interface CacheNames {
|
|||
/**
|
||||
* OSS配置
|
||||
*/
|
||||
String SYS_OSS_CONFIG = "sys_oss_config";
|
||||
String SYS_OSS_CONFIG = GlobalConstants.GLOBAL_REDIS_KEY + "sys_oss_config";
|
||||
|
||||
/**
|
||||
* 在线用户
|
||||
|
|
|
|||
|
|
@ -1,29 +0,0 @@
|
|||
package org.dromara.common.core.exception;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* 检查异常
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class CheckedException extends RuntimeException {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public CheckedException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public CheckedException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public CheckedException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public CheckedException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
package org.dromara.common.core.exception;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* 演示模式异常
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class DemoModeException extends RuntimeException {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public DemoModeException() {
|
||||
}
|
||||
}
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
package org.dromara.common.core.exception;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* 全局异常
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class GlobalException extends RuntimeException {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 错误提示
|
||||
*/
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* 错误明细,内部调试错误
|
||||
*/
|
||||
private String detailMessage;
|
||||
|
||||
public GlobalException(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getDetailMessage() {
|
||||
return detailMessage;
|
||||
}
|
||||
|
||||
public GlobalException setDetailMessage(String detailMessage) {
|
||||
this.detailMessage = detailMessage;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public GlobalException setMessage(String message) {
|
||||
this.message = message;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
package org.dromara.common.core.exception;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* 工具类异常
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class UtilException extends RuntimeException {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 8247610319171014183L;
|
||||
|
||||
public UtilException(Throwable e) {
|
||||
super(e.getMessage(), e);
|
||||
}
|
||||
|
||||
public UtilException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public UtilException(String message, Throwable throwable) {
|
||||
super(message, throwable);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,4 @@
|
|||
package org.dromara.common.core.exception;
|
||||
|
||||
import org.dromara.common.core.exception.user.UserException;
|
||||
package org.dromara.common.core.exception.user;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
package org.dromara.common.core.exception.user;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* 用户密码不正确或不符合规范异常类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class UserPasswordNotMatchException extends UserException {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public UserPasswordNotMatchException() {
|
||||
super("user.password.not.match");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,8 @@
|
|||
package org.dromara.common.core.utils.sql;
|
||||
|
||||
import org.dromara.common.core.exception.UtilException;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
|
||||
/**
|
||||
* sql操作工具类
|
||||
|
|
@ -28,7 +27,7 @@ public class SqlUtil {
|
|||
*/
|
||||
public static String escapeOrderBySql(String value) {
|
||||
if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value)) {
|
||||
throw new UtilException("参数不符合规范,不能进行查询");
|
||||
throw new IllegalArgumentException("参数不符合规范,不能进行查询");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
|
@ -50,7 +49,7 @@ public class SqlUtil {
|
|||
String[] sqlKeywords = StringUtils.split(SQL_REGEX, "\\|");
|
||||
for (String sqlKeyword : sqlKeywords) {
|
||||
if (StringUtils.indexOfIgnoreCase(value, sqlKeyword) > -1) {
|
||||
throw new UtilException("参数存在SQL注入风险");
|
||||
throw new IllegalArgumentException("参数存在SQL注入风险");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,7 +50,10 @@ sms.code.retry.limit.exceed=短信验证码输入错误{0}次,帐户锁定{1}
|
|||
email.code.not.blank=邮箱验证码不能为空
|
||||
email.code.retry.limit.count=邮箱验证码输入错误{0}次
|
||||
email.code.retry.limit.exceed=邮箱验证码输入错误{0}次,帐户锁定{1}分钟
|
||||
xcx.code.not.blank=小程序code不能为空
|
||||
xcx.code.not.blank=小程序[code]不能为空
|
||||
social.source.not.blank=第三方登录平台[source]不能为空
|
||||
social.code.not.blank=第三方登录平台[code]不能为空
|
||||
social.state.not.blank=第三方登录平台[state]不能为空
|
||||
##租户
|
||||
tenant.number.not.blank=租户编号不能为空
|
||||
tenant.not.exists=对不起, 您的租户不存在,请联系管理员
|
||||
|
|
|
|||
|
|
@ -50,7 +50,10 @@ sms.code.retry.limit.exceed=Sms code input error {0} times, account locked for {
|
|||
email.code.not.blank=Email code cannot be blank
|
||||
email.code.retry.limit.count=Email code input error {0} times
|
||||
email.code.retry.limit.exceed=Email code input error {0} times, account locked for {1} minutes
|
||||
xcx.code.not.blank=Mini program code cannot be blank
|
||||
xcx.code.not.blank=Mini program [code] cannot be blank
|
||||
social.source.not.blank=Social login platform [source] cannot be blank
|
||||
social.code.not.blank=Social login platform [code] cannot be blank
|
||||
social.state.not.blank=Social login platform [state] cannot be blank
|
||||
##租户
|
||||
tenant.number.not.blank=Tenant number cannot be blank
|
||||
tenant.not.exists=Sorry, your tenant does not exist. Please contact the administrator
|
||||
|
|
|
|||
|
|
@ -50,7 +50,10 @@ sms.code.retry.limit.exceed=短信验证码输入错误{0}次,帐户锁定{1}
|
|||
email.code.not.blank=邮箱验证码不能为空
|
||||
email.code.retry.limit.count=邮箱验证码输入错误{0}次
|
||||
email.code.retry.limit.exceed=邮箱验证码输入错误{0}次,帐户锁定{1}分钟
|
||||
xcx.code.not.blank=小程序code不能为空
|
||||
xcx.code.not.blank=小程序[code]不能为空
|
||||
social.source.not.blank=第三方登录平台[source]不能为空
|
||||
social.code.not.blank=第三方登录平台[code]不能为空
|
||||
social.state.not.blank=第三方登录平台[state]不能为空
|
||||
##租户
|
||||
tenant.number.not.blank=租户编号不能为空
|
||||
tenant.not.exists=对不起, 您的租户不存在,请联系管理员
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.easy-es</groupId>
|
||||
<groupId>org.dromara.easy-es</groupId>
|
||||
<artifactId>easy-es-boot-starter</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package org.dromara.common.elasticsearch.config;
|
||||
|
||||
import cn.easyes.starter.register.EsMapperScan;
|
||||
import org.dromara.easyes.starter.register.EsMapperScan;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,11 @@
|
|||
<artifactId>hutool-crypto</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-webmvc</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
package org.dromara.common.encrypt.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 强制加密注解
|
||||
*
|
||||
* @author Michelle.Chung
|
||||
*/
|
||||
@Documented
|
||||
@Target({ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ApiEncrypt {
|
||||
|
||||
/**
|
||||
* 响应加密忽略,默认不加密,为 true 时加密
|
||||
*/
|
||||
boolean response() default false;
|
||||
|
||||
}
|
||||
|
|
@ -3,10 +3,19 @@ package org.dromara.common.encrypt.filter;
|
|||
import cn.hutool.core.util.ObjectUtil;
|
||||
import jakarta.servlet.*;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.dromara.common.core.constant.HttpStatus;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.core.utils.SpringUtils;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.encrypt.annotation.ApiEncrypt;
|
||||
import org.dromara.common.encrypt.properties.ApiDecryptProperties;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.servlet.HandlerExceptionResolver;
|
||||
import org.springframework.web.servlet.HandlerExecutionChain;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
|
@ -25,8 +34,14 @@ public class CryptoFilter implements Filter {
|
|||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||
ServletRequest requestWrapper = null;
|
||||
HttpServletRequest servletRequest = (HttpServletRequest) request;
|
||||
HttpServletResponse servletResponse = (HttpServletResponse) response;
|
||||
|
||||
boolean encryptFlag = false;
|
||||
ServletRequest requestWrapper = null;
|
||||
ServletResponse responseWrapper = null;
|
||||
EncryptResponseBodyWrapper responseBodyWrapper = null;
|
||||
|
||||
// 是否为 json 请求
|
||||
if (StringUtils.startsWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE)) {
|
||||
// 是否为 put 或者 post 请求
|
||||
|
|
@ -34,16 +49,67 @@ public class CryptoFilter implements Filter {
|
|||
// 是否存在加密标头
|
||||
String headerValue = servletRequest.getHeader(properties.getHeaderFlag());
|
||||
if (StringUtils.isNotBlank(headerValue)) {
|
||||
requestWrapper = new DecryptRequestBodyWrapper(servletRequest, properties.getPublicKey(), properties.getPrivateKey(), properties.getHeaderFlag());
|
||||
// 请求解密
|
||||
requestWrapper = new DecryptRequestBodyWrapper(servletRequest, properties.getPrivateKey(), properties.getHeaderFlag());
|
||||
// 获取加密注解
|
||||
ApiEncrypt apiEncrypt = this.getApiEncryptAnnotation(servletRequest);
|
||||
if (ObjectUtil.isNotNull(apiEncrypt)) {
|
||||
// 响应加密标志
|
||||
encryptFlag = apiEncrypt.response();
|
||||
} else {
|
||||
// 是否有注解,有就报错,没有放行
|
||||
HandlerExceptionResolver exceptionResolver = SpringUtils.getBean(HandlerExceptionResolver.class);
|
||||
exceptionResolver.resolveException(
|
||||
servletRequest, servletResponse, null,
|
||||
new ServiceException("没有访问权限,请联系管理员授权", HttpStatus.FORBIDDEN));
|
||||
}
|
||||
}
|
||||
// 判断是否响应加密
|
||||
if (encryptFlag) {
|
||||
responseBodyWrapper = new EncryptResponseBodyWrapper(servletResponse);
|
||||
responseWrapper = responseBodyWrapper;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
chain.doFilter(ObjectUtil.defaultIfNull(requestWrapper, request), response);
|
||||
chain.doFilter(
|
||||
ObjectUtil.defaultIfNull(requestWrapper, request),
|
||||
ObjectUtil.defaultIfNull(responseWrapper, response));
|
||||
|
||||
if (encryptFlag) {
|
||||
servletResponse.reset();
|
||||
// 对原始内容加密
|
||||
String encryptContent = responseBodyWrapper.getEncryptContent(
|
||||
servletResponse, properties.getPublicKey(), properties.getHeaderFlag());
|
||||
// 对加密后的内容写出
|
||||
servletResponse.getWriter().write(encryptContent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 ApiEncrypt 注解
|
||||
*/
|
||||
private ApiEncrypt getApiEncryptAnnotation(HttpServletRequest servletRequest) {
|
||||
RequestMappingHandlerMapping handlerMapping = SpringUtils.getBean("requestMappingHandlerMapping", RequestMappingHandlerMapping.class);
|
||||
// 获取注解
|
||||
try {
|
||||
HandlerExecutionChain mappingHandler = handlerMapping.getHandler(servletRequest);
|
||||
if (ObjectUtil.isNotNull(mappingHandler)) {
|
||||
Object handler = mappingHandler.getHandler();
|
||||
if (ObjectUtil.isNotNull(handler)) {
|
||||
// 从handler获取注解
|
||||
if (handler instanceof HandlerMethod handlerMethod) {
|
||||
return handlerMethod.getMethodAnnotation(ApiEncrypt.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ public class DecryptRequestBodyWrapper extends HttpServletRequestWrapper {
|
|||
|
||||
private final byte[] body;
|
||||
|
||||
public DecryptRequestBodyWrapper(HttpServletRequest request, String publicKey, String privateKey, String headerFlag) throws IOException {
|
||||
public DecryptRequestBodyWrapper(HttpServletRequest request, String privateKey, String headerFlag) throws IOException {
|
||||
super(request);
|
||||
// 获取 AES 密码 采用 RSA 加密
|
||||
String headerRsa = request.getHeader(headerFlag);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,123 @@
|
|||
package org.dromara.common.encrypt.filter;
|
||||
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import jakarta.servlet.ServletOutputStream;
|
||||
import jakarta.servlet.WriteListener;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpServletResponseWrapper;
|
||||
import org.dromara.common.encrypt.utils.EncryptUtils;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* 加密响应参数包装类
|
||||
*
|
||||
* @author Michelle.Chung
|
||||
*/
|
||||
public class EncryptResponseBodyWrapper extends HttpServletResponseWrapper {
|
||||
|
||||
private final ByteArrayOutputStream byteArrayOutputStream;
|
||||
private final ServletOutputStream servletOutputStream;
|
||||
private final PrintWriter printWriter;
|
||||
|
||||
public EncryptResponseBodyWrapper(HttpServletResponse response) throws IOException {
|
||||
super(response);
|
||||
this.byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
this.servletOutputStream = this.getOutputStream();
|
||||
this.printWriter = new PrintWriter(new OutputStreamWriter(byteArrayOutputStream));
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrintWriter getWriter() {
|
||||
return printWriter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flushBuffer() throws IOException {
|
||||
if (servletOutputStream != null) {
|
||||
servletOutputStream.flush();
|
||||
}
|
||||
if (printWriter != null) {
|
||||
printWriter.flush();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
byteArrayOutputStream.reset();
|
||||
}
|
||||
|
||||
public byte[] getResponseData() throws IOException {
|
||||
flushBuffer();
|
||||
return byteArrayOutputStream.toByteArray();
|
||||
}
|
||||
|
||||
public String getContent() throws IOException {
|
||||
flushBuffer();
|
||||
return byteArrayOutputStream.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取加密内容
|
||||
*
|
||||
* @param servletResponse response
|
||||
* @param publicKey RSA公钥 (用于加密 AES 秘钥)
|
||||
* @param headerFlag 请求头标志
|
||||
* @return 加密内容
|
||||
* @throws IOException
|
||||
*/
|
||||
public String getEncryptContent(HttpServletResponse servletResponse, String publicKey, String headerFlag) throws IOException {
|
||||
// 生成秘钥
|
||||
String aesPassword = RandomUtil.randomString(32);
|
||||
// 秘钥使用 Base64 编码
|
||||
String encryptAes = EncryptUtils.encryptByBase64(aesPassword);
|
||||
// Rsa 公钥加密 Base64 编码
|
||||
String encryptPassword = EncryptUtils.encryptByRsa(encryptAes, publicKey);
|
||||
|
||||
// 设置响应头
|
||||
servletResponse.setHeader(headerFlag, encryptPassword);
|
||||
servletResponse.setHeader("Access-Control-Allow-Origin", "*");
|
||||
servletResponse.setHeader("Access-Control-Allow-Methods", "*");
|
||||
servletResponse.setCharacterEncoding(StandardCharsets.UTF_8.toString());
|
||||
|
||||
// 获取原始内容
|
||||
String originalBody = this.getContent();
|
||||
// 对内容进行加密
|
||||
return EncryptUtils.encryptByAes(originalBody, aesPassword);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServletOutputStream getOutputStream() throws IOException {
|
||||
return new ServletOutputStream() {
|
||||
@Override
|
||||
public boolean isReady() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWriteListener(WriteListener writeListener) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
byteArrayOutputStream.write(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] b) throws IOException {
|
||||
byteArrayOutputStream.write(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] b, int off, int len) throws IOException {
|
||||
byteArrayOutputStream.write(b, off, len);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -21,14 +21,14 @@ public class ApiDecryptProperties {
|
|||
*/
|
||||
private String headerFlag;
|
||||
|
||||
|
||||
/**
|
||||
* 公钥
|
||||
* 响应加密公钥
|
||||
*/
|
||||
private String publicKey;
|
||||
|
||||
/**
|
||||
* 私钥
|
||||
* 请求解密私钥
|
||||
*/
|
||||
private String privateKey;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -98,9 +98,30 @@ public class CellMergeStrategy extends AbstractMergeStrategy {
|
|||
cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex - 1, colNum, colNum));
|
||||
}
|
||||
map.put(field, new RepeatCell(val, i));
|
||||
} else if (i == list.size() - 1) {
|
||||
if (i > repeatCell.getCurrent()) {
|
||||
cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex, colNum, colNum));
|
||||
} else if (j == 0) {
|
||||
if (i == list.size() - 1) {
|
||||
if (i > repeatCell.getCurrent()) {
|
||||
cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex, colNum, colNum));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 判断前面的是否合并了
|
||||
RepeatCell firstCell = map.get(mergeFields.get(0));
|
||||
if (repeatCell.getCurrent() != firstCell.getCurrent()) {
|
||||
if (i == list.size() - 1) {
|
||||
if (i > repeatCell.getCurrent()) {
|
||||
cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex, colNum, colNum));
|
||||
}
|
||||
} else if (repeatCell.getCurrent() < firstCell.getCurrent()) {
|
||||
if (i - repeatCell.getCurrent() > 1) {
|
||||
cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex - 1, colNum, colNum));
|
||||
}
|
||||
map.put(field, new RepeatCell(val, i));
|
||||
}
|
||||
} else if (i == list.size() - 1) {
|
||||
if (i > repeatCell.getCurrent()) {
|
||||
cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex, colNum, colNum));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -269,6 +269,26 @@ public class ExcelUtil {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 多sheet模板导出 模板格式为 {key.属性}
|
||||
*
|
||||
* @param filename 文件名
|
||||
* @param templatePath 模板路径 resource 目录下的路径包括模板文件名
|
||||
* 例如: excel/temp.xlsx
|
||||
* 重点: 模板文件必须放置到启动类对应的 resource 目录下
|
||||
* @param data 模板需要的数据
|
||||
* @param response 响应体
|
||||
*/
|
||||
public static void exportTemplateMultiSheet(List<Map<String, Object>> data, String filename, String templatePath, HttpServletResponse response) {
|
||||
try {
|
||||
resetResponse(filename, response);
|
||||
ServletOutputStream os = response.getOutputStream();
|
||||
exportTemplateMultiSheet(data, templatePath, os);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("导出Excel异常");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 多表多数据模板导出 模板格式为 {key.属性}
|
||||
*
|
||||
|
|
@ -303,6 +323,42 @@ public class ExcelUtil {
|
|||
excelWriter.finish();
|
||||
}
|
||||
|
||||
/**
|
||||
* 多sheet模板导出 模板格式为 {key.属性}
|
||||
*
|
||||
* @param templatePath 模板路径 resource 目录下的路径包括模板文件名
|
||||
* 例如: excel/temp.xlsx
|
||||
* 重点: 模板文件必须放置到启动类对应的 resource 目录下
|
||||
* @param data 模板需要的数据
|
||||
* @param os 输出流
|
||||
*/
|
||||
public static void exportTemplateMultiSheet(List<Map<String, Object>> data, String templatePath, OutputStream os) {
|
||||
ClassPathResource templateResource = new ClassPathResource(templatePath);
|
||||
ExcelWriter excelWriter = EasyExcel.write(os)
|
||||
.withTemplate(templateResource.getStream())
|
||||
.autoCloseStream(false)
|
||||
// 大数值自动转换 防止失真
|
||||
.registerConverter(new ExcelBigNumberConvert())
|
||||
.build();
|
||||
if (CollUtil.isEmpty(data)) {
|
||||
throw new IllegalArgumentException("数据为空");
|
||||
}
|
||||
for (int i = 0; i < data.size(); i++) {
|
||||
WriteSheet writeSheet = EasyExcel.writerSheet(i).build();
|
||||
for (Map.Entry<String, Object> map : data.get(i).entrySet()) {
|
||||
// 设置列表后续还有数据
|
||||
FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
|
||||
if (map.getValue() instanceof Collection) {
|
||||
// 多表导出必须使用 FillWrapper
|
||||
excelWriter.fill(new FillWrapper(map.getKey(), (Collection<?>) map.getValue()), fillConfig, writeSheet);
|
||||
} else {
|
||||
excelWriter.fill(map.getValue(), writeSheet);
|
||||
}
|
||||
}
|
||||
}
|
||||
excelWriter.finish();
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置响应体
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
package org.dromara.common.job.config;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.dromara.common.core.utils.StreamUtils;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.job.config.properties.PowerJobProperties;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
|
|
@ -10,7 +10,6 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
|
|||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import tech.powerjob.common.utils.CommonUtils;
|
||||
import tech.powerjob.common.utils.NetUtils;
|
||||
import tech.powerjob.worker.PowerJobSpringWorker;
|
||||
|
|
@ -20,16 +19,15 @@ import java.util.Arrays;
|
|||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 启动定时任务
|
||||
* Autoconfiguration class for PowerJob-worker.
|
||||
*
|
||||
* @author yhan219
|
||||
* @since 2023/6/2
|
||||
* @author songyinyin
|
||||
* @since 2020/7/26 16:37
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@ConditionalOnProperty(prefix = "powerjob.worker", name = "enabled", havingValue = "true")
|
||||
@EnableConfigurationProperties(PowerJobProperties.class)
|
||||
@EnableScheduling
|
||||
public class PowerJobConfig {
|
||||
@ConditionalOnProperty(prefix = "powerjob.worker", name = "enabled", havingValue = "true", matchIfMissing = true)
|
||||
public class PowerJobConfig{
|
||||
|
||||
@Bean
|
||||
public PowerJobSpringWorker initPowerJob(PowerJobProperties properties, DiscoveryClient discoveryClient) {
|
||||
|
|
@ -87,7 +85,7 @@ public class PowerJobConfig {
|
|||
* When enabledTestMode is set as true, PowerJob-worker no longer connects to PowerJob-server
|
||||
* or validate appName.
|
||||
*/
|
||||
config.setEnableTestMode(worker.isEnableTestMode());
|
||||
config.setAllowLazyConnectServer(worker.isAllowLazyConnectServer());
|
||||
/*
|
||||
* Max length of appended workflow context . Appended workflow context value that is longer than the value will be ignored.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -81,10 +81,10 @@ public class PowerJobProperties {
|
|||
*/
|
||||
private int maxResultLength = 8192;
|
||||
/**
|
||||
* If test mode is set as true, Powerjob-worker no longer connects to the server or validates appName.
|
||||
* Test mode is used for conditions that your have no powerjob-server in your develop env, so you can't start up the application
|
||||
* If allowLazyConnectServer is set as true, PowerJob worker allows launching without a direct connection to the server.
|
||||
* allowLazyConnectServer is used for conditions that your have no powerjob-server in your develop env so you can't startup the application
|
||||
*/
|
||||
private boolean enableTestMode = false;
|
||||
private boolean allowLazyConnectServer = false;
|
||||
/**
|
||||
* Max length of appended workflow context value length. Appended workflow context value that is longer than the value will be ignored.
|
||||
* {@link WorkflowContext} max length for #appendedContextData
|
||||
|
|
|
|||
|
|
@ -27,9 +27,20 @@
|
|||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mybatis.spring.boot</groupId>
|
||||
<artifactId>mybatis-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.mybatis</groupId>
|
||||
<artifactId>mybatis-spring</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- sql性能分析插件 -->
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ public class MybatisExceptionHandler {
|
|||
public R<Void> handleCannotFindDataSourceException(MyBatisSystemException e, HttpServletRequest request) {
|
||||
String requestURI = request.getRequestURI();
|
||||
String message = e.getMessage();
|
||||
if (message.contains("CannotFindDataSourceException")) {
|
||||
if ("CannotFindDataSourceException".contains(message)) {
|
||||
log.error("请求地址'{}', 未找到数据源", requestURI);
|
||||
return R.fail("未找到数据源,请联系管理员确认");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -149,7 +149,12 @@ public class PlusDataPermissionHandler {
|
|||
int index = sb.lastIndexOf(".");
|
||||
String clazzName = sb.substring(0, index);
|
||||
String methodName = sb.substring(index + 1, sb.length());
|
||||
Class<?> clazz = ClassUtil.loadClass(clazzName);
|
||||
Class<?> clazz;
|
||||
try {
|
||||
clazz = ClassUtil.loadClass(clazzName);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
List<Method> methods = Arrays.stream(ClassUtil.getDeclaredMethods(clazz))
|
||||
.filter(method -> method.getName().equals(methodName)).toList();
|
||||
DataPermission dataPermission;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
package org.dromara.common.oss.constant;
|
||||
|
||||
import org.dromara.common.core.constant.GlobalConstants;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
|
|
@ -13,7 +15,7 @@ public interface OssConstant {
|
|||
/**
|
||||
* 默认配置KEY
|
||||
*/
|
||||
String DEFAULT_CONFIG_KEY = "sys_oss:default_config";
|
||||
String DEFAULT_CONFIG_KEY = GlobalConstants.GLOBAL_REDIS_KEY + "sys_oss:default_config";
|
||||
|
||||
/**
|
||||
* 预览列表资源开关Key
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ public class OssFactory {
|
|||
/**
|
||||
* 根据类型获取实例
|
||||
*/
|
||||
public static OssClient instance(String configKey) {
|
||||
public static synchronized OssClient instance(String configKey) {
|
||||
String json = CacheUtils.get(CacheNames.SYS_OSS_CONFIG, configKey);
|
||||
if (json == null) {
|
||||
throw new OssException("系统异常, '" + configKey + "'配置信息不存在!");
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>ruoyi-common-ratelimiter</artifactId>
|
||||
|
||||
<description>
|
||||
ruoyi-common-ratelimiter 限流功能
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-redis</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
package org.dromara.common.ratelimiter.annotation;
|
||||
|
||||
import org.dromara.common.ratelimiter.enums.LimitType;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 限流注解
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface RateLimiter {
|
||||
/**
|
||||
* 限流key,支持使用Spring el表达式来动态获取方法上的参数值
|
||||
* 格式类似于 #code.id #{#code}
|
||||
*/
|
||||
String key() default "";
|
||||
|
||||
/**
|
||||
* 限流时间,单位秒
|
||||
*/
|
||||
int time() default 60;
|
||||
|
||||
/**
|
||||
* 限流次数
|
||||
*/
|
||||
int count() default 100;
|
||||
|
||||
/**
|
||||
* 限流类型
|
||||
*/
|
||||
LimitType limitType() default LimitType.DEFAULT;
|
||||
|
||||
/**
|
||||
* 提示消息 支持国际化 格式为 {code}
|
||||
*/
|
||||
String message() default "{rate.limiter.message}";
|
||||
}
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
package org.dromara.common.ratelimiter.aspectj;
|
||||
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import org.dromara.common.core.constant.GlobalConstants;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.core.utils.MessageUtils;
|
||||
import org.dromara.common.core.utils.ServletUtils;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.ratelimiter.annotation.RateLimiter;
|
||||
import org.dromara.common.ratelimiter.enums.LimitType;
|
||||
import org.dromara.common.redis.utils.RedisUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Before;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.redisson.api.RateType;
|
||||
import org.springframework.core.DefaultParameterNameDiscoverer;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.ParserContext;
|
||||
import org.springframework.expression.common.TemplateParserContext;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* 限流处理
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Slf4j
|
||||
@Aspect
|
||||
public class RateLimiterAspect {
|
||||
|
||||
/**
|
||||
* 定义spel表达式解析器
|
||||
*/
|
||||
private final ExpressionParser parser = new SpelExpressionParser();
|
||||
/**
|
||||
* 定义spel解析模版
|
||||
*/
|
||||
private final ParserContext parserContext = new TemplateParserContext();
|
||||
/**
|
||||
* 定义spel上下文对象进行解析
|
||||
*/
|
||||
private final EvaluationContext context = new StandardEvaluationContext();
|
||||
/**
|
||||
* 方法参数解析器
|
||||
*/
|
||||
private final ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer();
|
||||
|
||||
@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);
|
||||
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) {
|
||||
String message = rateLimiter.message();
|
||||
if (StringUtils.startsWith(message, "{") && StringUtils.endsWith(message, "}")) {
|
||||
message = MessageUtils.message(StringUtils.substring(message, 1, message.length() - 1));
|
||||
}
|
||||
throw new ServiceException(message);
|
||||
}
|
||||
log.info("限制令牌 => {}, 剩余令牌 => {}, 缓存key => '{}'", count, number, combineKey);
|
||||
} catch (Exception e) {
|
||||
if (e instanceof ServiceException) {
|
||||
throw e;
|
||||
} else {
|
||||
throw new RuntimeException("服务器限流异常,请稍候再试");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getCombineKey(RateLimiter rateLimiter, JoinPoint point) {
|
||||
String key = rateLimiter.key();
|
||||
// 获取方法(通过方法签名来获取)
|
||||
MethodSignature signature = (MethodSignature) point.getSignature();
|
||||
Method method = signature.getMethod();
|
||||
Class<?> targetClass = method.getDeclaringClass();
|
||||
// 判断是否是spel格式
|
||||
if (StringUtils.containsAny(key, "#")) {
|
||||
// 获取参数值
|
||||
Object[] args = point.getArgs();
|
||||
// 获取方法上参数的名称
|
||||
String[] parameterNames = pnd.getParameterNames(method);
|
||||
if (ArrayUtil.isEmpty(parameterNames)) {
|
||||
throw new ServiceException("限流key解析异常!请联系管理员!");
|
||||
}
|
||||
for (int i = 0; i < parameterNames.length; i++) {
|
||||
context.setVariable(parameterNames[i], args[i]);
|
||||
}
|
||||
// 解析返回给key
|
||||
try {
|
||||
Expression expression;
|
||||
if (StringUtils.startsWith(key, parserContext.getExpressionPrefix())
|
||||
&& StringUtils.endsWith(key, parserContext.getExpressionSuffix())) {
|
||||
expression = parser.parseExpression(key, parserContext);
|
||||
} else {
|
||||
expression = parser.parseExpression(key);
|
||||
}
|
||||
key = expression.getValue(context, String.class) + ":";
|
||||
} catch (Exception e) {
|
||||
throw new ServiceException("限流key解析异常!请联系管理员!");
|
||||
}
|
||||
}
|
||||
StringBuilder stringBuffer = new StringBuilder(GlobalConstants.RATE_LIMIT_KEY);
|
||||
stringBuffer.append(ServletUtils.getRequest().getRequestURI()).append(":");
|
||||
if (rateLimiter.limitType() == LimitType.IP) {
|
||||
// 获取请求ip
|
||||
stringBuffer.append(ServletUtils.getClientIP()).append(":");
|
||||
} else if (rateLimiter.limitType() == LimitType.CLUSTER) {
|
||||
// 获取客户端实例id
|
||||
stringBuffer.append(RedisUtils.getClient().getId()).append(":");
|
||||
}
|
||||
return stringBuffer.append(key).toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package org.dromara.common.ratelimiter.config;
|
||||
|
||||
import org.dromara.common.ratelimiter.aspectj.RateLimiterAspect;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.data.redis.connection.RedisConfiguration;
|
||||
|
||||
/**
|
||||
* @author guangxin
|
||||
* @date 2023/1/18
|
||||
*/
|
||||
@AutoConfiguration(after = RedisConfiguration.class)
|
||||
public class RateLimiterConfig {
|
||||
|
||||
@Bean
|
||||
public RateLimiterAspect rateLimiterAspect() {
|
||||
return new RateLimiterAspect();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package org.dromara.common.ratelimiter.enums;
|
||||
|
||||
/**
|
||||
* 限流类型
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
|
||||
public enum LimitType {
|
||||
/**
|
||||
* 默认策略全局限流
|
||||
*/
|
||||
DEFAULT,
|
||||
|
||||
/**
|
||||
* 根据请求者IP进行限流
|
||||
*/
|
||||
IP,
|
||||
|
||||
/**
|
||||
* 实例限流(集群多后端实例)
|
||||
*/
|
||||
CLUSTER
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
org.dromara.common.ratelimiter.config.RateLimiterConfig
|
||||
|
|
@ -49,6 +49,8 @@ public class RedisConfiguration {
|
|||
CompositeCodec codec = new CompositeCodec(StringCodec.INSTANCE, jsonCodec, jsonCodec);
|
||||
config.setThreads(redissonProperties.getThreads())
|
||||
.setNettyThreads(redissonProperties.getNettyThreads())
|
||||
// 缓存 Lua 脚本 减少网络传输(redisson 大部分的功能都是基于 Lua 脚本实现)
|
||||
.setUseScriptCache(true)
|
||||
.setCodec(codec);
|
||||
RedissonProperties.SingleServerConfig singleServerConfig = redissonProperties.getSingleServerConfig();
|
||||
if (ObjectUtil.isNotNull(singleServerConfig)) {
|
||||
|
|
|
|||
|
|
@ -141,6 +141,18 @@ public class RedisUtils {
|
|||
return bucket.setIfAbsent(value, duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果存在则设置 并返回 true 如果存在则返回 false
|
||||
*
|
||||
* @param key 缓存的键值
|
||||
* @param value 缓存的值
|
||||
* @return set成功或失败
|
||||
*/
|
||||
public static <T> boolean setObjectIfExists(final String key, final T value, final Duration duration) {
|
||||
RBucket<T> bucket = CLIENT.getBucket(key);
|
||||
return bucket.setIfExists(value, duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册对象监听器
|
||||
* <p>
|
||||
|
|
@ -242,6 +254,18 @@ public class RedisUtils {
|
|||
return rList.addAll(dataList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 追加缓存List数据
|
||||
*
|
||||
* @param key 缓存的键值
|
||||
* @param data 待缓存的数据
|
||||
* @return 缓存的对象
|
||||
*/
|
||||
public static <T> boolean addCacheList(final String key, final T data) {
|
||||
RList<T> rList = CLIENT.getList(key);
|
||||
return rList.add(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册List监听器
|
||||
* <p>
|
||||
|
|
@ -266,6 +290,19 @@ public class RedisUtils {
|
|||
return rList.readAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得缓存的list对象(范围)
|
||||
*
|
||||
* @param key 缓存的键值
|
||||
* @param form 起始下标
|
||||
* @param to 截止下标
|
||||
* @return 缓存键值对应的数据
|
||||
*/
|
||||
public static <T> List<T> getCacheListRange(final String key, int form, int to) {
|
||||
RList<T> rList = CLIENT.getList(key);
|
||||
return rList.range(form, to);
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存Set
|
||||
*
|
||||
|
|
@ -278,6 +315,18 @@ public class RedisUtils {
|
|||
return rSet.addAll(dataSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* 追加缓存Set数据
|
||||
*
|
||||
* @param key 缓存的键值
|
||||
* @param data 待缓存的数据
|
||||
* @return 缓存的对象
|
||||
*/
|
||||
public static <T> boolean addCacheSet(final String key, final T data) {
|
||||
RSet<T> rSet = CLIENT.getSet(key);
|
||||
return rSet.add(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册Set监听器
|
||||
* <p>
|
||||
|
|
|
|||
|
|
@ -45,12 +45,9 @@ public class PlusSaTokenDao implements SaTokenDao {
|
|||
*/
|
||||
@Override
|
||||
public void update(String key, String value) {
|
||||
long expire = getTimeout(key);
|
||||
// -2 = 无此键
|
||||
if (expire == NOT_VALUE_EXPIRE) {
|
||||
return;
|
||||
if (RedisUtils.hasKey(key)) {
|
||||
RedisUtils.setCacheObject(key, value, true);
|
||||
}
|
||||
this.set(key, value, expire);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -75,17 +72,6 @@ public class PlusSaTokenDao implements SaTokenDao {
|
|||
*/
|
||||
@Override
|
||||
public void updateTimeout(String key, long timeout) {
|
||||
// 判断是否想要设置为永久
|
||||
if (timeout == NEVER_EXPIRE) {
|
||||
long expire = getTimeout(key);
|
||||
if (expire == NEVER_EXPIRE) {
|
||||
// 如果其已经被设置为永久,则不作任何处理
|
||||
} else {
|
||||
// 如果尚未被设置为永久,那么再次set一次
|
||||
this.set(key, this.get(key), timeout);
|
||||
}
|
||||
return;
|
||||
}
|
||||
RedisUtils.expire(key, Duration.ofSeconds(timeout));
|
||||
}
|
||||
|
||||
|
|
@ -119,12 +105,9 @@ public class PlusSaTokenDao implements SaTokenDao {
|
|||
*/
|
||||
@Override
|
||||
public void updateObject(String key, Object object) {
|
||||
long expire = getObjectTimeout(key);
|
||||
// -2 = 无此键
|
||||
if (expire == NOT_VALUE_EXPIRE) {
|
||||
return;
|
||||
if (RedisUtils.hasKey(key)) {
|
||||
RedisUtils.setCacheObject(key, object, true);
|
||||
}
|
||||
this.setObject(key, object, expire);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -149,17 +132,6 @@ public class PlusSaTokenDao implements SaTokenDao {
|
|||
*/
|
||||
@Override
|
||||
public void updateObjectTimeout(String key, long timeout) {
|
||||
// 判断是否想要设置为永久
|
||||
if (timeout == NEVER_EXPIRE) {
|
||||
long expire = getObjectTimeout(key);
|
||||
if (expire == NEVER_EXPIRE) {
|
||||
// 如果其已经被设置为永久,则不作任何处理
|
||||
} else {
|
||||
// 如果尚未被设置为永久,那么再次set一次
|
||||
this.setObject(key, this.getObject(key), timeout);
|
||||
}
|
||||
return;
|
||||
}
|
||||
RedisUtils.expire(key, Duration.ofSeconds(timeout));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import org.dromara.common.core.enums.UserType;
|
|||
import org.dromara.system.api.model.LoginUser;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* 登录鉴权助手
|
||||
|
|
@ -36,6 +37,7 @@ public class LoginHelper {
|
|||
public static final String USER_KEY = "userId";
|
||||
public static final String DEPT_KEY = "deptId";
|
||||
public static final String CLIENT_KEY = "clientid";
|
||||
public static final String TENANT_ADMIN_KEY = "isTenantAdmin";
|
||||
|
||||
/**
|
||||
* 登录系统 基于 设备类型
|
||||
|
|
@ -55,32 +57,27 @@ public class LoginHelper {
|
|||
model.setExtra(TENANT_KEY, loginUser.getTenantId())
|
||||
.setExtra(USER_KEY, loginUser.getUserId())
|
||||
.setExtra(DEPT_KEY, loginUser.getDeptId()));
|
||||
StpUtil.getSession().set(LOGIN_USER_KEY, loginUser);
|
||||
StpUtil.getTokenSession().set(LOGIN_USER_KEY, loginUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户(多级缓存)
|
||||
*/
|
||||
public static LoginUser getLoginUser() {
|
||||
LoginUser loginUser = (LoginUser) SaHolder.getStorage().get(LOGIN_USER_KEY);
|
||||
if (loginUser != null) {
|
||||
return loginUser;
|
||||
}
|
||||
SaSession session = StpUtil.getSession();
|
||||
if (ObjectUtil.isNull(session)) {
|
||||
return null;
|
||||
}
|
||||
loginUser = (LoginUser) session.get(LOGIN_USER_KEY);
|
||||
SaHolder.getStorage().set(LOGIN_USER_KEY, loginUser);
|
||||
return loginUser;
|
||||
return (LoginUser) getStorageIfAbsentSet(LOGIN_USER_KEY, () -> {
|
||||
SaSession session = StpUtil.getTokenSession();
|
||||
if (ObjectUtil.isNull(session)) {
|
||||
return null;
|
||||
}
|
||||
return session.get(LOGIN_USER_KEY);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户基于token
|
||||
*/
|
||||
public static LoginUser getLoginUser(String token) {
|
||||
Object loginId = StpUtil.getLoginIdByToken(token);
|
||||
SaSession session = StpUtil.getSessionByLoginId(loginId);
|
||||
SaSession session = StpUtil.getTokenSessionByToken(token);
|
||||
if (ObjectUtil.isNull(session)) {
|
||||
return null;
|
||||
}
|
||||
|
|
@ -109,17 +106,7 @@ public class LoginHelper {
|
|||
}
|
||||
|
||||
private static Object getExtra(String key) {
|
||||
Object obj;
|
||||
try {
|
||||
obj = SaHolder.getStorage().get(key);
|
||||
if (ObjectUtil.isNull(obj)) {
|
||||
obj = StpUtil.getExtra(key);
|
||||
SaHolder.getStorage().set(key, obj);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
return obj;
|
||||
return getStorageIfAbsentSet(key, () -> StpUtil.getExtra(key));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -162,7 +149,26 @@ public class LoginHelper {
|
|||
}
|
||||
|
||||
public static boolean isTenantAdmin() {
|
||||
return isTenantAdmin(getLoginUser().getRolePermission());
|
||||
Object value = getStorageIfAbsentSet(TENANT_ADMIN_KEY, () -> {
|
||||
return isTenantAdmin(getLoginUser().getRolePermission());
|
||||
});
|
||||
return Convert.toBool(value);
|
||||
}
|
||||
|
||||
public static boolean isLogin() {
|
||||
return getLoginUser() != null;
|
||||
}
|
||||
|
||||
public static Object getStorageIfAbsentSet(String key, Supplier<Object> handle) {
|
||||
try {
|
||||
Object obj = SaHolder.getStorage().get(key);
|
||||
if (ObjectUtil.isNull(obj)) {
|
||||
obj = handle.get();
|
||||
SaHolder.getStorage().set(key, obj);
|
||||
}
|
||||
return obj;
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import jakarta.validation.ConstraintViolation;
|
|||
import jakarta.validation.ConstraintViolationException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.core.exception.DemoModeException;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.core.exception.base.BaseException;
|
||||
import org.dromara.common.core.utils.StreamUtils;
|
||||
|
|
@ -173,11 +172,4 @@ public class GlobalExceptionHandler {
|
|||
return R.fail(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 演示模式异常
|
||||
*/
|
||||
@ExceptionHandler(DemoModeException.class)
|
||||
public R<Void> handleDemoModeException(DemoModeException e) {
|
||||
return R.fail("演示模式,不允许操作");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
package com.ruoyi.common.sms.config.properties;//package com.ruoyi.common.sms.config.properties;
|
||||
//
|
||||
//import lombok.Data;
|
||||
//import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
//import org.springframework.stereotype.Component;
|
||||
//
|
||||
///**
|
||||
// * SMS短信 配置属性
|
||||
// *
|
||||
// * @author Lion Li
|
||||
// * @version 4.2.0
|
||||
// */
|
||||
//@Data
|
||||
//@ConfigurationProperties(prefix = "sms")
|
||||
//public class SmsProperties {
|
||||
//
|
||||
// private Boolean enabled;
|
||||
//
|
||||
//}
|
||||
|
|
@ -1,15 +1,14 @@
|
|||
package com.ruoyi.common.sms.config;
|
||||
package org.dromara.common.sms.config;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
|
||||
/**
|
||||
* 短信配置类
|
||||
* 短信配置类(暂时没用 预留扩展)
|
||||
*
|
||||
* @author Lion Li
|
||||
* @version 4.2.0
|
||||
*/
|
||||
@AutoConfiguration
|
||||
//@EnableConfigurationProperties(SmsProperties.class)
|
||||
public class SmsAutoConfiguration {
|
||||
|
||||
}
|
||||
|
|
@ -1 +1 @@
|
|||
com.ruoyi.common.sms.config.SmsAutoConfiguration
|
||||
org.dromara.common.sms.config.SmsAutoConfiguration
|
||||
|
|
|
|||
|
|
@ -35,33 +35,34 @@ public class SocialUtils {
|
|||
if (ObjectUtil.isNull(obj)) {
|
||||
throw new AuthException("不支持的第三方登录类型");
|
||||
}
|
||||
String clientId = obj.getClientId();
|
||||
String clientSecret = obj.getClientSecret();
|
||||
String redirectUri = obj.getRedirectUri();
|
||||
final AuthConfig.AuthConfigBuilder builder = AuthConfig.builder()
|
||||
.clientId(obj.getClientId())
|
||||
.clientSecret(obj.getClientSecret())
|
||||
.redirectUri(obj.getRedirectUri());
|
||||
return switch (source.toLowerCase()) {
|
||||
case "dingtalk" -> new AuthDingTalkRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret).redirectUri(redirectUri).build(), STATE_CACHE);
|
||||
case "baidu" -> new AuthBaiduRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret).redirectUri(redirectUri).build(), STATE_CACHE);
|
||||
case "github" -> new AuthGithubRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret).redirectUri(redirectUri).build(), STATE_CACHE);
|
||||
case "gitee" -> new AuthGiteeRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret).redirectUri(redirectUri).build(), STATE_CACHE);
|
||||
case "weibo" -> new AuthWeiboRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret).redirectUri(redirectUri).build(), STATE_CACHE);
|
||||
case "coding" -> new AuthCodingRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret).redirectUri(redirectUri).build(), STATE_CACHE);
|
||||
case "oschina" -> new AuthOschinaRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret).redirectUri(redirectUri).build(), STATE_CACHE);
|
||||
case "dingtalk" -> new AuthDingTalkRequest(builder.build(), STATE_CACHE);
|
||||
case "baidu" -> new AuthBaiduRequest(builder.build(), STATE_CACHE);
|
||||
case "github" -> new AuthGithubRequest(builder.build(), STATE_CACHE);
|
||||
case "gitee" -> new AuthGiteeRequest(builder.build(), STATE_CACHE);
|
||||
case "weibo" -> new AuthWeiboRequest(builder.build(), STATE_CACHE);
|
||||
case "coding" -> new AuthCodingRequest(builder.build(), STATE_CACHE);
|
||||
case "oschina" -> new AuthOschinaRequest(builder.build(), STATE_CACHE);
|
||||
// 支付宝在创建回调地址时,不允许使用localhost或者127.0.0.1,所以这儿的回调地址使用的局域网内的ip
|
||||
case "alipay_wallet" -> new AuthAlipayRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret).redirectUri(redirectUri).build(), socialProperties.getType().get("alipay_wallet").getAlipayPublicKey(), STATE_CACHE);
|
||||
case "qq" -> new AuthQqRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret).redirectUri(redirectUri).build(), STATE_CACHE);
|
||||
case "wechat_open" -> new AuthWeChatOpenRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret).redirectUri(redirectUri).build(), STATE_CACHE);
|
||||
case "taobao" -> new AuthTaobaoRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret).redirectUri(redirectUri).build(), STATE_CACHE);
|
||||
case "douyin" -> new AuthDouyinRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret).redirectUri(redirectUri).build(), STATE_CACHE);
|
||||
case "linkedin" -> new AuthLinkedinRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret).redirectUri(redirectUri).build(), STATE_CACHE);
|
||||
case "microsoft" -> new AuthMicrosoftRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret).redirectUri(redirectUri).build(), STATE_CACHE);
|
||||
case "renren" -> new AuthRenrenRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret).redirectUri(redirectUri).build(), STATE_CACHE);
|
||||
case "stack_overflow" -> new AuthStackOverflowRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret).redirectUri(redirectUri).stackOverflowKey("").build(), STATE_CACHE);
|
||||
case "huawei" -> new AuthHuaweiRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret).redirectUri(redirectUri).build(), STATE_CACHE);
|
||||
case "wechat_enterprise" -> new AuthWeChatEnterpriseQrcodeRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret).redirectUri(redirectUri).agentId("").build(), STATE_CACHE);
|
||||
case "gitlab" -> new AuthGitlabRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret).redirectUri(redirectUri).build(), STATE_CACHE);
|
||||
case "wechat_mp" -> new AuthWeChatMpRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret).redirectUri(redirectUri).build(), STATE_CACHE);
|
||||
case "aliyun" -> new AuthAliyunRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret).redirectUri(redirectUri).build(), STATE_CACHE);
|
||||
case "maxkey" -> new AuthMaxKeyRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret).redirectUri(redirectUri).build(), STATE_CACHE);
|
||||
case "alipay_wallet" -> new AuthAlipayRequest(builder.build(), socialProperties.getType().get("alipay_wallet").getAlipayPublicKey(), STATE_CACHE);
|
||||
case "qq" -> new AuthQqRequest(builder.build(), STATE_CACHE);
|
||||
case "wechat_open" -> new AuthWeChatOpenRequest(builder.build(), STATE_CACHE);
|
||||
case "taobao" -> new AuthTaobaoRequest(builder.build(), STATE_CACHE);
|
||||
case "douyin" -> new AuthDouyinRequest(builder.build(), STATE_CACHE);
|
||||
case "linkedin" -> new AuthLinkedinRequest(builder.build(), STATE_CACHE);
|
||||
case "microsoft" -> new AuthMicrosoftRequest(builder.build(), STATE_CACHE);
|
||||
case "renren" -> new AuthRenrenRequest(builder.build(), STATE_CACHE);
|
||||
case "stack_overflow" -> new AuthStackOverflowRequest(builder.stackOverflowKey("").build(), STATE_CACHE);
|
||||
case "huawei" -> new AuthHuaweiRequest(builder.build(), STATE_CACHE);
|
||||
case "wechat_enterprise" -> new AuthWeChatEnterpriseQrcodeRequest(builder.agentId("").build(), STATE_CACHE);
|
||||
case "gitlab" -> new AuthGitlabRequest(builder.build(), STATE_CACHE);
|
||||
case "wechat_mp" -> new AuthWeChatMpRequest(builder.build(), STATE_CACHE);
|
||||
case "aliyun" -> new AuthAliyunRequest(builder.build(), STATE_CACHE);
|
||||
case "maxkey" -> new AuthMaxKeyRequest(builder.build(), STATE_CACHE);
|
||||
default -> throw new AuthException("未获取到有效的Auth配置");
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package org.dromara.common.tenant.helper;
|
||||
|
||||
import cn.dev33.satoken.context.SaHolder;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import com.alibaba.ttl.TransmittableThreadLocal;
|
||||
import com.baomidou.mybatisplus.core.plugins.IgnoreStrategy;
|
||||
|
|
@ -13,7 +14,6 @@ import org.dromara.common.core.utils.SpringUtils;
|
|||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.redis.utils.RedisUtils;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
|
|
@ -82,10 +82,13 @@ public class TenantHelper {
|
|||
/**
|
||||
* 设置动态租户(一直有效 需要手动清理)
|
||||
* <p>
|
||||
* 如果为非web环境 那么只在当前线程内生效
|
||||
* 如果为未登录状态下 那么只在当前线程内生效
|
||||
*/
|
||||
public static void setDynamic(String tenantId) {
|
||||
if (!isWeb()) {
|
||||
if (!isEnable()) {
|
||||
return;
|
||||
}
|
||||
if (!isLogin()) {
|
||||
TEMP_DYNAMIC_TENANT.set(tenantId);
|
||||
return;
|
||||
}
|
||||
|
|
@ -97,10 +100,13 @@ public class TenantHelper {
|
|||
/**
|
||||
* 获取动态租户(一直有效 需要手动清理)
|
||||
* <p>
|
||||
* 如果为非web环境 那么只在当前线程内生效
|
||||
* 如果为未登录状态下 那么只在当前线程内生效
|
||||
*/
|
||||
public static String getDynamic() {
|
||||
if (!isWeb()) {
|
||||
if (!isEnable()) {
|
||||
return null;
|
||||
}
|
||||
if (!isLogin()) {
|
||||
return TEMP_DYNAMIC_TENANT.get();
|
||||
}
|
||||
String cacheKey = DYNAMIC_TENANT_KEY + ":" + LoginHelper.getUserId();
|
||||
|
|
@ -117,7 +123,10 @@ public class TenantHelper {
|
|||
* 清除动态租户
|
||||
*/
|
||||
public static void clearDynamic() {
|
||||
if (!isWeb()) {
|
||||
if (!isEnable()) {
|
||||
return;
|
||||
}
|
||||
if (!isLogin()) {
|
||||
TEMP_DYNAMIC_TENANT.remove();
|
||||
return;
|
||||
}
|
||||
|
|
@ -126,10 +135,41 @@ public class TenantHelper {
|
|||
SaHolder.getStorage().delete(cacheKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在动态租户中执行
|
||||
*
|
||||
* @param handle 处理执行方法
|
||||
*/
|
||||
public static void dynamic(String tenantId, Runnable handle) {
|
||||
setDynamic(tenantId);
|
||||
try {
|
||||
handle.run();
|
||||
} finally {
|
||||
clearDynamic();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 在动态租户中执行
|
||||
*
|
||||
* @param handle 处理执行方法
|
||||
*/
|
||||
public static <T> T dynamic(String tenantId, Supplier<T> handle) {
|
||||
setDynamic(tenantId);
|
||||
try {
|
||||
return handle.get();
|
||||
} finally {
|
||||
clearDynamic();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前租户id(动态租户优先)
|
||||
*/
|
||||
public static String getTenantId() {
|
||||
if (!isEnable()) {
|
||||
return null;
|
||||
}
|
||||
String tenantId = TenantHelper.getDynamic();
|
||||
if (StringUtils.isBlank(tenantId)) {
|
||||
tenantId = LoginHelper.getTenantId();
|
||||
|
|
@ -137,11 +177,13 @@ public class TenantHelper {
|
|||
return tenantId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否web环境
|
||||
*/
|
||||
private static boolean isWeb() {
|
||||
return RequestContextHolder.getRequestAttributes() != null;
|
||||
private static boolean isLogin() {
|
||||
try {
|
||||
StpUtil.checkLogin();
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,12 @@ public interface TransConstant {
|
|||
*/
|
||||
String USER_ID_TO_NAME = "user_id_to_name";
|
||||
|
||||
/**
|
||||
* 用户id转用户昵称
|
||||
*/
|
||||
String USER_ID_TO_NICKNAME = "user_id_to_nickname";
|
||||
|
||||
|
||||
/**
|
||||
* 部门id转名称
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
package org.dromara.common.translation.core.impl;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.apache.dubbo.config.annotation.DubboReference;
|
||||
import org.dromara.common.translation.annotation.TranslationType;
|
||||
import org.dromara.common.translation.constant.TransConstant;
|
||||
import org.dromara.common.translation.core.TranslationInterface;
|
||||
import org.dromara.system.api.RemoteUserService;
|
||||
|
||||
/**
|
||||
* 用户昵称翻译实现
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@TranslationType(type = TransConstant.USER_ID_TO_NICKNAME)
|
||||
public class NicknameTranslationImpl implements TranslationInterface<String> {
|
||||
|
||||
@DubboReference
|
||||
private RemoteUserService remoteUserService;
|
||||
|
||||
@Override
|
||||
public String translation(Object key, String other) {
|
||||
return remoteUserService.selectNicknameById((Long) key);
|
||||
}
|
||||
}
|
||||
|
|
@ -16,7 +16,7 @@ import org.apache.dubbo.config.annotation.DubboReference;
|
|||
@TranslationType(type = TransConstant.OSS_ID_TO_URL)
|
||||
public class OssUrlTranslationImpl implements TranslationInterface<String> {
|
||||
|
||||
@DubboReference
|
||||
@DubboReference(mock = "true")
|
||||
private RemoteFileService ossService;
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -3,3 +3,4 @@ org.dromara.common.translation.core.impl.DeptNameTranslationImpl
|
|||
org.dromara.common.translation.core.impl.DictTypeTranslationImpl
|
||||
org.dromara.common.translation.core.impl.OssUrlTranslationImpl
|
||||
org.dromara.common.translation.core.impl.UserNameTranslationImpl
|
||||
org.dromara.common.translation.core.impl.NicknameTranslationImpl
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ public class WebSocketUtils {
|
|||
|
||||
private static void sendMessage(WebSocketSession session, WebSocketMessage<?> message) {
|
||||
if (session == null || !session.isOpen()) {
|
||||
log.error("[send] session会话已经关闭");
|
||||
log.warn("[send] session会话已经关闭");
|
||||
} else {
|
||||
try {
|
||||
session.sendMessage(message);
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
package org.dromara.demo.controller;
|
||||
|
||||
import cn.easyes.core.conditions.LambdaEsQueryWrapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.demo.domain.Document;
|
||||
import org.dromara.demo.esmapper.DocumentMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.easyes.core.conditions.select.LambdaEsQueryWrapper;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
|
|
|
|||
|
|
@ -94,6 +94,41 @@ public class TestExcelController {
|
|||
exportExcelService.exportWithOptions(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 多个sheet导出
|
||||
*/
|
||||
@GetMapping("/exportTemplateMultiSheet")
|
||||
public void exportTemplateMultiSheet(HttpServletResponse response) {
|
||||
List<TestObj1> list1 = new ArrayList<>();
|
||||
list1.add(new TestObj1("list1测试1", "list1测试2", "list1测试3"));
|
||||
list1.add(new TestObj1("list1测试4", "list1测试5", "list1测试6"));
|
||||
List<TestObj1> list2 = new ArrayList<>();
|
||||
list2.add(new TestObj1("list2测试1", "list2测试2", "list2测试3"));
|
||||
list2.add(new TestObj1("list2测试4", "list2测试5", "list2测试6"));
|
||||
List<TestObj1> list3 = new ArrayList<>();
|
||||
list3.add(new TestObj1("list3测试1", "list3测试2", "list3测试3"));
|
||||
list3.add(new TestObj1("list3测试4", "list3测试5", "list3测试6"));
|
||||
List<TestObj1> list4 = new ArrayList<>();
|
||||
list4.add(new TestObj1("list4测试1", "list4测试2", "list4测试3"));
|
||||
list4.add(new TestObj1("list4测试4", "list4测试5", "list4测试6"));
|
||||
|
||||
List<Map<String, Object>> list = new ArrayList<>();
|
||||
Map<String, Object> sheetMap1 = new HashMap<>();
|
||||
sheetMap1.put("data1", list1);
|
||||
Map<String, Object> sheetMap2 = new HashMap<>();
|
||||
sheetMap2.put("data2", list2);
|
||||
Map<String, Object> sheetMap3 = new HashMap<>();
|
||||
sheetMap3.put("data3", list3);
|
||||
Map<String, Object> sheetMap4 = new HashMap<>();
|
||||
sheetMap4.put("data4", list4);
|
||||
|
||||
list.add(sheetMap1);
|
||||
list.add(sheetMap2);
|
||||
list.add(sheetMap3);
|
||||
list.add(sheetMap4);
|
||||
ExcelUtil.exportTemplateMultiSheet(list, "多sheet列表", "excel/多sheet列表.xlsx", response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入表格
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package org.dromara.demo.esmapper;
|
||||
|
||||
import cn.easyes.core.conditions.interfaces.BaseEsMapper;
|
||||
import org.dromara.demo.domain.Document;
|
||||
import org.dromara.easyes.core.core.BaseEsMapper;
|
||||
|
||||
public interface DocumentMapper extends BaseEsMapper<Document> {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ public class SensitiveServiceImpl implements SensitiveService {
|
|||
*/
|
||||
@Override
|
||||
public boolean isSensitive(String roleKey, String perms) {
|
||||
if (!StpUtil.isLogin()) {
|
||||
if (!LoginHelper.isLogin()) {
|
||||
return true;
|
||||
}
|
||||
boolean roleExist = StringUtils.isNotBlank(roleKey);
|
||||
|
|
|
|||
|
|
@ -90,8 +90,6 @@ easy-es:
|
|||
connectTimeout: 5000
|
||||
# 通信超时时间 单位:ms
|
||||
socketTimeout: 5000
|
||||
# 请求超时时间 单位:ms
|
||||
requestTimeout: 5000
|
||||
# 连接请求超时时间 单位:ms
|
||||
connectionRequestTimeout: 5000
|
||||
# 最大连接数 单位:个
|
||||
|
|
@ -114,5 +112,3 @@ easy-es:
|
|||
enable-track-total-hits: true
|
||||
# 数据刷新策略,默认为不刷新
|
||||
refresh-policy: immediate
|
||||
# 是否全局开启must查询类型转换为filter查询类型 默认为false不转换
|
||||
enable-must2-filter: false
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -64,6 +64,17 @@
|
|||
<artifactId>ruoyi-common-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-tenant</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-mybatis</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
|
|
|||
|
|
@ -9,15 +9,14 @@ RUN mkdir -p /ruoyi/gateway/logs \
|
|||
|
||||
WORKDIR /ruoyi/gateway
|
||||
|
||||
ENV SERVER_PORT=8080 LANG=C.UTF-8 LC_ALL=C.UTF-8
|
||||
ENV SERVER_PORT=8080 LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS=""
|
||||
|
||||
EXPOSE ${SERVER_PORT}
|
||||
|
||||
ADD ./target/ruoyi-gateway.jar ./app.jar
|
||||
|
||||
ENTRYPOINT ["java", \
|
||||
"-Djava.security.egd=file:/dev/./urandom", \
|
||||
"-Dserver.port=${SERVER_PORT}", \
|
||||
# "-Dskywalking.agent.service_name=ruoyi-gateway", \
|
||||
# "-javaagent:/ruoyi/skywalking/agent/skywalking-agent.jar", \
|
||||
"-jar", "app.jar"]
|
||||
ENTRYPOINT java -Djava.security.egd=file:/dev/./urandom -Dserver.port=${SERVER_PORT} \
|
||||
#-Dskywalking.agent.service_name=ruoyi-gateway \
|
||||
#-javaagent:/ruoyi/skywalking/agent/skywalking-agent.jar \
|
||||
-jar app.jar \
|
||||
-XX:+HeapDumpOnOutOfMemoryError -Xlog:gc*,:time,tags,level -XX:+UseZGC ${JAVA_OPTS}
|
||||
|
|
|
|||
|
|
@ -7,10 +7,10 @@ RUN mkdir -p /ruoyi/gen/logs
|
|||
|
||||
WORKDIR /ruoyi/gen
|
||||
|
||||
ENV SERVER_PORT=9202 LANG=C.UTF-8 LC_ALL=C.UTF-8
|
||||
ENV SERVER_PORT=9202 LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS=""
|
||||
|
||||
EXPOSE ${SERVER_PORT}
|
||||
|
||||
ADD ./target/ruoyi-gen.jar ./app.jar
|
||||
|
||||
ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-Dserver.port=${SERVER_PORT}", "-jar", "app.jar"]
|
||||
ENTRYPOINT java -Djava.security.egd=file:/dev/./urandom -Dserver.port=${SERVER_PORT} -jar app.jar ${JAVA_OPTS}
|
||||
|
|
|
|||
|
|
@ -9,15 +9,15 @@ RUN mkdir -p /ruoyi/job/logs \
|
|||
|
||||
WORKDIR /ruoyi/job
|
||||
|
||||
ENV SERVER_PORT=9203 LANG=C.UTF-8 LC_ALL=C.UTF-8
|
||||
ENV SERVER_PORT=9203 LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS=""
|
||||
|
||||
EXPOSE ${SERVER_PORT}
|
||||
|
||||
ADD ./target/ruoyi-job.jar ./app.jar
|
||||
|
||||
ENTRYPOINT ["java", \
|
||||
"-Djava.security.egd=file:/dev/./urandom", \
|
||||
"-Dserver.port=${SERVER_PORT}", \
|
||||
# "-Dskywalking.agent.service_name=ruoyi-job", \
|
||||
# "-javaagent:/ruoyi/skywalking/agent/skywalking-agent.jar", \
|
||||
"-jar", "app.jar"]
|
||||
ENTRYPOINT java -Djava.security.egd=file:/dev/./urandom -Dserver.port=${SERVER_PORT} \
|
||||
#-Dskywalking.agent.service_name=ruoyi-job \
|
||||
#-javaagent:/ruoyi/skywalking/agent/skywalking-agent.jar \
|
||||
-jar app.jar \
|
||||
-XX:+HeapDumpOnOutOfMemoryError -Xlog:gc*,:time,tags,level -XX:+UseZGC ${JAVA_OPTS}
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue