Compare commits

..

No commits in common. "master" and "v3.1.0" have entirely different histories.

387 changed files with 5959 additions and 13299 deletions

1
.github/FUNDING.yml vendored
View File

@ -1 +0,0 @@
custom: http://doc.ruoyi.vip/ruoyi-cloud/other/donate.html

3
.gitignore vendored
View File

@ -25,8 +25,6 @@ target/
*.iml *.iml
*.ipr *.ipr
### JRebel ###
rebel.xml
### NetBeans ### ### NetBeans ###
nbproject/private/ nbproject/private/
build/* build/*
@ -39,7 +37,6 @@ nbdist/
# Others # Others
*.log *.log
*.xml.versionsBackup *.xml.versionsBackup
*.swp
!*/build/*.java !*/build/*.java
!*/build/*.html !*/build/*.html

View File

@ -1,14 +1,3 @@
<p align="center">
<img alt="logo" src="https://oscimg.oschina.net/oscnet/up-b99b286755aef70355a7084753f89cdb7c9.png">
</p>
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">RuoYi v3.6.3</h1>
<h4 align="center">基于 Vue/Element UI 和 Spring Boot/Spring Cloud & Alibaba 前后端分离的分布式微服务架构</h4>
<p align="center">
<a href="https://gitee.com/y_project/RuoYi-Cloud/stargazers"><img src="https://gitee.com/y_project/RuoYi-Cloud/badge/star.svg?theme=dark"></a>
<a href="https://gitee.com/y_project/RuoYi-Cloud"><img src="https://img.shields.io/badge/RuoYi-v3.6.3-brightgreen.svg"></a>
<a href="https://gitee.com/y_project/RuoYi-Cloud/blob/master/LICENSE"><img src="https://img.shields.io/github/license/mashape/apistatus.svg"></a>
</p>
## 平台简介 ## 平台简介
若依是一套全部开源的快速开发平台,毫无保留给个人及企业免费使用。 若依是一套全部开源的快速开发平台,毫无保留给个人及企业免费使用。
@ -17,7 +6,6 @@
* 后端采用Spring Boot、Spring Cloud & Alibaba。 * 后端采用Spring Boot、Spring Cloud & Alibaba。
* 注册中心、配置中心选型Nacos权限认证使用Redis。 * 注册中心、配置中心选型Nacos权限认证使用Redis。
* 流量控制框架选型Sentinel分布式事务选型Seata。 * 流量控制框架选型Sentinel分布式事务选型Seata。
* 提供了技术栈([Vue3](https://v3.cn.vuejs.org) [Element Plus](https://element-plus.org/zh-CN) [Vite](https://cn.vitejs.dev))版本[RuoYi-Cloud-Vue3](https://github.com/yangzongzhuan/RuoYi-Cloud-Vue3),保持同步更新。
* 如需不分离应用,请移步 [RuoYi](https://gitee.com/y_project/RuoYi),如需分离应用,请移步 [RuoYi-Vue](https://gitee.com/y_project/RuoYi-Vue) * 如需不分离应用,请移步 [RuoYi](https://gitee.com/y_project/RuoYi),如需分离应用,请移步 [RuoYi-Vue](https://gitee.com/y_project/RuoYi-Vue)
* 阿里云折扣场:[点我进入](http://aly.ruoyi.vip),腾讯云秒杀场:[点我进入](http://txy.ruoyi.vip)&nbsp;&nbsp; * 阿里云折扣场:[点我进入](http://aly.ruoyi.vip),腾讯云秒杀场:[点我进入](http://txy.ruoyi.vip)&nbsp;&nbsp;
* 阿里云优惠券:[点我领取](https://www.aliyun.com/minisite/goods?userCode=brki8iof&share_source=copy_link),腾讯云优惠券:[点我领取](https://cloud.tencent.com/redirect.php?redirect=1025&cps_key=198c8df2ed259157187173bc7f4f32fd&from=console)&nbsp;&nbsp; * 阿里云优惠券:[点我领取](https://www.aliyun.com/minisite/goods?userCode=brki8iof&share_source=copy_link),腾讯云优惠券:[点我领取](https://cloud.tencent.com/redirect.php?redirect=1025&cps_key=198c8df2ed259157187173bc7f4f32fd&from=console)&nbsp;&nbsp;
@ -39,7 +27,6 @@ com.ruoyi
│ └── ruoyi-common-datasource // 多数据源 │ └── ruoyi-common-datasource // 多数据源
│ └── ruoyi-common-log // 日志记录 │ └── ruoyi-common-log // 日志记录
│ └── ruoyi-common-redis // 缓存服务 │ └── ruoyi-common-redis // 缓存服务
│ └── ruoyi-common-seata // 分布式事务
│ └── ruoyi-common-security // 安全模块 │ └── ruoyi-common-security // 安全模块
│ └── ruoyi-common-swagger // 系统接口 │ └── ruoyi-common-swagger // 系统接口
├── ruoyi-modules // 业务模块 ├── ruoyi-modules // 业务模块
@ -128,4 +115,4 @@ com.ruoyi
## 若依微服务交流群 ## 若依微服务交流群
QQ群 [![加入QQ群](https://img.shields.io/badge/已满-42799195-blue.svg)](https://jq.qq.com/?_wv=1027&k=yqInfq0S) [![加入QQ群](https://img.shields.io/badge/已满-170157040-blue.svg)](https://jq.qq.com/?_wv=1027&k=Oy1mb3p8) [![加入QQ群](https://img.shields.io/badge/已满-130643120-blue.svg)](https://jq.qq.com/?_wv=1027&k=rvxkJtXK) [![加入QQ群](https://img.shields.io/badge/已满-225920371-blue.svg)](https://jq.qq.com/?_wv=1027&k=0Ck3PvTe) [![加入QQ群](https://img.shields.io/badge/已满-201705537-blue.svg)](https://jq.qq.com/?_wv=1027&k=FnHHP4TT) [![加入QQ群](https://img.shields.io/badge/已满-236543183-blue.svg)](https://jq.qq.com/?_wv=1027&k=qdT1Ojpz) [![加入QQ群](https://img.shields.io/badge/已满-213618602-blue.svg)](https://jq.qq.com/?_wv=1027&k=nw3OiyXs) [![加入QQ群](https://img.shields.io/badge/已满-148794840-blue.svg)](https://jq.qq.com/?_wv=1027&k=kiU5WDls) [![加入QQ群](https://img.shields.io/badge/已满-118752664-blue.svg)](https://jq.qq.com/?_wv=1027&k=MtBy6YfT) [![加入QQ群](https://img.shields.io/badge/已满-101038945-blue.svg)](https://jq.qq.com/?_wv=1027&k=FqImHgH2) [![加入QQ群](https://img.shields.io/badge/已满-128355254-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=G4jZ4EtdT50PhnMBudTnEwgonxkXOscJ&authKey=FkGHYfoTKlGE6wHdKdjH9bVoOgQjtLP9WM%2Fj7pqGY1msoqw9uxDiBo39E2mLgzYg&noverify=0&group_code=128355254) [![加入QQ群](https://img.shields.io/badge/179219821-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=irnwcXhbLOQEv1g-TwGifjNTA_f4wZiA&authKey=4bpzEwhcUY%2FvsPDHvzYn6xfoS%2FtOArvZ%2BGXzfr7O0%2FEqLfkKA%2BuCDXlzHIFg8t93&noverify=0&group_code=179219821) 点击按钮入群。 QQ群 [![加入QQ群](https://img.shields.io/badge/已满-42799195-blue.svg)](https://jq.qq.com/?_wv=1027&k=yqInfq0S) [![加入QQ群](https://img.shields.io/badge/已满-170157040-blue.svg)](https://jq.qq.com/?_wv=1027&k=Oy1mb3p8) [![加入QQ群](https://img.shields.io/badge/已满-130643120-blue.svg)](https://jq.qq.com/?_wv=1027&k=rvxkJtXK) [![加入QQ群](https://img.shields.io/badge/225920371-blue.svg)](https://jq.qq.com/?_wv=1027&k=0Ck3PvTe) 点击按钮入群。

View File

@ -1,6 +1,6 @@
@echo off @echo off
echo. echo.
echo [信息] 清理工程target生成路径。 echo [信息] 清理生成路径。
echo. echo.
%~d0 %~d0

View File

@ -1,6 +1,6 @@
@echo off @echo off
echo. echo.
echo [信息] 使用Jar命令运行Auth工程。 echo [信息] 运行auth工程。
echo. echo.
cd %~dp0 cd %~dp0
@ -8,7 +8,7 @@ cd ../ruoyi-auth/target
set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m
java -Dfile.encoding=utf-8 %JAVA_OPTS% -jar ruoyi-auth.jar java -Dfile.encoding=utf-8 -jar %JAVA_OPTS% ruoyi-auth.jar
cd bin cd bin
pause pause

View File

@ -1,6 +1,6 @@
@echo off @echo off
echo. echo.
echo [信息] 使用Jar命令运行Gateway工程。 echo [信息] 运行gateway工程。
echo. echo.
cd %~dp0 cd %~dp0
@ -8,7 +8,7 @@ cd ../ruoyi-gateway/target
set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m
java -Dfile.encoding=utf-8 %JAVA_OPTS% -jar ruoyi-gateway.jar java -Dfile.encoding=utf-8 -jar %JAVA_OPTS% ruoyi-gateway.jar
cd bin cd bin
pause pause

View File

@ -1,6 +1,6 @@
@echo off @echo off
echo. echo.
echo [信息] 使用Jar命令运行Modules-File工程。 echo [信息] 运行modules-file工程。
echo. echo.
cd %~dp0 cd %~dp0
@ -8,7 +8,7 @@ cd ../ruoyi-modules/ruoyi-file/target
set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m
java -Dfile.encoding=utf-8 %JAVA_OPTS% -jar ruoyi-modules-file.jar java -Dfile.encoding=utf-8 -jar %JAVA_OPTS% ruoyi-modules-file.jar
cd bin cd bin
pause pause

View File

@ -1,6 +1,6 @@
@echo off @echo off
echo. echo.
echo [信息] 使用Jar命令运行Modules-Gen工程。 echo [信息] 运行modules-gen工程。
echo. echo.
cd %~dp0 cd %~dp0
@ -8,7 +8,7 @@ cd ../ruoyi-modules/ruoyi-gen/target
set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m
java -Dfile.encoding=utf-8 %JAVA_OPTS% -jar ruoyi-modules-gen.jar java -Dfile.encoding=utf-8 -jar %JAVA_OPTS% ruoyi-modules-gen.jar
cd bin cd bin
pause pause

View File

@ -1,6 +1,6 @@
@echo off @echo off
echo. echo.
echo [信息] 使用Jar命令运行Modules-Job工程。 echo [信息] 运行modules-job工程。
echo. echo.
cd %~dp0 cd %~dp0
@ -8,7 +8,7 @@ cd ../ruoyi-modules/ruoyi-job/target
set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m
java -Dfile.encoding=utf-8 %JAVA_OPTS% -jar ruoyi-modules-job.jar java -Dfile.encoding=utf-8 -jar %JAVA_OPTS% ruoyi-modules-job.jar
cd bin cd bin
pause pause

View File

@ -1,6 +1,6 @@
@echo off @echo off
echo. echo.
echo [信息] 使用Jar命令运行Modules-System工程。 echo [信息] 运行modules-system工程。
echo. echo.
cd %~dp0 cd %~dp0
@ -8,7 +8,7 @@ cd ../ruoyi-modules/ruoyi-system/target
set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m
java -Dfile.encoding=utf-8 %JAVA_OPTS% -jar ruoyi-modules-system.jar java -Dfile.encoding=utf-8 -jar %JAVA_OPTS% ruoyi-modules-system.jar
cd bin cd bin
pause pause

View File

@ -1,6 +1,6 @@
@echo off @echo off
echo. echo.
echo [信息] 使用Jar命令运行Monitor工程。 echo [信息] 运行monitor工程。
echo. echo.
cd %~dp0 cd %~dp0
@ -8,7 +8,7 @@ cd ../ruoyi-visual/ruoyi-monitor/target
set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m
java -Dfile.encoding=utf-8 %JAVA_OPTS% -jar ruoyi-visual-monitor.jar java -Dfile.encoding=utf-8 -jar %JAVA_OPTS% ruoyi-visual-monitor.jar
cd bin cd bin
pause pause

View File

@ -1,41 +0,0 @@
#!/bin/sh
# 复制项目的文件到对应docker路径便于一键生成镜像。
usage() {
echo "Usage: sh copy.sh"
exit 1
}
# copy sql
echo "begin copy sql "
cp ../sql/ry_20230706.sql ./mysql/db
cp ../sql/ry_config_20220929.sql ./mysql/db
# copy html
echo "begin copy html "
cp -r ../ruoyi-ui/dist/** ./nginx/html/dist
# copy jar
echo "begin copy ruoyi-gateway "
cp ../ruoyi-gateway/target/ruoyi-gateway.jar ./ruoyi/gateway/jar
echo "begin copy ruoyi-auth "
cp ../ruoyi-auth/target/ruoyi-auth.jar ./ruoyi/auth/jar
echo "begin copy ruoyi-visual "
cp ../ruoyi-visual/ruoyi-monitor/target/ruoyi-visual-monitor.jar ./ruoyi/visual/monitor/jar
echo "begin copy ruoyi-modules-system "
cp ../ruoyi-modules/ruoyi-system/target/ruoyi-modules-system.jar ./ruoyi/modules/system/jar
echo "begin copy ruoyi-modules-file "
cp ../ruoyi-modules/ruoyi-file/target/ruoyi-modules-file.jar ./ruoyi/modules/file/jar
echo "begin copy ruoyi-modules-job "
cp ../ruoyi-modules/ruoyi-job/target/ruoyi-modules-job.jar ./ruoyi/modules/job/jar
echo "begin copy ruoyi-modules-gen "
cp ../ruoyi-modules/ruoyi-gen/target/ruoyi-modules-gen.jar ./ruoyi/modules/gen/jar

View File

@ -26,12 +26,12 @@ port(){
# 启动基础环境(必须) # 启动基础环境(必须)
base(){ base(){
docker-compose up -d ruoyi-mysql ruoyi-redis ruoyi-nacos docker-compose up -d ruoyi-mysql ruoyi-redis ruoyi-nacos ruoyi-nginx
} }
# 启动程序模块(必须) # 启动程序模块(必须)
modules(){ modules(){
docker-compose up -d ruoyi-nginx ruoyi-gateway ruoyi-auth ruoyi-modules-system docker-compose up -d ruoyi-gateway ruoyi-auth ruoyi-modules-system
} }
# 关闭所有环境/模块 # 关闭所有环境/模块

View File

@ -16,7 +16,7 @@ management.metrics.export.influx.enabled=false
server.tomcat.accesslog.enabled=true server.tomcat.accesslog.enabled=true
server.tomcat.accesslog.pattern=%h %l %u %t "%r" %s %b %D %{User-Agent}i %{Request-Source}i server.tomcat.accesslog.pattern=%h %l %u %t "%r" %s %b %D %{User-Agent}i %{Request-Source}i
server.tomcat.basedir=/home/ruoyi/nacos/tomcat/logs server.tomcat.basedir=
nacos.security.ignore.urls=/,/error,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-ui/public/**,/v1/auth/**,/v1/console/health/**,/actuator/**,/v1/console/server/** nacos.security.ignore.urls=/,/error,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-ui/public/**,/v1/auth/**,/v1/console/health/**,/actuator/**,/v1/console/server/**

View File

@ -14,28 +14,23 @@ http {
listen 80; listen 80;
server_name localhost; server_name localhost;
location / { location / {
root /home/ruoyi/projects/ruoyi-ui; root /home/ruoyi/projects/ruoyi-ui;
try_files $uri $uri/ /index.html; try_files $uri $uri/ /index.html;
index index.html index.htm; index index.html index.htm;
} }
location /prod-api/{ location /prod-api/{
proxy_set_header Host $http_host; proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr; proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://ruoyi-gateway:8080/; proxy_pass http://ruoyi-gateway:8080/;
} }
# 避免actuator暴露
if ($request_uri ~ "/actuator") {
return 403;
}
error_page 500 502 503 504 /50x.html; error_page 500 502 503 504 /50x.html;
location = /50x.html { location = /50x.html {
root html; root html;
} }
} }
} }# requirepass 123456

145
pom.xml
View File

@ -6,35 +6,38 @@
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi</artifactId> <artifactId>ruoyi</artifactId>
<version>3.6.3</version> <version>3.1.0</version>
<name>ruoyi</name> <name>ruoyi</name>
<url>http://www.ruoyi.vip</url> <url>http://www.ruoyi.vip</url>
<description>若依微服务系统</description> <description>若依微服务系统</description>
<properties> <properties>
<ruoyi.version>3.6.3</ruoyi.version> <ruoyi.version>3.1.0</ruoyi.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version> <java.version>1.8</java.version>
<spring-boot.version>2.7.18</spring-boot.version> <spring-boot.version>2.5.3</spring-boot.version>
<spring-cloud.version>2021.0.8</spring-cloud.version> <spring-cloud.version>2020.0.3</spring-cloud.version>
<spring-cloud-alibaba.version>2021.0.5.0</spring-cloud-alibaba.version> <spring-cloud-alibaba.version>2021.1</spring-cloud-alibaba.version>
<spring-boot-admin.version>2.7.11</spring-boot-admin.version> <alibaba.nacos.version>2.0.3</alibaba.nacos.version>
<spring-boot-admin.version>2.4.3</spring-boot-admin.version>
<spring-boot.mybatis>2.2.0</spring-boot.mybatis>
<swagger.fox.version>3.0.0</swagger.fox.version> <swagger.fox.version>3.0.0</swagger.fox.version>
<swagger.core.version>1.6.2</swagger.core.version> <swagger.core.version>1.6.2</swagger.core.version>
<tobato.version>1.27.2</tobato.version> <tobato.version>1.27.2</tobato.version>
<kaptcha.version>2.3.3</kaptcha.version> <kaptcha.version>2.3.2</kaptcha.version>
<pagehelper.boot.version>2.0.0</pagehelper.boot.version> <pagehelper.boot.version>1.3.1</pagehelper.boot.version>
<druid.version>1.2.20</druid.version> <druid.version>1.2.6</druid.version>
<dynamic-ds.version>4.2.0</dynamic-ds.version> <dynamic-ds.version>3.4.1</dynamic-ds.version>
<commons.io.version>2.13.0</commons.io.version> <commons.io.version>2.11.0</commons.io.version>
<velocity.version>2.3</velocity.version> <commons.fileupload.version>1.4</commons.fileupload.version>
<fastjson.version>2.0.43</fastjson.version> <velocity.version>1.7</velocity.version>
<jjwt.version>0.9.1</jjwt.version> <fastjson.version>1.2.76</fastjson.version>
<minio.version>8.2.2</minio.version> <minio.version>8.2.2</minio.version>
<poi.version>4.1.2</poi.version> <poi.version>4.1.2</poi.version>
<transmittable-thread-local.version>2.14.4</transmittable-thread-local.version> <common-pool.version>2.10.0</common-pool.version>
<commons-collections.version>3.2.2</commons-collections.version>
</properties> </properties>
<!-- 依赖声明 --> <!-- 依赖声明 -->
@ -59,6 +62,13 @@
<scope>import</scope> <scope>import</scope>
</dependency> </dependency>
<!-- Alibaba Nacos 配置 -->
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>${alibaba.nacos.version}</version>
</dependency>
<!-- SpringBoot 依赖配置 --> <!-- SpringBoot 依赖配置 -->
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
@ -68,6 +78,13 @@
<scope>import</scope> <scope>import</scope>
</dependency> </dependency>
<!-- SpringBoot 监控客户端 -->
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
<version>${spring-boot-admin.version}</version>
</dependency>
<!-- FastDFS 分布式文件系统 --> <!-- FastDFS 分布式文件系统 -->
<dependency> <dependency>
<groupId>com.github.tobato</groupId> <groupId>com.github.tobato</groupId>
@ -75,6 +92,13 @@
<version>${tobato.version}</version> <version>${tobato.version}</version>
</dependency> </dependency>
<!-- Mybatis 依赖配置 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${spring-boot.mybatis}</version>
</dependency>
<!-- Swagger 依赖配置 --> <!-- Swagger 依赖配置 -->
<dependency> <dependency>
<groupId>io.swagger</groupId> <groupId>io.swagger</groupId>
@ -89,7 +113,7 @@
<!-- 验证码 --> <!-- 验证码 -->
<dependency> <dependency>
<groupId>pro.fessional</groupId> <groupId>com.github.penggle</groupId>
<artifactId>kaptcha</artifactId> <artifactId>kaptcha</artifactId>
<version>${kaptcha.version}</version> <version>${kaptcha.version}</version>
</dependency> </dependency>
@ -115,32 +139,45 @@
<version>${poi.version}</version> <version>${poi.version}</version>
</dependency> </dependency>
<!-- 文件上传工具类 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>${commons.fileupload.version}</version>
</dependency>
<!-- 代码生成使用模板 --> <!-- 代码生成使用模板 -->
<dependency> <dependency>
<groupId>org.apache.velocity</groupId> <groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId> <artifactId>velocity</artifactId>
<version>${velocity.version}</version> <version>${velocity.version}</version>
<exclusions>
<exclusion>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Collection 增强Java集合框架 -->
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>${commons-collections.version}</version>
</dependency> </dependency>
<!-- JSON 解析器和生成器 --> <!-- JSON 解析器和生成器 -->
<dependency> <dependency>
<groupId>com.alibaba.fastjson2</groupId> <groupId>com.alibaba</groupId>
<artifactId>fastjson2</artifactId> <artifactId>fastjson</artifactId>
<version>${fastjson.version}</version> <version>${fastjson.version}</version>
</dependency> </dependency>
<!-- JWT --> <!-- 公共资源池 -->
<dependency> <dependency>
<groupId>io.jsonwebtoken</groupId> <groupId>org.apache.commons</groupId>
<artifactId>jjwt</artifactId> <artifactId>commons-pool2</artifactId>
<version>${jjwt.version}</version> <version>${common-pool.version}</version>
</dependency>
<!-- 线程传递值 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>transmittable-thread-local</artifactId>
<version>${transmittable-thread-local.version}</version>
</dependency> </dependency>
<!-- 核心模块 --> <!-- 核心模块 -->
@ -178,13 +215,6 @@
<version>${ruoyi.version}</version> <version>${ruoyi.version}</version>
</dependency> </dependency>
<!-- 分布式事务 -->
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common-seata</artifactId>
<version>${ruoyi.version}</version>
</dependency>
<!-- 日志记录 --> <!-- 日志记录 -->
<dependency> <dependency>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
@ -239,47 +269,6 @@
</configuration> </configuration>
</plugin> </plugin>
</plugins> </plugins>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
</build> </build>
<repositories>
<repository>
<id>public</id>
<name>aliyun nexus</name>
<url>https://maven.aliyun.com/repository/public</url>
<releases>
<enabled>true</enabled>
</releases>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>public</id>
<name>aliyun nexus</name>
<url>https://maven.aliyun.com/repository/public</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</project> </project>

View File

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi</artifactId> <artifactId>ruoyi</artifactId>
<version>3.6.3</version> <version>3.1.0</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi-api</artifactId> <artifactId>ruoyi-api</artifactId>
<version>3.6.3</version> <version>3.1.0</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -27,7 +27,7 @@ public interface RemoteLogService
* @return 结果 * @return 结果
*/ */
@PostMapping("/operlog") @PostMapping("/operlog")
public R<Boolean> saveLog(@RequestBody SysOperLog sysOperLog, @RequestHeader(SecurityConstants.FROM_SOURCE) String source) throws Exception; public R<Boolean> saveLog(@RequestBody SysOperLog sysOperLog, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
/** /**
* 保存访问记录 * 保存访问记录

View File

@ -4,7 +4,6 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.validation.constraints.Email; import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size; import javax.validation.constraints.Size;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.lang3.builder.ToStringStyle;
@ -32,7 +31,7 @@ public class SysDept extends BaseEntity
private String deptName; private String deptName;
/** 显示顺序 */ /** 显示顺序 */
private Integer orderNum; private String orderNum;
/** 负责人 */ /** 负责人 */
private String leader; private String leader;
@ -97,13 +96,13 @@ public class SysDept extends BaseEntity
this.deptName = deptName; this.deptName = deptName;
} }
@NotNull(message = "显示顺序不能为空") @NotBlank(message = "显示顺序不能为空")
public Integer getOrderNum() public String getOrderNum()
{ {
return orderNum; return orderNum;
} }
public void setOrderNum(Integer orderNum) public void setOrderNum(String orderNum)
{ {
this.orderNum = orderNum; this.orderNum = orderNum;
} }

View File

@ -79,10 +79,6 @@ public class SysOperLog extends BaseEntity
@Excel(name = "操作时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") @Excel(name = "操作时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date operTime; private Date operTime;
/** 消耗时间 */
@Excel(name = "消耗时间", suffix = "毫秒")
private Long costTime;
public Long getOperId() public Long getOperId()
{ {
return operId; return operId;
@ -242,14 +238,4 @@ public class SysOperLog extends BaseEntity
{ {
this.operTime = operTime; this.operTime = operTime;
} }
public Long getCostTime()
{
return costTime;
}
public void setCostTime(Long costTime)
{
this.costTime = costTime;
}
} }

View File

@ -1,8 +1,6 @@
package com.ruoyi.system.api.domain; package com.ruoyi.system.api.domain;
import java.util.Set;
import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size; import javax.validation.constraints.Size;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.lang3.builder.ToStringStyle;
@ -33,7 +31,7 @@ public class SysRole extends BaseEntity
/** 角色排序 */ /** 角色排序 */
@Excel(name = "角色排序") @Excel(name = "角色排序")
private Integer roleSort; private String roleSort;
/** 数据范围1所有数据权限2自定义数据权限3本部门数据权限4本部门及以下数据权限5仅本人数据权限 */ /** 数据范围1所有数据权限2自定义数据权限3本部门数据权限4本部门及以下数据权限5仅本人数据权限 */
@Excel(name = "数据范围", readConverterExp = "1=所有数据权限,2=自定义数据权限,3=本部门数据权限,4=本部门及以下数据权限,5=仅本人数据权限") @Excel(name = "数据范围", readConverterExp = "1=所有数据权限,2=自定义数据权限,3=本部门数据权限,4=本部门及以下数据权限,5=仅本人数据权限")
@ -61,9 +59,6 @@ public class SysRole extends BaseEntity
/** 部门组(数据权限) */ /** 部门组(数据权限) */
private Long[] deptIds; private Long[] deptIds;
/** 角色菜单权限 */
private Set<String> permissions;
public SysRole() public SysRole()
{ {
@ -118,13 +113,13 @@ public class SysRole extends BaseEntity
this.roleKey = roleKey; this.roleKey = roleKey;
} }
@NotNull(message = "显示顺序不能为空") @NotBlank(message = "显示顺序不能为空")
public Integer getRoleSort() public String getRoleSort()
{ {
return roleSort; return roleSort;
} }
public void setRoleSort(Integer roleSort) public void setRoleSort(String roleSort)
{ {
this.roleSort = roleSort; this.roleSort = roleSort;
} }
@ -208,18 +203,7 @@ public class SysRole extends BaseEntity
{ {
this.deptIds = deptIds; this.deptIds = deptIds;
} }
public Set<String> getPermissions()
{
return permissions;
}
public void setPermissions(Set<String> permissions)
{
this.permissions = permissions;
}
@Override
public String toString() { public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("roleId", getRoleId()) .append("roleId", getRoleId())

View File

@ -2,15 +2,17 @@ package com.ruoyi.system.api.domain;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import javax.validation.constraints.*; import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.lang3.builder.ToStringStyle;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.ruoyi.common.core.annotation.Excel; import com.ruoyi.common.core.annotation.Excel;
import com.ruoyi.common.core.annotation.Excel.ColumnType; import com.ruoyi.common.core.annotation.Excel.ColumnType;
import com.ruoyi.common.core.annotation.Excel.Type; import com.ruoyi.common.core.annotation.Excel.Type;
import com.ruoyi.common.core.annotation.Excels; import com.ruoyi.common.core.annotation.Excels;
import com.ruoyi.common.core.web.domain.BaseEntity; import com.ruoyi.common.core.web.domain.BaseEntity;
import com.ruoyi.common.core.xss.Xss;
/** /**
* 用户对象 sys_user * 用户对象 sys_user
@ -129,7 +131,6 @@ public class SysUser extends BaseEntity
this.deptId = deptId; this.deptId = deptId;
} }
@Xss(message = "用户昵称不能包含脚本字符")
@Size(min = 0, max = 30, message = "用户昵称长度不能超过30个字符") @Size(min = 0, max = 30, message = "用户昵称长度不能超过30个字符")
public String getNickName() public String getNickName()
{ {
@ -141,7 +142,6 @@ public class SysUser extends BaseEntity
this.nickName = nickName; this.nickName = nickName;
} }
@Xss(message = "用户账号不能包含脚本字符")
@NotBlank(message = "用户账号不能为空") @NotBlank(message = "用户账号不能为空")
@Size(min = 0, max = 30, message = "用户账号长度不能超过30个字符") @Size(min = 0, max = 30, message = "用户账号长度不能超过30个字符")
public String getUserName() public String getUserName()
@ -197,6 +197,7 @@ public class SysUser extends BaseEntity
this.avatar = avatar; this.avatar = avatar;
} }
@JsonProperty
public String getPassword() public String getPassword()
{ {
return password; return password;

View File

@ -28,13 +28,13 @@ public class RemoteLogFallbackFactory implements FallbackFactory<RemoteLogServic
@Override @Override
public R<Boolean> saveLog(SysOperLog sysOperLog, String source) public R<Boolean> saveLog(SysOperLog sysOperLog, String source)
{ {
return R.fail("保存操作日志失败:" + throwable.getMessage()); return null;
} }
@Override @Override
public R<Boolean> saveLogininfor(SysLogininfor sysLogininfor, String source) public R<Boolean> saveLogininfor(SysLogininfor sysLogininfor, String source)
{ {
return R.fail("保存登录日志失败:" + throwable.getMessage()); return null;
} }
}; };

View File

@ -0,0 +1,4 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.ruoyi.system.api.factory.RemoteUserFallbackFactory,\
com.ruoyi.system.api.factory.RemoteLogFallbackFactory, \
com.ruoyi.system.api.factory.RemoteFileFallbackFactory

View File

@ -1,3 +0,0 @@
com.ruoyi.system.api.factory.RemoteUserFallbackFactory
com.ruoyi.system.api.factory.RemoteLogFallbackFactory
com.ruoyi.system.api.factory.RemoteFileFallbackFactory

View File

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi</artifactId> <artifactId>ruoyi</artifactId>
<version>3.6.3</version> <version>3.1.0</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -10,11 +10,8 @@ import com.ruoyi.auth.form.LoginBody;
import com.ruoyi.auth.form.RegisterBody; import com.ruoyi.auth.form.RegisterBody;
import com.ruoyi.auth.service.SysLoginService; import com.ruoyi.auth.service.SysLoginService;
import com.ruoyi.common.core.domain.R; import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.utils.JwtUtils;
import com.ruoyi.common.core.utils.StringUtils; import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.security.auth.AuthUtil;
import com.ruoyi.common.security.service.TokenService; import com.ruoyi.common.security.service.TokenService;
import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.system.api.model.LoginUser; import com.ruoyi.system.api.model.LoginUser;
/** /**
@ -43,12 +40,12 @@ public class TokenController
@DeleteMapping("logout") @DeleteMapping("logout")
public R<?> logout(HttpServletRequest request) public R<?> logout(HttpServletRequest request)
{ {
String token = SecurityUtils.getToken(request); LoginUser loginUser = tokenService.getLoginUser(request);
if (StringUtils.isNotEmpty(token)) if (StringUtils.isNotNull(loginUser))
{ {
String username = JwtUtils.getUserName(token); String username = loginUser.getUsername();
// 删除用户缓存记录 // 删除用户缓存记录
AuthUtil.logoutByToken(token); tokenService.delLoginUser(loginUser.getToken());
// 记录用户退出日志 // 记录用户退出日志
sysLoginService.logout(username); sysLoginService.logout(username);
} }

View File

@ -2,19 +2,19 @@ package com.ruoyi.auth.service;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.ruoyi.common.core.constant.CacheConstants;
import com.ruoyi.common.core.constant.Constants; import com.ruoyi.common.core.constant.Constants;
import com.ruoyi.common.core.constant.SecurityConstants; import com.ruoyi.common.core.constant.SecurityConstants;
import com.ruoyi.common.core.constant.UserConstants; import com.ruoyi.common.core.constant.UserConstants;
import com.ruoyi.common.core.domain.R; import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.enums.UserStatus; import com.ruoyi.common.core.enums.UserStatus;
import com.ruoyi.common.core.exception.ServiceException; import com.ruoyi.common.core.exception.BaseException;
import com.ruoyi.common.core.text.Convert; import com.ruoyi.common.core.utils.SecurityUtils;
import com.ruoyi.common.core.utils.ServletUtils;
import com.ruoyi.common.core.utils.StringUtils; import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.core.utils.ip.IpUtils; import com.ruoyi.common.core.utils.ip.IpUtils;
import com.ruoyi.common.redis.service.RedisService; import com.ruoyi.system.api.RemoteLogService;
import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.system.api.RemoteUserService; import com.ruoyi.system.api.RemoteUserService;
import com.ruoyi.system.api.domain.SysLogininfor;
import com.ruoyi.system.api.domain.SysUser; import com.ruoyi.system.api.domain.SysUser;
import com.ruoyi.system.api.model.LoginUser; import com.ruoyi.system.api.model.LoginUser;
@ -26,18 +26,12 @@ import com.ruoyi.system.api.model.LoginUser;
@Component @Component
public class SysLoginService public class SysLoginService
{ {
@Autowired
private RemoteLogService remoteLogService;
@Autowired @Autowired
private RemoteUserService remoteUserService; private RemoteUserService remoteUserService;
@Autowired
private SysPasswordService passwordService;
@Autowired
private SysRecordLogService recordLogService;
@Autowired
private RedisService redisService;
/** /**
* 登录 * 登录
*/ */
@ -46,64 +40,60 @@ public class SysLoginService
// 用户名或密码为空 错误 // 用户名或密码为空 错误
if (StringUtils.isAnyBlank(username, password)) if (StringUtils.isAnyBlank(username, password))
{ {
recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户/密码必须填写"); recordLogininfor(username, Constants.LOGIN_FAIL, "用户/密码必须填写");
throw new ServiceException("用户/密码必须填写"); throw new BaseException("用户/密码必须填写");
} }
// 密码如果不在指定范围内 错误 // 密码如果不在指定范围内 错误
if (password.length() < UserConstants.PASSWORD_MIN_LENGTH if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
|| password.length() > UserConstants.PASSWORD_MAX_LENGTH) || password.length() > UserConstants.PASSWORD_MAX_LENGTH)
{ {
recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户密码不在指定范围"); recordLogininfor(username, Constants.LOGIN_FAIL, "用户密码不在指定范围");
throw new ServiceException("用户密码不在指定范围"); throw new BaseException("用户密码不在指定范围");
} }
// 用户名不在指定范围内 错误 // 用户名不在指定范围内 错误
if (username.length() < UserConstants.USERNAME_MIN_LENGTH if (username.length() < UserConstants.USERNAME_MIN_LENGTH
|| username.length() > UserConstants.USERNAME_MAX_LENGTH) || username.length() > UserConstants.USERNAME_MAX_LENGTH)
{ {
recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户名不在指定范围"); recordLogininfor(username, Constants.LOGIN_FAIL, "用户名不在指定范围");
throw new ServiceException("用户名不在指定范围"); throw new BaseException("用户名不在指定范围");
}
// IP黑名单校验
String blackStr = Convert.toStr(redisService.getCacheObject(CacheConstants.SYS_LOGIN_BLACKIPLIST));
if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr()))
{
recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "很遗憾访问IP已被列入系统黑名单");
throw new ServiceException("很遗憾访问IP已被列入系统黑名单");
} }
// 查询用户信息 // 查询用户信息
R<LoginUser> userResult = remoteUserService.getUserInfo(username, SecurityConstants.INNER); R<LoginUser> userResult = remoteUserService.getUserInfo(username, SecurityConstants.INNER);
if (StringUtils.isNull(userResult) || StringUtils.isNull(userResult.getData()))
{
recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "登录用户不存在");
throw new ServiceException("登录用户:" + username + " 不存在");
}
if (R.FAIL == userResult.getCode()) if (R.FAIL == userResult.getCode())
{ {
throw new ServiceException(userResult.getMsg()); throw new BaseException(userResult.getMsg());
}
if (StringUtils.isNull(userResult) || StringUtils.isNull(userResult.getData()))
{
recordLogininfor(username, Constants.LOGIN_FAIL, "登录用户不存在");
throw new BaseException("登录用户:" + username + " 不存在");
} }
LoginUser userInfo = userResult.getData(); LoginUser userInfo = userResult.getData();
SysUser user = userResult.getData().getSysUser(); SysUser user = userResult.getData().getSysUser();
if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) if (UserStatus.DELETED.getCode().equals(user.getDelFlag()))
{ {
recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "对不起,您的账号已被删除"); recordLogininfor(username, Constants.LOGIN_FAIL, "对不起,您的账号已被删除");
throw new ServiceException("对不起,您的账号:" + username + " 已被删除"); throw new BaseException("对不起,您的账号:" + username + " 已被删除");
} }
if (UserStatus.DISABLE.getCode().equals(user.getStatus())) if (UserStatus.DISABLE.getCode().equals(user.getStatus()))
{ {
recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户已停用,请联系管理员"); recordLogininfor(username, Constants.LOGIN_FAIL, "用户已停用,请联系管理员");
throw new ServiceException("对不起,您的账号:" + username + " 已停用"); throw new BaseException("对不起,您的账号:" + username + " 已停用");
} }
passwordService.validate(user, password); if (!SecurityUtils.matchesPassword(password, user.getPassword()))
recordLogService.recordLogininfor(username, Constants.LOGIN_SUCCESS, "登录成功"); {
recordLogininfor(username, Constants.LOGIN_FAIL, "用户密码错误");
throw new BaseException("用户不存在/密码错误");
}
recordLogininfor(username, Constants.LOGIN_SUCCESS, "登录成功");
return userInfo; return userInfo;
} }
public void logout(String loginName) public void logout(String loginName)
{ {
recordLogService.recordLogininfor(loginName, Constants.LOGOUT, "退出成功"); recordLogininfor(loginName, Constants.LOGOUT, "退出成功");
} }
/** /**
@ -114,17 +104,17 @@ public class SysLoginService
// 用户名或密码为空 错误 // 用户名或密码为空 错误
if (StringUtils.isAnyBlank(username, password)) if (StringUtils.isAnyBlank(username, password))
{ {
throw new ServiceException("用户/密码必须填写"); throw new BaseException("用户/密码必须填写");
} }
if (username.length() < UserConstants.USERNAME_MIN_LENGTH if (username.length() < UserConstants.USERNAME_MIN_LENGTH
|| username.length() > UserConstants.USERNAME_MAX_LENGTH) || username.length() > UserConstants.USERNAME_MAX_LENGTH)
{ {
throw new ServiceException("账户长度必须在2到20个字符之间"); throw new BaseException("账户长度必须在2到20个字符之间");
} }
if (password.length() < UserConstants.PASSWORD_MIN_LENGTH if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
|| password.length() > UserConstants.PASSWORD_MAX_LENGTH) || password.length() > UserConstants.PASSWORD_MAX_LENGTH)
{ {
throw new ServiceException("密码长度必须在5到20个字符之间"); throw new BaseException("密码长度必须在5到20个字符之间");
} }
// 注册用户信息 // 注册用户信息
@ -136,8 +126,34 @@ public class SysLoginService
if (R.FAIL == registerResult.getCode()) if (R.FAIL == registerResult.getCode())
{ {
throw new ServiceException(registerResult.getMsg()); throw new BaseException(registerResult.getMsg());
} }
recordLogService.recordLogininfor(username, Constants.REGISTER, "注册成功"); recordLogininfor(username, Constants.REGISTER, "注册成功");
} }
}
/**
* 记录登录信息
*
* @param username 用户名
* @param status 状态
* @param message 消息内容
* @return
*/
public void recordLogininfor(String username, String status, String message)
{
SysLogininfor logininfor = new SysLogininfor();
logininfor.setUserName(username);
logininfor.setIpaddr(IpUtils.getIpAddr(ServletUtils.getRequest()));
logininfor.setMsg(message);
// 日志状态
if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER))
{
logininfor.setStatus("0");
}
else if (Constants.LOGIN_FAIL.equals(status))
{
logininfor.setStatus("1");
}
remoteLogService.saveLogininfor(logininfor, SecurityConstants.INNER);
}
}

View File

@ -1,85 +0,0 @@
package com.ruoyi.auth.service;
import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.ruoyi.common.core.constant.CacheConstants;
import com.ruoyi.common.core.constant.Constants;
import com.ruoyi.common.core.exception.ServiceException;
import com.ruoyi.common.redis.service.RedisService;
import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.system.api.domain.SysUser;
/**
* 登录密码方法
*
* @author ruoyi
*/
@Component
public class SysPasswordService
{
@Autowired
private RedisService redisService;
private int maxRetryCount = CacheConstants.PASSWORD_MAX_RETRY_COUNT;
private Long lockTime = CacheConstants.PASSWORD_LOCK_TIME;
@Autowired
private SysRecordLogService recordLogService;
/**
* 登录账户密码错误次数缓存键名
*
* @param username 用户名
* @return 缓存键key
*/
private String getCacheKey(String username)
{
return CacheConstants.PWD_ERR_CNT_KEY + username;
}
public void validate(SysUser user, String password)
{
String username = user.getUserName();
Integer retryCount = redisService.getCacheObject(getCacheKey(username));
if (retryCount == null)
{
retryCount = 0;
}
if (retryCount >= Integer.valueOf(maxRetryCount).intValue())
{
String errMsg = String.format("密码输入错误%s次帐户锁定%s分钟", maxRetryCount, lockTime);
recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL,errMsg);
throw new ServiceException(errMsg);
}
if (!matches(user, password))
{
retryCount = retryCount + 1;
recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, String.format("密码输入错误%s次", retryCount));
redisService.setCacheObject(getCacheKey(username), retryCount, lockTime, TimeUnit.MINUTES);
throw new ServiceException("用户不存在/密码错误");
}
else
{
clearLoginRecordCache(username);
}
}
public boolean matches(SysUser user, String rawPassword)
{
return SecurityUtils.matchesPassword(rawPassword, user.getPassword());
}
public void clearLoginRecordCache(String loginName)
{
if (redisService.hasKey(getCacheKey(loginName)))
{
redisService.deleteObject(getCacheKey(loginName));
}
}
}

View File

@ -1,48 +0,0 @@
package com.ruoyi.auth.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.ruoyi.common.core.constant.Constants;
import com.ruoyi.common.core.constant.SecurityConstants;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.core.utils.ip.IpUtils;
import com.ruoyi.system.api.RemoteLogService;
import com.ruoyi.system.api.domain.SysLogininfor;
/**
* 记录日志方法
*
* @author ruoyi
*/
@Component
public class SysRecordLogService
{
@Autowired
private RemoteLogService remoteLogService;
/**
* 记录登录信息
*
* @param username 用户名
* @param status 状态
* @param message 消息内容
* @return
*/
public void recordLogininfor(String username, String status, String message)
{
SysLogininfor logininfor = new SysLogininfor();
logininfor.setUserName(username);
logininfor.setIpaddr(IpUtils.getIpAddr());
logininfor.setMsg(message);
// 日志状态
if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER))
{
logininfor.setStatus(Constants.LOGIN_SUCCESS_STATUS);
}
else if (Constants.LOGIN_FAIL.equals(status))
{
logininfor.setStatus(Constants.LOGIN_FAIL_STATUS);
}
remoteLogService.saveLogininfor(logininfor, SecurityConstants.INNER);
}
}

View File

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi</artifactId> <artifactId>ruoyi</artifactId>
<version>3.6.3</version> <version>3.1.0</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@ -12,18 +12,17 @@
<module>ruoyi-common-log</module> <module>ruoyi-common-log</module>
<module>ruoyi-common-core</module> <module>ruoyi-common-core</module>
<module>ruoyi-common-redis</module> <module>ruoyi-common-redis</module>
<module>ruoyi-common-seata</module>
<module>ruoyi-common-swagger</module> <module>ruoyi-common-swagger</module>
<module>ruoyi-common-security</module> <module>ruoyi-common-security</module>
<module>ruoyi-common-datascope</module> <module>ruoyi-common-datascope</module>
<module>ruoyi-common-datasource</module> <module>ruoyi-common-datasource</module>
</modules> </modules>
<artifactId>ruoyi-common</artifactId> <artifactId>ruoyi-common</artifactId>
<packaging>pom</packaging> <packaging>pom</packaging>
<description> <description>
ruoyi-common通用模块 ruoyi-common通用模块
</description> </description>
</project> </project>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common</artifactId> <artifactId>ruoyi-common</artifactId>
<version>3.6.3</version> <version>3.1.0</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@ -41,10 +41,10 @@
<artifactId>spring-web</artifactId> <artifactId>spring-web</artifactId>
</dependency> </dependency>
<!-- Transmittable ThreadLocal --> <!-- Apache Commons Pool2 -->
<dependency> <dependency>
<groupId>com.alibaba</groupId> <groupId>org.apache.commons</groupId>
<artifactId>transmittable-thread-local</artifactId> <artifactId>commons-pool2</artifactId>
</dependency> </dependency>
<!-- Pagehelper --> <!-- Pagehelper -->
@ -67,20 +67,8 @@
<!-- Alibaba Fastjson --> <!-- Alibaba Fastjson -->
<dependency> <dependency>
<groupId>com.alibaba.fastjson2</groupId> <groupId>com.alibaba</groupId>
<artifactId>fastjson2</artifactId> <artifactId>fastjson</artifactId>
</dependency>
<!-- Jwt -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
</dependency>
<!-- Jaxb -->
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
</dependency> </dependency>
<!-- Apache Lang3 --> <!-- Apache Lang3 -->
@ -95,6 +83,12 @@
<artifactId>commons-io</artifactId> <artifactId>commons-io</artifactId>
</dependency> </dependency>
<!-- Commons Fileupload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
</dependency>
<!-- excel工具 --> <!-- excel工具 -->
<dependency> <dependency>
<groupId>org.apache.poi</groupId> <groupId>org.apache.poi</groupId>

View File

@ -5,9 +5,6 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
import java.math.BigDecimal; import java.math.BigDecimal;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import com.ruoyi.common.core.utils.poi.ExcelHandlerAdapter;
/** /**
* 自定义导出Excel数据注解 * 自定义导出Excel数据注解
@ -54,12 +51,17 @@ public @interface Excel
public int roundingMode() default BigDecimal.ROUND_HALF_EVEN; public int roundingMode() default BigDecimal.ROUND_HALF_EVEN;
/** /**
* 导出时在excel中每个列的高度 * 导出类型0数字 1字符串
*/
public ColumnType cellType() default ColumnType.STRING;
/**
* 导出时在excel中每个列的高度 单位为字符
*/ */
public double height() default 14; public double height() default 14;
/** /**
* 导出时在excel中每个列的宽度 * 导出时在excel中每个列的宽 单位为字符
*/ */
public double width() default 16; public double width() default 16;
@ -83,11 +85,6 @@ public @interface Excel
*/ */
public String[] combo() default {}; public String[] combo() default {};
/**
* 是否需要纵向合并单元格,应对需求:含有list集合单元格)
*/
public boolean needMerge() default false;
/** /**
* 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写. * 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写.
*/ */
@ -104,44 +101,25 @@ public @interface Excel
public boolean isStatistics() default false; public boolean isStatistics() default false;
/** /**
* 导出类型0数字 1字符串 * 导出字段对齐方式0默认1靠左2居中3靠右
*/ */
public ColumnType cellType() default ColumnType.STRING; Align align() default Align.AUTO;
/** public enum Align
* 导出列头背景颜色 {
*/ AUTO(0), LEFT(1), CENTER(2), RIGHT(3);
public IndexedColors headerBackgroundColor() default IndexedColors.GREY_50_PERCENT; private final int value;
/** Align(int value)
* 导出列头字体颜色 {
*/ this.value = value;
public IndexedColors headerColor() default IndexedColors.WHITE; }
/** public int value()
* 导出单元格背景颜色 {
*/ return this.value;
public IndexedColors backgroundColor() default IndexedColors.WHITE; }
}
/**
* 导出单元格字体颜色
*/
public IndexedColors color() default IndexedColors.BLACK;
/**
* 导出字段对齐方式
*/
public HorizontalAlignment align() default HorizontalAlignment.CENTER;
/**
* 自定义数据处理器
*/
public Class<?> handler() default ExcelHandlerAdapter.class;
/**
* 自定义数据处理器参数
*/
public String[] args() default {};
/** /**
* 字段类型0导出导入1仅导出2仅导入 * 字段类型0导出导入1仅导出2仅导入

View File

@ -1,59 +1,14 @@
package com.ruoyi.common.core.constant; package com.ruoyi.common.core.constant;
/** /**
* 缓存常量信息 * 缓存的key 常量
* *
* @author ruoyi * @author ruoyi
*/ */
public class CacheConstants public class CacheConstants
{ {
/**
* 缓存有效期默认720分钟
*/
public final static long EXPIRATION = 720;
/**
* 缓存刷新时间默认120分钟
*/
public final static long REFRESH_TIME = 120;
/**
* 密码最大错误次数
*/
public final static int PASSWORD_MAX_RETRY_COUNT = 5;
/**
* 密码锁定时间默认10分钟
*/
public final static long PASSWORD_LOCK_TIME = 10;
/** /**
* 权限缓存前缀 * 权限缓存前缀
*/ */
public final static String LOGIN_TOKEN_KEY = "login_tokens:"; public final static String LOGIN_TOKEN_KEY = "login_tokens:";
/**
* 验证码 redis key
*/
public static final String CAPTCHA_CODE_KEY = "captcha_codes:";
/**
* 参数管理 cache key
*/
public static final String SYS_CONFIG_KEY = "sys_config:";
/**
* 字典管理 cache key
*/
public static final String SYS_DICT_KEY = "sys_dict:";
/**
* 登录账户密码错误次数 redis key
*/
public static final String PWD_ERR_CNT_KEY = "pwd_err_cnt:";
/**
* 登录IP黑名单 cache key
*/
public static final String SYS_LOGIN_BLACKIPLIST = SYS_CONFIG_KEY + "sys.login.blackIPList";
} }

View File

@ -17,25 +17,10 @@ public class Constants
*/ */
public static final String GBK = "GBK"; public static final String GBK = "GBK";
/**
* www主域
*/
public static final String WWW = "www.";
/** /**
* RMI 远程方法调用 * RMI 远程方法调用
*/ */
public static final String LOOKUP_RMI = "rmi:"; public static final String LOOKUP_RMI = "rmi://";
/**
* LDAP 远程方法调用
*/
public static final String LOOKUP_LDAP = "ldap:";
/**
* LDAPS 远程方法调用
*/
public static final String LOOKUP_LDAPS = "ldaps:";
/** /**
* http请求 * http请求
@ -57,16 +42,6 @@ public class Constants
*/ */
public static final Integer FAIL = 500; public static final Integer FAIL = 500;
/**
* 登录成功状态
*/
public static final String LOGIN_SUCCESS_STATUS = "0";
/**
* 登录失败状态
*/
public static final String LOGIN_FAIL_STATUS = "1";
/** /**
* 登录成功 * 登录成功
*/ */
@ -107,29 +82,33 @@ public class Constants
*/ */
public static final String IS_ASC = "isAsc"; public static final String IS_ASC = "isAsc";
/**
* 验证码 redis key
*/
public static final String CAPTCHA_CODE_KEY = "captcha_codes:";
/** /**
* 验证码有效期分钟 * 验证码有效期分钟
*/ */
public static final long CAPTCHA_EXPIRATION = 2; public static final long CAPTCHA_EXPIRATION = 2;
/**
* 令牌有效期分钟
*/
public final static long TOKEN_EXPIRE = 720;
/**
* 参数管理 cache key
*/
public static final String SYS_CONFIG_KEY = "sys_config:";
/**
* 字典管理 cache key
*/
public static final String SYS_DICT_KEY = "sys_dict:";
/** /**
* 资源映射路径 前缀 * 资源映射路径 前缀
*/ */
public static final String RESOURCE_PREFIX = "/profile"; public static final String RESOURCE_PREFIX = "/profile";
/**
* 自动识别json对象白名单配置仅允许解析的包名范围越小越安全
*/
public static final String[] JSON_WHITELIST_STR = { "org.springframework", "com.ruoyi" };
/**
* 定时任务白名单配置仅允许访问的包名如其他需要可以自行添加
*/
public static final String[] JOB_WHITELIST_STR = { "com.ruoyi" };
/**
* 定时任务违规的字符
*/
public static final String[] JOB_ERROR_STR = { "java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml",
"org.springframework", "org.apache", "com.ruoyi.common.core.utils.file" };
} }

View File

@ -109,9 +109,6 @@ public class GenConstants
/** 模糊查询 */ /** 模糊查询 */
public static final String QUERY_LIKE = "LIKE"; public static final String QUERY_LIKE = "LIKE";
/** 相等查询 */
public static final String QUERY_EQ = "EQ";
/** 需要 */ /** 需要 */
public static final String REQUIRE = "1"; public static final String REQUIRE = "1";
} }

View File

@ -86,9 +86,4 @@ public class HttpStatus
* 接口未实现 * 接口未实现
*/ */
public static final int NOT_IMPLEMENTED = 501; public static final int NOT_IMPLEMENTED = 501;
/**
* 系统警告消息
*/
public static final int WARN = 601;
} }

View File

@ -7,6 +7,16 @@ package com.ruoyi.common.core.constant;
*/ */
public class SecurityConstants public class SecurityConstants
{ {
/**
* 令牌自定义标识
*/
public static final String TOKEN_AUTHENTICATION = "Authorization";
/**
* 令牌前缀
*/
public static final String TOKEN_PREFIX = "Bearer ";
/** /**
* 用户ID字段 * 用户ID字段
*/ */
@ -31,19 +41,4 @@ public class SecurityConstants
* 内部请求 * 内部请求
*/ */
public static final String INNER = "inner"; public static final String INNER = "inner";
/**
* 用户标识
*/
public static final String USER_KEY = "user_key";
/**
* 登录用户
*/
public static final String LOGIN_USER = "login_user";
/**
* 角色权限
*/
public static final String ROLE_PERMISSION = "role_permission";
} }

View File

@ -1,25 +0,0 @@
package com.ruoyi.common.core.constant;
/**
* Token的Key常量
*
* @author ruoyi
*/
public class TokenConstants
{
/**
* 令牌自定义标识
*/
public static final String AUTHENTICATION = "Authorization";
/**
* 令牌前缀
*/
public static final String PREFIX = "Bearer ";
/**
* 令牌秘钥
*/
public final static String SECRET = "abcdefghijklmnopqrstuvwxyz";
}

View File

@ -60,9 +60,10 @@ public class UserConstants
/** InnerLink组件标识 */ /** InnerLink组件标识 */
public final static String INNER_LINK = "InnerLink"; public final static String INNER_LINK = "InnerLink";
/** 校验是否唯一的返回标识 */ /** 校验返回结果码 */
public final static boolean UNIQUE = true; public final static String UNIQUE = "0";
public final static boolean NOT_UNIQUE = false;
public final static String NOT_UNIQUE = "1";
/** /**
* 用户名长度限制 * 用户名长度限制

View File

@ -1,98 +0,0 @@
package com.ruoyi.common.core.context;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import com.alibaba.ttl.TransmittableThreadLocal;
import com.ruoyi.common.core.constant.SecurityConstants;
import com.ruoyi.common.core.text.Convert;
import com.ruoyi.common.core.utils.StringUtils;
/**
* 获取当前线程变量中的 用户id用户名称Token等信息
* 注意 必须在网关通过请求头的方法传入同时在HeaderInterceptor拦截器设置值 否则这里无法获取
*
* @author ruoyi
*/
public class SecurityContextHolder
{
private static final TransmittableThreadLocal<Map<String, Object>> THREAD_LOCAL = new TransmittableThreadLocal<>();
public static void set(String key, Object value)
{
Map<String, Object> map = getLocalMap();
map.put(key, value == null ? StringUtils.EMPTY : value);
}
public static String get(String key)
{
Map<String, Object> map = getLocalMap();
return Convert.toStr(map.getOrDefault(key, StringUtils.EMPTY));
}
public static <T> T get(String key, Class<T> clazz)
{
Map<String, Object> map = getLocalMap();
return StringUtils.cast(map.getOrDefault(key, null));
}
public static Map<String, Object> getLocalMap()
{
Map<String, Object> map = THREAD_LOCAL.get();
if (map == null)
{
map = new ConcurrentHashMap<String, Object>();
THREAD_LOCAL.set(map);
}
return map;
}
public static void setLocalMap(Map<String, Object> threadLocalMap)
{
THREAD_LOCAL.set(threadLocalMap);
}
public static Long getUserId()
{
return Convert.toLong(get(SecurityConstants.DETAILS_USER_ID), 0L);
}
public static void setUserId(String account)
{
set(SecurityConstants.DETAILS_USER_ID, account);
}
public static String getUserName()
{
return get(SecurityConstants.DETAILS_USERNAME);
}
public static void setUserName(String username)
{
set(SecurityConstants.DETAILS_USERNAME, username);
}
public static String getUserKey()
{
return get(SecurityConstants.USER_KEY);
}
public static void setUserKey(String userKey)
{
set(SecurityConstants.USER_KEY, userKey);
}
public static String getPermission()
{
return get(SecurityConstants.ROLE_PERMISSION);
}
public static void setPermission(String permissions)
{
set(SecurityConstants.ROLE_PERMISSION, permissions);
}
public static void remove()
{
THREAD_LOCAL.remove();
}
}

View File

@ -102,14 +102,4 @@ public class R<T> implements Serializable
{ {
this.data = data; this.data = data;
} }
public static <T> Boolean isError(R<T> ret)
{
return !isSuccess(ret);
}
public static <T> Boolean isSuccess(R<T> ret)
{
return R.SUCCESS == ret.getCode();
}
} }

View File

@ -1,79 +1,79 @@
package com.ruoyi.common.core.exception.base; package com.ruoyi.common.core.exception;
/** /**
* 基础异常 * 基础异常
* *
* @author ruoyi * @author ruoyi
*/ */
public class BaseException extends RuntimeException public class BaseException extends RuntimeException
{ {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
* 所属模块 * 所属模块
*/ */
private String module; private String module;
/** /**
* 错误码 * 错误码
*/ */
private String code; private String code;
/** /**
* 错误码对应的参数 * 错误码对应的参数
*/ */
private Object[] args; private Object[] args;
/** /**
* 错误消息 * 错误消息
*/ */
private String defaultMessage; private String defaultMessage;
public BaseException(String module, String code, Object[] args, String defaultMessage) public BaseException(String module, String code, Object[] args, String defaultMessage)
{ {
this.module = module; this.module = module;
this.code = code; this.code = code;
this.args = args; this.args = args;
this.defaultMessage = defaultMessage; this.defaultMessage = defaultMessage;
} }
public BaseException(String module, String code, Object[] args) public BaseException(String module, String code, Object[] args)
{ {
this(module, code, args, null); this(module, code, args, null);
} }
public BaseException(String module, String defaultMessage) public BaseException(String module, String defaultMessage)
{ {
this(module, null, null, defaultMessage); this(module, null, null, defaultMessage);
} }
public BaseException(String code, Object[] args) public BaseException(String code, Object[] args)
{ {
this(null, code, args, null); this(null, code, args, null);
} }
public BaseException(String defaultMessage) public BaseException(String defaultMessage)
{ {
this(null, null, null, defaultMessage); this(null, null, null, defaultMessage);
} }
public String getModule() public String getModule()
{ {
return module; return module;
} }
public String getCode() public String getCode()
{ {
return code; return code;
} }
public Object[] getArgs() public Object[] getArgs()
{ {
return args; return args;
} }
public String getDefaultMessage() public String getDefaultMessage()
{ {
return defaultMessage; return defaultMessage;
} }
} }

View File

@ -0,0 +1,43 @@
package com.ruoyi.common.core.exception;
/**
* 自定义异常
*
* @author ruoyi
*/
public class CustomException extends RuntimeException
{
private static final long serialVersionUID = 1L;
private Integer code;
private String message;
public CustomException(String message)
{
this.message = message;
}
public CustomException(String message, Integer code)
{
this.message = message;
this.code = code;
}
public CustomException(String message, Throwable e)
{
super(message, e);
this.message = message;
}
@Override
public String getMessage()
{
return message;
}
public Integer getCode()
{
return code;
}
}

View File

@ -1,58 +0,0 @@
package com.ruoyi.common.core.exception;
/**
* 全局异常
*
* @author ruoyi
*/
public class GlobalException extends RuntimeException
{
private static final long serialVersionUID = 1L;
/**
* 错误提示
*/
private String message;
/**
* 错误明细内部调试错误
*
* {@link CommonResult#getDetailMessage()} 一致的设计
*/
private String detailMessage;
/**
* 空构造方法避免反序列化问题
*/
public GlobalException()
{
}
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;
}
}

View File

@ -1,74 +0,0 @@
package com.ruoyi.common.core.exception;
/**
* 业务异常
*
* @author ruoyi
*/
public final class ServiceException extends RuntimeException
{
private static final long serialVersionUID = 1L;
/**
* 错误码
*/
private Integer code;
/**
* 错误提示
*/
private String message;
/**
* 错误明细内部调试错误
*
* {@link CommonResult#getDetailMessage()} 一致的设计
*/
private String detailMessage;
/**
* 空构造方法避免反序列化问题
*/
public ServiceException()
{
}
public ServiceException(String message)
{
this.message = message;
}
public ServiceException(String message, Integer code)
{
this.message = message;
this.code = code;
}
public String getDetailMessage()
{
return detailMessage;
}
@Override
public String getMessage()
{
return message;
}
public Integer getCode()
{
return code;
}
public ServiceException setMessage(String message)
{
this.message = message;
return this;
}
public ServiceException setDetailMessage(String detailMessage)
{
this.detailMessage = detailMessage;
return this;
}
}

View File

@ -1,16 +0,0 @@
package com.ruoyi.common.core.exception.auth;
/**
* 未能通过的登录认证异常
*
* @author ruoyi
*/
public class NotLoginException extends RuntimeException
{
private static final long serialVersionUID = 1L;
public NotLoginException(String message)
{
super(message);
}
}

View File

@ -1,23 +0,0 @@
package com.ruoyi.common.core.exception.auth;
import org.apache.commons.lang3.StringUtils;
/**
* 未能通过的权限认证异常
*
* @author ruoyi
*/
public class NotPermissionException extends RuntimeException
{
private static final long serialVersionUID = 1L;
public NotPermissionException(String permission)
{
super(permission);
}
public NotPermissionException(String[] permissions)
{
super(StringUtils.join(permissions, ","));
}
}

View File

@ -1,23 +0,0 @@
package com.ruoyi.common.core.exception.auth;
import org.apache.commons.lang3.StringUtils;
/**
* 未能通过的角色认证异常
*
* @author ruoyi
*/
public class NotRoleException extends RuntimeException
{
private static final long serialVersionUID = 1L;
public NotRoleException(String role)
{
super(role);
}
public NotRoleException(String[] roles)
{
super(StringUtils.join(roles, ","));
}
}

View File

@ -1,6 +1,6 @@
package com.ruoyi.common.core.exception.file; package com.ruoyi.common.core.exception.file;
import com.ruoyi.common.core.exception.base.BaseException; import com.ruoyi.common.core.exception.BaseException;
/** /**
* 文件信息异常类 * 文件信息异常类
@ -11,9 +11,9 @@ public class FileException extends BaseException
{ {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
public FileException(String code, Object[] args, String msg) public FileException(String code, Object[] args)
{ {
super("file", code, args, msg); super("file", code, args, null);
} }
} }

View File

@ -11,6 +11,6 @@ public class FileNameLengthLimitExceededException extends FileException
public FileNameLengthLimitExceededException(int defaultFileNameLength) public FileNameLengthLimitExceededException(int defaultFileNameLength)
{ {
super("upload.filename.exceed.length", new Object[] { defaultFileNameLength }, "the filename is too long"); super("upload.filename.exceed.length", new Object[] { defaultFileNameLength });
} }
} }

View File

@ -11,6 +11,6 @@ public class FileSizeLimitExceededException extends FileException
public FileSizeLimitExceededException(long defaultMaxSize) public FileSizeLimitExceededException(long defaultMaxSize)
{ {
super("upload.exceed.maxSize", new Object[] { defaultMaxSize }, "the filesize is too large"); super("upload.exceed.maxSize", new Object[] { defaultMaxSize });
} }
} }

View File

@ -1,61 +0,0 @@
package com.ruoyi.common.core.exception.file;
import java.io.PrintStream;
import java.io.PrintWriter;
/**
* 文件上传异常类
*
* @author ruoyi
*/
public class FileUploadException extends Exception
{
private static final long serialVersionUID = 1L;
private final Throwable cause;
public FileUploadException()
{
this(null, null);
}
public FileUploadException(final String msg)
{
this(msg, null);
}
public FileUploadException(String msg, Throwable cause)
{
super(msg);
this.cause = cause;
}
@Override
public void printStackTrace(PrintStream stream)
{
super.printStackTrace(stream);
if (cause != null)
{
stream.println("Caused by:");
cause.printStackTrace(stream);
}
}
@Override
public void printStackTrace(PrintWriter writer)
{
super.printStackTrace(writer);
if (cause != null)
{
writer.println("Caused by:");
cause.printStackTrace(writer);
}
}
@Override
public Throwable getCause()
{
return cause;
}
}

View File

@ -1,6 +1,7 @@
package com.ruoyi.common.core.exception.file; package com.ruoyi.common.core.exception.file;
import java.util.Arrays; import java.util.Arrays;
import org.apache.commons.fileupload.FileUploadException;
/** /**
* 文件上传 误异常类 * 文件上传 误异常类

View File

@ -1,6 +1,6 @@
package com.ruoyi.common.core.exception.user; package com.ruoyi.common.core.exception.user;
import com.ruoyi.common.core.exception.base.BaseException; import com.ruoyi.common.core.exception.BaseException;
/** /**
* 用户信息异常类 * 用户信息异常类

View File

@ -2,7 +2,6 @@ package com.ruoyi.common.core.text;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.BigInteger; import java.math.BigInteger;
import java.math.RoundingMode;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.text.NumberFormat; import java.text.NumberFormat;
@ -314,7 +313,7 @@ public class Convert
* 转换为Integer数组<br> * 转换为Integer数组<br>
* *
* @param split 分隔符 * @param split 分隔符
* @param str 被转换的值 * @param split 被转换的值
* @return 结果 * @return 结果
*/ */
public static Integer[] toIntArray(String split, String str) public static Integer[] toIntArray(String split, String str)
@ -371,7 +370,7 @@ public class Convert
* 转换为String数组<br> * 转换为String数组<br>
* *
* @param split 分隔符 * @param split 分隔符
* @param str 被转换的值 * @param split 被转换的值
* @return 结果 * @return 结果
*/ */
public static String[] toStrArray(String split, String str) public static String[] toStrArray(String split, String str)
@ -562,12 +561,17 @@ public class Convert
switch (valueStr) switch (valueStr)
{ {
case "true": case "true":
case "yes":
case "ok":
case "1":
return true; return true;
case "false": case "false":
return false;
case "yes":
return true;
case "ok":
return true;
case "no": case "no":
return false;
case "1":
return true;
case "0": case "0":
return false; return false;
default: default:
@ -713,7 +717,7 @@ public class Convert
} }
if (value instanceof Double) if (value instanceof Double)
{ {
return BigDecimal.valueOf((Double) value); return new BigDecimal((Double) value);
} }
if (value instanceof Integer) if (value instanceof Integer)
{ {
@ -903,7 +907,7 @@ public class Convert
*/ */
public static String toSBC(String input, Set<Character> notConvertSet) public static String toSBC(String input, Set<Character> notConvertSet)
{ {
char[] c = input.toCharArray(); char c[] = input.toCharArray();
for (int i = 0; i < c.length; i++) for (int i = 0; i < c.length; i++)
{ {
if (null != notConvertSet && notConvertSet.contains(c[i])) if (null != notConvertSet && notConvertSet.contains(c[i]))
@ -945,7 +949,7 @@ public class Convert
*/ */
public static String toDBC(String text, Set<Character> notConvertSet) public static String toDBC(String text, Set<Character> notConvertSet)
{ {
char[] c = text.toCharArray(); char c[] = text.toCharArray();
for (int i = 0; i < c.length; i++) for (int i = 0; i < c.length; i++)
{ {
if (null != notConvertSet && notConvertSet.contains(c[i])) if (null != notConvertSet && notConvertSet.contains(c[i]))
@ -963,7 +967,9 @@ public class Convert
c[i] = (char) (c[i] - 65248); c[i] = (char) (c[i] - 65248);
} }
} }
return new String(c); String returnString = new String(c);
return returnString;
} }
/** /**
@ -984,12 +990,7 @@ public class Convert
String s = ""; String s = "";
for (int i = 0; i < fraction.length; i++) for (int i = 0; i < fraction.length; i++)
{ {
// 优化double计算精度丢失问题 s += (digit[(int) (Math.floor(n * 10 * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", "");
BigDecimal nNum = new BigDecimal(n);
BigDecimal decimal = new BigDecimal(10);
BigDecimal scale = nNum.multiply(decimal).setScale(2, RoundingMode.HALF_EVEN);
double d = scale.doubleValue();
s += (digit[(int) (Math.floor(d * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", "");
} }
if (s.length() < 1) if (s.length() < 1)
{ {

View File

@ -1,4 +1,4 @@
package com.ruoyi.common.core.utils.uuid; package com.ruoyi.common.core.text;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
@ -66,7 +66,7 @@ public final class UUID implements java.io.Serializable, Comparable<UUID>
} }
/** /**
* 获取类型 4伪随机生成的UUID 的静态工厂 * 获取类型 4伪随机生成的UUID 的静态工厂 使用加密的本地线程伪随机数生成器生成该 UUID
* *
* @return 随机生成的 {@code UUID} * @return 随机生成的 {@code UUID}
*/ */

View File

@ -3,17 +3,12 @@ package com.ruoyi.common.core.utils;
import java.lang.management.ManagementFactory; import java.lang.management.ManagementFactory;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Date; import java.util.Date;
import org.apache.commons.lang3.time.DateFormatUtils; import org.apache.commons.lang3.time.DateFormatUtils;
/** /**
* 时间工具类 * 时间工具类
* *
* @author ruoyi * @author ruoyi
*/ */
public class DateUtils extends org.apache.commons.lang3.time.DateUtils public class DateUtils extends org.apache.commons.lang3.time.DateUtils
@ -27,15 +22,15 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss"; public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss";
public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss"; public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
private static String[] parsePatterns = { private static String[] parsePatterns = {
"yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM", "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM",
"yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM", "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM",
"yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"}; "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"};
/** /**
* 获取当前Date型日期 * 获取当前Date型日期
* *
* @return Date() 当前日期 * @return Date() 当前日期
*/ */
public static Date getNowDate() public static Date getNowDate()
@ -45,7 +40,7 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
/** /**
* 获取当前日期, 默认格式为yyyy-MM-dd * 获取当前日期, 默认格式为yyyy-MM-dd
* *
* @return String * @return String
*/ */
public static String getDate() public static String getDate()
@ -126,7 +121,7 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
return null; return null;
} }
} }
/** /**
* 获取服务器启动时间 * 获取服务器启动时间
*/ */
@ -137,20 +132,16 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
} }
/** /**
* 计算时间差 * 计算两个时间差
*
* @param endDate 最后时间
* @param startTime 开始时间
* @return 时间差/小时/分钟
*/ */
public static String timeDistance(Date endDate, Date startTime) public static String getDatePoor(Date endDate, Date nowDate)
{ {
long nd = 1000 * 24 * 60 * 60; long nd = 1000 * 24 * 60 * 60;
long nh = 1000 * 60 * 60; long nh = 1000 * 60 * 60;
long nm = 1000 * 60; long nm = 1000 * 60;
// long ns = 1000; // long ns = 1000;
// 获得两个时间的毫秒时间差异 // 获得两个时间的毫秒时间差异
long diff = endDate.getTime() - startTime.getTime(); long diff = endDate.getTime() - nowDate.getTime();
// 计算差多少天 // 计算差多少天
long day = diff / nd; long day = diff / nd;
// 计算差多少小时 // 计算差多少小时
@ -161,23 +152,4 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
// long sec = diff % nd % nh % nm / ns; // long sec = diff % nd % nh % nm / ns;
return day + "" + hour + "小时" + min + "分钟"; return day + "" + hour + "小时" + min + "分钟";
} }
/**
* 增加 LocalDateTime ==> Date
*/
public static Date toDate(LocalDateTime temporalAccessor)
{
ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault());
return Date.from(zdt.toInstant());
}
/**
* 增加 LocalDate ==> Date
*/
public static Date toDate(LocalDate temporalAccessor)
{
LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0));
ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault());
return Date.from(zdt.toInstant());
}
} }

View File

@ -18,7 +18,8 @@ public class ExceptionUtil
{ {
StringWriter sw = new StringWriter(); StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw, true)); e.printStackTrace(new PrintWriter(sw, true));
return sw.toString(); String str = sw.toString();
return str;
} }
public static String getRootErrorMessage(Exception e) public static String getRootErrorMessage(Exception e)

View File

@ -1,4 +1,6 @@
package com.ruoyi.common.core.utils.uuid; package com.ruoyi.common.core.utils;
import com.ruoyi.common.core.text.UUID;
/** /**
* ID生成器工具类 * ID生成器工具类

View File

@ -1,123 +0,0 @@
package com.ruoyi.common.core.utils;
import java.util.Map;
import com.ruoyi.common.core.constant.SecurityConstants;
import com.ruoyi.common.core.constant.TokenConstants;
import com.ruoyi.common.core.text.Convert;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
/**
* Jwt工具类
*
* @author ruoyi
*/
public class JwtUtils
{
public static String secret = TokenConstants.SECRET;
/**
* 从数据声明生成令牌
*
* @param claims 数据声明
* @return 令牌
*/
public static String createToken(Map<String, Object> claims)
{
String token = Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS512, secret).compact();
return token;
}
/**
* 从令牌中获取数据声明
*
* @param token 令牌
* @return 数据声明
*/
public static Claims parseToken(String token)
{
return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
}
/**
* 根据令牌获取用户标识
*
* @param token 令牌
* @return 用户ID
*/
public static String getUserKey(String token)
{
Claims claims = parseToken(token);
return getValue(claims, SecurityConstants.USER_KEY);
}
/**
* 根据令牌获取用户标识
*
* @param claims 身份信息
* @return 用户ID
*/
public static String getUserKey(Claims claims)
{
return getValue(claims, SecurityConstants.USER_KEY);
}
/**
* 根据令牌获取用户ID
*
* @param token 令牌
* @return 用户ID
*/
public static String getUserId(String token)
{
Claims claims = parseToken(token);
return getValue(claims, SecurityConstants.DETAILS_USER_ID);
}
/**
* 根据身份信息获取用户ID
*
* @param claims 身份信息
* @return 用户ID
*/
public static String getUserId(Claims claims)
{
return getValue(claims, SecurityConstants.DETAILS_USER_ID);
}
/**
* 根据令牌获取用户名
*
* @param token 令牌
* @return 用户名
*/
public static String getUserName(String token)
{
Claims claims = parseToken(token);
return getValue(claims, SecurityConstants.DETAILS_USERNAME);
}
/**
* 根据身份信息获取用户名
*
* @param claims 身份信息
* @return 用户名
*/
public static String getUserName(Claims claims)
{
return getValue(claims, SecurityConstants.DETAILS_USERNAME);
}
/**
* 根据身份信息获取键值
*
* @param claims 身份信息
* @param key
* @return
*/
public static String getValue(Claims claims, String key)
{
return Convert.toStr(claims.get(key), "");
}
}

View File

@ -1,35 +0,0 @@
package com.ruoyi.common.core.utils;
import com.github.pagehelper.PageHelper;
import com.ruoyi.common.core.utils.sql.SqlUtil;
import com.ruoyi.common.core.web.page.PageDomain;
import com.ruoyi.common.core.web.page.TableSupport;
/**
* 分页工具类
*
* @author ruoyi
*/
public class PageUtils extends PageHelper
{
/**
* 设置请求分页数据
*/
public static void startPage()
{
PageDomain pageDomain = TableSupport.buildPageRequest();
Integer pageNum = pageDomain.getPageNum();
Integer pageSize = pageDomain.getPageSize();
String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy());
Boolean reasonable = pageDomain.getReasonable();
PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable);
}
/**
* 清理分页的线程变量
*/
public static void clearPage()
{
PageHelper.clearPage();
}
}

View File

@ -0,0 +1,164 @@
package com.ruoyi.common.core.utils;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.ruoyi.common.core.text.Convert;
import com.ruoyi.common.core.utils.StringUtils;
public class ReUtil
{
public final static Pattern GROUP_VAR = Pattern.compile("\\$(\\d+)");
/**
* 正则中需要被转义的关键字
*/
public final static Set<Character> RE_KEYS = new HashSet<>(
Arrays.asList('$', '(', ')', '*', '+', '.', '[', ']', '?', '\\', '^', '{', '}', '|'));;
/**
* 正则替换指定值<br>
* 通过正则查找到字符串然后把匹配到的字符串加入到replacementTemplate中$1表示分组1的字符串
*
* <p>
* 例如原字符串是中文1234我想把1234换成(1234)则可以
*
* <pre>
* ReUtil.replaceAll("中文1234", "(\\d+)", "($1)"))
*
* 结果中文(1234)
* </pre>
*
* @param content 文本
* @param regex 正则
* @param replacementTemplate 替换的文本模板可以使用$1类似的变量提取正则匹配出的内容
* @return 处理后的文本
*/
public static String replaceAll(CharSequence content, String regex, String replacementTemplate)
{
final Pattern pattern = Pattern.compile(regex, Pattern.DOTALL);
return replaceAll(content, pattern, replacementTemplate);
}
/**
* 正则替换指定值<br>
* 通过正则查找到字符串然后把匹配到的字符串加入到replacementTemplate中$1表示分组1的字符串
*
* @param content 文本
* @param pattern {@link Pattern}
* @param replacementTemplate 替换的文本模板可以使用$1类似的变量提取正则匹配出的内容
* @return 处理后的文本
* @since 3.0.4
*/
public static String replaceAll(CharSequence content, Pattern pattern, String replacementTemplate)
{
if (StringUtils.isEmpty(content))
{
return StringUtils.EMPTY;
}
final Matcher matcher = pattern.matcher(content);
boolean result = matcher.find();
if (result)
{
final Set<String> varNums = findAll(GROUP_VAR, replacementTemplate, 1, new HashSet<>());
final StringBuffer sb = new StringBuffer();
do
{
String replacement = replacementTemplate;
for (String var : varNums)
{
int group = Integer.parseInt(var);
replacement = replacement.replace("$" + var, matcher.group(group));
}
matcher.appendReplacement(sb, escape(replacement));
result = matcher.find();
}
while (result);
matcher.appendTail(sb);
return sb.toString();
}
return Convert.toStr(content);
}
/**
* 取得内容中匹配的所有结果
*
* @param <T> 集合类型
* @param pattern 编译后的正则模式
* @param content 被查找的内容
* @param group 正则的分组
* @param collection 返回的集合类型
* @return 结果集
*/
public static <T extends Collection<String>> T findAll(Pattern pattern, CharSequence content, int group,
T collection)
{
if (null == pattern || null == content)
{
return null;
}
if (null == collection)
{
throw new NullPointerException("Null collection param provided!");
}
final Matcher matcher = pattern.matcher(content);
while (matcher.find())
{
collection.add(matcher.group(group));
}
return collection;
}
/**
* 转义字符将正则的关键字转义
*
* @param c 字符
* @return 转义后的文本
*/
public static String escape(char c)
{
final StringBuilder builder = new StringBuilder();
if (RE_KEYS.contains(c))
{
builder.append('\\');
}
builder.append(c);
return builder.toString();
}
/**
* 转义字符串将正则的关键字转义
*
* @param content 文本
* @return 转义后的文本
*/
public static String escape(CharSequence content)
{
if (StringUtils.isBlank(content))
{
return StringUtils.EMPTY;
}
final StringBuilder builder = new StringBuilder();
int len = content.length();
char current;
for (int i = 0; i < len; i++)
{
current = content.charAt(i);
if (RE_KEYS.contains(current))
{
builder.append('\\');
}
builder.append(current);
}
return builder.toString();
}
}

View File

@ -1,13 +1,9 @@
package com.ruoyi.common.security.utils; package com.ruoyi.common.core.utils;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import com.ruoyi.common.core.constant.SecurityConstants; import com.ruoyi.common.core.constant.SecurityConstants;
import com.ruoyi.common.core.constant.TokenConstants; import com.ruoyi.common.core.text.Convert;
import com.ruoyi.common.core.context.SecurityContextHolder;
import com.ruoyi.common.core.utils.ServletUtils;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.system.api.model.LoginUser;
/** /**
* 权限获取工具类 * 权限获取工具类
@ -16,36 +12,21 @@ import com.ruoyi.system.api.model.LoginUser;
*/ */
public class SecurityUtils public class SecurityUtils
{ {
/**
* 获取用户
*/
public static String getUsername()
{
String username = ServletUtils.getRequest().getHeader(SecurityConstants.DETAILS_USERNAME);
return ServletUtils.urlDecode(username);
}
/** /**
* 获取用户ID * 获取用户ID
*/ */
public static Long getUserId() public static Long getUserId()
{ {
return SecurityContextHolder.getUserId(); return Convert.toLong(ServletUtils.getRequest().getHeader(SecurityConstants.DETAILS_USER_ID));
}
/**
* 获取用户名称
*/
public static String getUsername()
{
return SecurityContextHolder.getUserName();
}
/**
* 获取用户key
*/
public static String getUserKey()
{
return SecurityContextHolder.getUserKey();
}
/**
* 获取登录用户信息
*/
public static LoginUser getLoginUser()
{
return SecurityContextHolder.get(SecurityConstants.LOGIN_USER, LoginUser.class);
} }
/** /**
@ -61,20 +42,18 @@ public class SecurityUtils
*/ */
public static String getToken(HttpServletRequest request) public static String getToken(HttpServletRequest request)
{ {
// 从header获取token标识 String token = request.getHeader(SecurityConstants.TOKEN_AUTHENTICATION);
String token = request.getHeader(TokenConstants.AUTHENTICATION);
return replaceTokenPrefix(token); return replaceTokenPrefix(token);
} }
/** /**
* 裁剪token前缀 * 替换token前缀
*/ */
public static String replaceTokenPrefix(String token) public static String replaceTokenPrefix(String token)
{ {
// 如果前端设置了令牌前缀则裁剪掉前缀 if (StringUtils.isNotEmpty(token) && token.startsWith(SecurityConstants.TOKEN_PREFIX))
if (StringUtils.isNotEmpty(token) && token.startsWith(TokenConstants.PREFIX))
{ {
token = token.replaceFirst(TokenConstants.PREFIX, ""); token = token.replace(SecurityConstants.TOKEN_PREFIX, "");
} }
return token; return token;
} }

View File

@ -4,11 +4,9 @@ import java.io.IOException;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.URLDecoder; import java.net.URLDecoder;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.util.Collections;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.HashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSession;
@ -17,11 +15,10 @@ import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.util.LinkedCaseInsensitiveMap;
import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.context.request.ServletRequestAttributes;
import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.core.constant.Constants; import com.ruoyi.common.core.constant.Constants;
import com.ruoyi.common.core.domain.R; import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.text.Convert; import com.ruoyi.common.core.text.Convert;
@ -66,50 +63,6 @@ public class ServletUtils
return Convert.toInt(getRequest().getParameter(name), defaultValue); return Convert.toInt(getRequest().getParameter(name), defaultValue);
} }
/**
* 获取Boolean参数
*/
public static Boolean getParameterToBool(String name)
{
return Convert.toBool(getRequest().getParameter(name));
}
/**
* 获取Boolean参数
*/
public static Boolean getParameterToBool(String name, Boolean defaultValue)
{
return Convert.toBool(getRequest().getParameter(name), defaultValue);
}
/**
* 获得所有请求参数
*
* @param request 请求对象{@link ServletRequest}
* @return Map
*/
public static Map<String, String[]> getParams(ServletRequest request)
{
final Map<String, String[]> map = request.getParameterMap();
return Collections.unmodifiableMap(map);
}
/**
* 获得所有请求参数
*
* @param request 请求对象{@link ServletRequest}
* @return Map
*/
public static Map<String, String> getParamMap(ServletRequest request)
{
Map<String, String> params = new HashMap<>();
for (Map.Entry<String, String[]> entry : getParams(request).entrySet())
{
params.put(entry.getKey(), StringUtils.join(entry.getValue(), ","));
}
return params;
}
/** /**
* 获取request * 获取request
*/ */
@ -161,19 +114,9 @@ public class ServletUtils
} }
} }
public static String getHeader(HttpServletRequest request, String name)
{
String value = request.getHeader(name);
if (StringUtils.isEmpty(value))
{
return StringUtils.EMPTY;
}
return urlDecode(value);
}
public static Map<String, String> getHeaders(HttpServletRequest request) public static Map<String, String> getHeaders(HttpServletRequest request)
{ {
Map<String, String> map = new LinkedCaseInsensitiveMap<>(); Map<String, String> map = new LinkedHashMap<>();
Enumeration<String> enumeration = request.getHeaderNames(); Enumeration<String> enumeration = request.getHeaderNames();
if (enumeration != null) if (enumeration != null)
{ {
@ -192,8 +135,9 @@ public class ServletUtils
* *
* @param response 渲染对象 * @param response 渲染对象
* @param string 待渲染的字符串 * @param string 待渲染的字符串
* @return null
*/ */
public static void renderString(HttpServletResponse response, String string) public static String renderString(HttpServletResponse response, String string)
{ {
try try
{ {
@ -206,6 +150,7 @@ public class ServletUtils
{ {
e.printStackTrace(); e.printStackTrace();
} }
return null;
} }
/** /**
@ -216,13 +161,13 @@ public class ServletUtils
public static boolean isAjaxRequest(HttpServletRequest request) public static boolean isAjaxRequest(HttpServletRequest request)
{ {
String accept = request.getHeader("accept"); String accept = request.getHeader("accept");
if (accept != null && accept.contains("application/json")) if (accept != null && accept.indexOf("application/json") != -1)
{ {
return true; return true;
} }
String xRequestedWith = request.getHeader("X-Requested-With"); String xRequestedWith = request.getHeader("X-Requested-With");
if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest")) if (xRequestedWith != null && xRequestedWith.indexOf("XMLHttpRequest") != -1)
{ {
return true; return true;
} }
@ -234,7 +179,11 @@ public class ServletUtils
} }
String ajax = request.getParameter("__ajax"); String ajax = request.getParameter("__ajax");
return StringUtils.inStringIgnoreCase(ajax, "json", "xml"); if (StringUtils.inStringIgnoreCase(ajax, "json", "xml"))
{
return true;
}
return false;
} }
/** /**
@ -251,7 +200,7 @@ public class ServletUtils
} }
catch (UnsupportedEncodingException e) catch (UnsupportedEncodingException e)
{ {
return StringUtils.EMPTY; return "";
} }
} }
@ -269,7 +218,7 @@ public class ServletUtils
} }
catch (UnsupportedEncodingException e) catch (UnsupportedEncodingException e)
{ {
return StringUtils.EMPTY; return "";
} }
} }
@ -327,7 +276,7 @@ public class ServletUtils
response.setStatusCode(status); response.setStatusCode(status);
response.getHeaders().add(HttpHeaders.CONTENT_TYPE, contentType); response.getHeaders().add(HttpHeaders.CONTENT_TYPE, contentType);
R<?> result = R.fail(code, value.toString()); R<?> result = R.fail(code, value.toString());
DataBuffer dataBuffer = response.bufferFactory().wrap(JSON.toJSONString(result).getBytes()); DataBuffer dataBuffer = response.bufferFactory().wrap(JSONObject.toJSONString(result).getBytes());
return response.writeWith(Mono.just(dataBuffer)); return response.writeWith(Mono.just(dataBuffer));
} }
} }

View File

@ -294,32 +294,6 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils
return StringUtils.startsWithAny(link, Constants.HTTP, Constants.HTTPS); return StringUtils.startsWithAny(link, Constants.HTTP, Constants.HTTPS);
} }
/**
* 判断给定的collection列表中是否包含数组array 判断给定的数组array中是否包含给定的元素value
*
* @param collection 给定的集合
* @param array 给定的数组
* @return boolean 结果
*/
public static boolean containsAny(Collection<String> collection, String... array)
{
if (isEmpty(collection) || isEmpty(array))
{
return false;
}
else
{
for (String str : array)
{
if (collection.contains(str))
{
return true;
}
}
return false;
}
}
/** /**
* 驼峰转下划线命名 * 驼峰转下划线命名
*/ */
@ -428,8 +402,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils
} }
/** /**
* 驼峰式命名法 * 驼峰式命名法 例如user_name->userName
* 例如user_name->userName
*/ */
public static String toCamelCase(String s) public static String toCamelCase(String s)
{ {
@ -437,10 +410,6 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils
{ {
return null; return null;
} }
if (s.indexOf(SEPARATOR) == -1)
{
return s;
}
s = s.toLowerCase(); s = s.toLowerCase();
StringBuilder sb = new StringBuilder(s.length()); StringBuilder sb = new StringBuilder(s.length());
boolean upperCase = false; boolean upperCase = false;
@ -509,53 +478,4 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils
{ {
return (T) obj; return (T) obj;
} }
}
/**
* 数字左边补齐0使之达到指定长度注意如果数字转换为字符串后长度大于size则只保留 最后size个字符
*
* @param num 数字对象
* @param size 字符串指定长度
* @return 返回数字的字符串格式该字符串为指定长度
*/
public static final String padl(final Number num, final int size)
{
return padl(num.toString(), size, '0');
}
/**
* 字符串左补齐如果原始字符串s长度大于size则只保留最后size个字符
*
* @param s 原始字符串
* @param size 字符串指定长度
* @param c 用于补齐的字符
* @return 返回指定长度的字符串由原字符串左补齐或截取得到
*/
public static final String padl(final String s, final int size, final char c)
{
final StringBuilder sb = new StringBuilder(size);
if (s != null)
{
final int len = s.length();
if (s.length() <= size)
{
for (int i = size - len; i > 0; i--)
{
sb.append(c);
}
sb.append(s);
}
else
{
return s.substring(len - size, len);
}
}
else
{
for (int i = size; i > 0; i--)
{
sb.append(c);
}
}
return sb.toString();
}
}

View File

@ -1,24 +0,0 @@
package com.ruoyi.common.core.utils.bean;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Validator;
/**
* bean对象属性验证
*
* @author ruoyi
*/
public class BeanValidators
{
public static void validateWithException(Validator validator, Object object, Class<?>... groups)
throws ConstraintViolationException
{
Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);
if (!constraintViolations.isEmpty())
{
throw new ConstraintViolationException(constraintViolations);
}
}
}

View File

@ -1,10 +1,7 @@
package com.ruoyi.common.core.utils.file; package com.ruoyi.common.core.utils.file;
import java.io.File; import java.io.File;
import java.util.Objects;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.web.multipart.MultipartFile;
/** /**
* 文件类型工具类 * 文件类型工具类
@ -48,22 +45,6 @@ public class FileTypeUtils
return fileName.substring(separatorIndex + 1).toLowerCase(); return fileName.substring(separatorIndex + 1).toLowerCase();
} }
/**
* 获取文件名的后缀
*
* @param file 表单文件
* @return 后缀名
*/
public static final String getExtension(MultipartFile file)
{
String extension = FilenameUtils.getExtension(file.getOriginalFilename());
if (StringUtils.isEmpty(extension))
{
extension = MimeTypeUtils.getExtension(Objects.requireNonNull(file.getContentType()));
}
return extension;
}
/** /**
* 获取文件类型 * 获取文件类型
* *

View File

@ -97,7 +97,8 @@ public class FileUtils
// 路径为文件且不为空则进行删除 // 路径为文件且不为空则进行删除
if (file.isFile() && file.exists()) if (file.isFile() && file.exists())
{ {
flag = file.delete(); file.delete();
flag = true;
} }
return flag; return flag;
} }
@ -126,8 +127,15 @@ public class FileUtils
{ {
return false; return false;
} }
// 判断是否在允许下载的文件规则内
return ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource)); // 检查允许下载的文件规则
if (ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource)))
{
return true;
}
// 不在允许下载的文件规则
return false;
} }
/** /**
@ -236,7 +244,6 @@ public class FileUtils
.append(percentEncodedFileName); .append(percentEncodedFileName);
response.setHeader("Content-disposition", contentDispositionValue.toString()); response.setHeader("Content-disposition", contentDispositionValue.toString());
response.setHeader("download-filename", percentEncodedFileName);
} }
/** /**

View File

@ -1,6 +1,7 @@
package com.ruoyi.common.core.utils.file; package com.ruoyi.common.core.utils.file;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream; import java.io.InputStream;
import java.net.URL; import java.net.URL;
import java.net.URLConnection; import java.net.URLConnection;
@ -54,12 +55,13 @@ public class ImageUtils
/** /**
* 读取文件为字节数据 * 读取文件为字节数据
* *
* @param url 地址 * @param key 地址
* @return 字节数据 * @return 字节数据
*/ */
public static byte[] readFile(String url) public static byte[] readFile(String url)
{ {
InputStream in = null; InputStream in = null;
ByteArrayOutputStream baos = null;
try try
{ {
// 网络地址 // 网络地址
@ -79,6 +81,7 @@ public class ImageUtils
finally finally
{ {
IOUtils.closeQuietly(in); IOUtils.closeQuietly(in);
IOUtils.closeQuietly(baos);
} }
} }
} }

View File

@ -69,37 +69,26 @@ public class EscapeUtil
*/ */
private static String encode(String text) private static String encode(String text)
{ {
if (StringUtils.isEmpty(text)) int len;
if ((text == null) || ((len = text.length()) == 0))
{ {
return StringUtils.EMPTY; return StringUtils.EMPTY;
} }
StringBuilder buffer = new StringBuilder(len + (len >> 2));
final StringBuilder tmp = new StringBuilder(text.length() * 6);
char c; char c;
for (int i = 0; i < text.length(); i++) for (int i = 0; i < len; i++)
{ {
c = text.charAt(i); c = text.charAt(i);
if (c < 256) if (c < 64)
{ {
tmp.append("%"); buffer.append(TEXT[c]);
if (c < 16)
{
tmp.append("0");
}
tmp.append(Integer.toString(c, 16));
} }
else else
{ {
tmp.append("%u"); buffer.append(c);
if (c <= 0xfff)
{
// issue#I49JU8@Gitee
tmp.append("0");
}
tmp.append(Integer.toString(c, 16));
} }
} }
return tmp.toString(); return buffer.toString();
} }
/** /**
@ -156,12 +145,11 @@ public class EscapeUtil
public static void main(String[] args) public static void main(String[] args)
{ {
String html = "<script>alert(1);</script>"; String html = "<script>alert(1);</script>";
String escape = EscapeUtil.escape(html);
// String html = "<scr<script>ipt>alert(\"XSS\")</scr<script>ipt>"; // String html = "<scr<script>ipt>alert(\"XSS\")</scr<script>ipt>";
// String html = "<123"; // String html = "<123";
// String html = "123>"; // String html = "123>";
System.out.println("clean: " + EscapeUtil.clean(html)); System.out.println(EscapeUtil.clean(html));
System.out.println("escape: " + escape); System.out.println(EscapeUtil.escape(html));
System.out.println("unescape: " + EscapeUtil.unescape(escape)); System.out.println(EscapeUtil.unescape(html));
} }
} }

View File

@ -332,7 +332,7 @@ public final class HTMLFilter
final String name = m.group(1).toLowerCase(); final String name = m.group(1).toLowerCase();
if (allowed(name)) if (allowed(name))
{ {
if (!inArray(name, vSelfClosingTags)) if (false == inArray(name, vSelfClosingTags))
{ {
if (vTagCounts.containsKey(name)) if (vTagCounts.containsKey(name))
{ {
@ -387,7 +387,7 @@ public final class HTMLFilter
{ {
paramValue = processParamProtocol(paramValue); paramValue = processParamProtocol(paramValue);
} }
params.append(' ').append(paramName).append("=\\\"").append(paramValue).append("\\\""); params.append(' ').append(paramName).append("=\"").append(paramValue).append("\"");
} }
} }

View File

@ -3,7 +3,6 @@ package com.ruoyi.common.core.utils.ip;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import com.ruoyi.common.core.utils.ServletUtils;
import com.ruoyi.common.core.utils.StringUtils; import com.ruoyi.common.core.utils.StringUtils;
/** /**
@ -13,79 +12,58 @@ import com.ruoyi.common.core.utils.StringUtils;
*/ */
public class IpUtils public class IpUtils
{ {
public final static String REGX_0_255 = "(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]\\d|\\d)";
// 匹配 ip
public final static String REGX_IP = "((" + REGX_0_255 + "\\.){3}" + REGX_0_255 + ")";
public final static String REGX_IP_WILDCARD = "(((\\*\\.){3}\\*)|(" + REGX_0_255 + "(\\.\\*){3})|(" + REGX_0_255 + "\\." + REGX_0_255 + ")(\\.\\*){2}" + "|((" + REGX_0_255 + "\\.){3}\\*))";
// 匹配网段
public final static String REGX_IP_SEG = "(" + REGX_IP + "\\-" + REGX_IP + ")";
/**
* 获取客户端IP
*
* @return IP地址
*/
public static String getIpAddr()
{
return getIpAddr(ServletUtils.getRequest());
}
/**
* 获取客户端IP
*
* @param request 请求对象
* @return IP地址
*/
public static String getIpAddr(HttpServletRequest request) public static String getIpAddr(HttpServletRequest request)
{ {
if (request == null) if (request == null)
{ {
return "unknown"; return null;
}
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
{
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
{
ip = request.getHeader("X-Forwarded-For");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
{
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
{
ip = request.getHeader("X-Real-IP");
} }
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) String ip = null;
// X-Forwarded-ForSquid 服务代理
String ipAddresses = request.getHeader("X-Forwarded-For");
if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses))
{
// Proxy-Client-IPapache 服务代理
ipAddresses = request.getHeader("Proxy-Client-IP");
}
if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses))
{
// WL-Proxy-Client-IPweblogic 服务代理
ipAddresses = request.getHeader("WL-Proxy-Client-IP");
}
if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses))
{
// HTTP_CLIENT_IP有些代理服务器
ipAddresses = request.getHeader("HTTP_CLIENT_IP");
}
if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses))
{
// X-Real-IPnginx服务代理
ipAddresses = request.getHeader("X-Real-IP");
}
// 有些网络通过多层代理那么获取到的ip就会有多个一般都是通过逗号,分割开来并且第一个ip为客户端的真实IP
if (ipAddresses != null && ipAddresses.length() != 0)
{
ip = ipAddresses.split(",")[0];
}
// 还是不能获取到最后再通过request.getRemoteAddr();获取
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses))
{ {
ip = request.getRemoteAddr(); ip = request.getRemoteAddr();
} }
return ip.equals("0:0:0:0:0:0:0:1") ? "127.0.0.1" : ip;
return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : getMultistageReverseProxyIp(ip);
} }
/**
* 检查是否为内部IP地址
*
* @param ip IP地址
* @return 结果
*/
public static boolean internalIp(String ip) public static boolean internalIp(String ip)
{ {
byte[] addr = textToNumericFormatV4(ip); byte[] addr = textToNumericFormatV4(ip);
return internalIp(addr) || "127.0.0.1".equals(ip); return internalIp(addr) || "127.0.0.1".equals(ip);
} }
/**
* 检查是否为内部IP地址
*
* @param addr byte地址
* @return 结果
*/
private static boolean internalIp(byte[] addr) private static boolean internalIp(byte[] addr)
{ {
if (StringUtils.isNull(addr) || addr.length < 2) if (StringUtils.isNull(addr) || addr.length < 2)
@ -146,8 +124,7 @@ public class IpUtils
{ {
case 1: case 1:
l = Long.parseLong(elements[0]); l = Long.parseLong(elements[0]);
if ((l < 0L) || (l > 4294967295L)) if ((l < 0L) || (l > 4294967295L)){
{
return null; return null;
} }
bytes[0] = (byte) (int) (l >> 24 & 0xFF); bytes[0] = (byte) (int) (l >> 24 & 0xFF);
@ -157,14 +134,12 @@ public class IpUtils
break; break;
case 2: case 2:
l = Integer.parseInt(elements[0]); l = Integer.parseInt(elements[0]);
if ((l < 0L) || (l > 255L)) if ((l < 0L) || (l > 255L)) {
{
return null; return null;
} }
bytes[0] = (byte) (int) (l & 0xFF); bytes[0] = (byte) (int) (l & 0xFF);
l = Integer.parseInt(elements[1]); l = Integer.parseInt(elements[1]);
if ((l < 0L) || (l > 16777215L)) if ((l < 0L) || (l > 16777215L)) {
{
return null; return null;
} }
bytes[1] = (byte) (int) (l >> 16 & 0xFF); bytes[1] = (byte) (int) (l >> 16 & 0xFF);
@ -175,15 +150,13 @@ public class IpUtils
for (i = 0; i < 2; ++i) for (i = 0; i < 2; ++i)
{ {
l = Integer.parseInt(elements[i]); l = Integer.parseInt(elements[i]);
if ((l < 0L) || (l > 255L)) if ((l < 0L) || (l > 255L)) {
{
return null; return null;
} }
bytes[i] = (byte) (int) (l & 0xFF); bytes[i] = (byte) (int) (l & 0xFF);
} }
l = Integer.parseInt(elements[2]); l = Integer.parseInt(elements[2]);
if ((l < 0L) || (l > 65535L)) if ((l < 0L) || (l > 65535L)) {
{
return null; return null;
} }
bytes[2] = (byte) (int) (l >> 8 & 0xFF); bytes[2] = (byte) (int) (l >> 8 & 0xFF);
@ -193,8 +166,7 @@ public class IpUtils
for (i = 0; i < 4; ++i) for (i = 0; i < 4; ++i)
{ {
l = Integer.parseInt(elements[i]); l = Integer.parseInt(elements[i]);
if ((l < 0L) || (l > 255L)) if ((l < 0L) || (l > 255L)) {
{
return null; return null;
} }
bytes[i] = (byte) (int) (l & 0xFF); bytes[i] = (byte) (int) (l & 0xFF);
@ -211,11 +183,6 @@ public class IpUtils
return bytes; return bytes;
} }
/**
* 获取IP地址
*
* @return 本地IP地址
*/
public static String getHostIp() public static String getHostIp()
{ {
try try
@ -228,11 +195,6 @@ public class IpUtils
return "127.0.0.1"; return "127.0.0.1";
} }
/**
* 获取主机名
*
* @return 本地主机名
*/
public static String getHostName() public static String getHostName()
{ {
try try
@ -244,139 +206,4 @@ public class IpUtils
} }
return "未知"; return "未知";
} }
/**
* 从多级反向代理中获得第一个非unknown IP地址
*
* @param ip 获得的IP地址
* @return 第一个非unknown IP地址
*/
public static String getMultistageReverseProxyIp(String ip)
{
// 多级反向代理检测
if (ip != null && ip.indexOf(",") > 0)
{
final String[] ips = ip.trim().split(",");
for (String subIp : ips)
{
if (false == isUnknown(subIp))
{
ip = subIp;
break;
}
}
}
return StringUtils.substring(ip, 0, 255);
}
/**
* 检测给定字符串是否为未知多用于检测HTTP请求相关
*
* @param checkString 被检测的字符串
* @return 是否未知
*/
public static boolean isUnknown(String checkString)
{
return StringUtils.isBlank(checkString) || "unknown".equalsIgnoreCase(checkString);
}
/**
* 是否为IP
*/
public static boolean isIP(String ip)
{
return StringUtils.isNotBlank(ip) && ip.matches(REGX_IP);
}
/**
* 是否为IP *为间隔的通配符地址
*/
public static boolean isIpWildCard(String ip)
{
return StringUtils.isNotBlank(ip) && ip.matches(REGX_IP_WILDCARD);
}
/**
* 检测参数是否在ip通配符里
*/
public static boolean ipIsInWildCardNoCheck(String ipWildCard, String ip)
{
String[] s1 = ipWildCard.split("\\.");
String[] s2 = ip.split("\\.");
boolean isMatchedSeg = true;
for (int i = 0; i < s1.length && !s1[i].equals("*"); i++)
{
if (!s1[i].equals(s2[i]))
{
isMatchedSeg = false;
break;
}
}
return isMatchedSeg;
}
/**
* 是否为特定格式如:10.10.10.1-10.10.10.99的ip段字符串
*/
public static boolean isIPSegment(String ipSeg)
{
return StringUtils.isNotBlank(ipSeg) && ipSeg.matches(REGX_IP_SEG);
}
/**
* 判断ip是否在指定网段中
*/
public static boolean ipIsInNetNoCheck(String iparea, String ip)
{
int idx = iparea.indexOf('-');
String[] sips = iparea.substring(0, idx).split("\\.");
String[] sipe = iparea.substring(idx + 1).split("\\.");
String[] sipt = ip.split("\\.");
long ips = 0L, ipe = 0L, ipt = 0L;
for (int i = 0; i < 4; ++i)
{
ips = ips << 8 | Integer.parseInt(sips[i]);
ipe = ipe << 8 | Integer.parseInt(sipe[i]);
ipt = ipt << 8 | Integer.parseInt(sipt[i]);
}
if (ips > ipe)
{
long t = ips;
ips = ipe;
ipe = t;
}
return ips <= ipt && ipt <= ipe;
}
/**
* 校验ip是否符合过滤串规则
*
* @param filter 过滤IP列表,支持后缀'*'通配,支持网段如:`10.10.10.1-10.10.10.99`
* @param ip 校验IP地址
* @return boolean 结果
*/
public static boolean isMatchedIp(String filter, String ip)
{
if (StringUtils.isEmpty(filter) || StringUtils.isEmpty(ip))
{
return false;
}
String[] ips = filter.split(";");
for (String iStr : ips)
{
if (isIP(iStr) && iStr.equals(ip))
{
return true;
}
else if (isIpWildCard(iStr) && ipIsInWildCardNoCheck(iStr, ip))
{
return true;
}
else if (isIPSegment(iStr) && ipIsInNetNoCheck(iStr, ip))
{
return true;
}
}
return false;
}
} }

View File

@ -1,24 +0,0 @@
package com.ruoyi.common.core.utils.poi;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Workbook;
/**
* Excel数据格式处理适配器
*
* @author ruoyi
*/
public interface ExcelHandlerAdapter
{
/**
* 格式化
*
* @param value 单元格数据值
* @param args excel注解args参数组
* @param cell 单元格对象
* @param wb 工作簿对象
*
* @return 处理后的值
*/
Object format(Object value, String[] args, Cell cell, Workbook wb);
}

View File

@ -1,6 +1,6 @@
package com.ruoyi.common.core.utils.sql; package com.ruoyi.common.core.utils.sql;
import com.ruoyi.common.core.exception.UtilException; import com.ruoyi.common.core.exception.BaseException;
import com.ruoyi.common.core.utils.StringUtils; import com.ruoyi.common.core.utils.StringUtils;
/** /**
@ -10,21 +10,11 @@ import com.ruoyi.common.core.utils.StringUtils;
*/ */
public class SqlUtil public class SqlUtil
{ {
/**
* 定义常用的 sql关键字
*/
public static String SQL_REGEX = "and |extractvalue|updatexml|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |+|user()";
/** /**
* 仅支持字母数字下划线空格逗号小数点支持多个字段排序 * 仅支持字母数字下划线空格逗号小数点支持多个字段排序
*/ */
public static String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+"; public static String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+";
/**
* 限制orderBy最大长度
*/
private static final int ORDER_BY_MAX_LENGTH = 500;
/** /**
* 检查字符防止注入绕过 * 检查字符防止注入绕过
*/ */
@ -32,11 +22,7 @@ public class SqlUtil
{ {
if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value)) if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value))
{ {
throw new UtilException("参数不符合规范,不能进行查询"); throw new BaseException("参数不符合规范,不能进行查询");
}
if (StringUtils.length(value) > ORDER_BY_MAX_LENGTH)
{
throw new UtilException("参数已超过最大限制,不能进行查询");
} }
return value; return value;
} }
@ -48,23 +34,4 @@ public class SqlUtil
{ {
return value.matches(SQL_PATTERN); return value.matches(SQL_PATTERN);
} }
/**
* SQL关键字检查
*/
public static void filterKeyword(String value)
{
if (StringUtils.isEmpty(value))
{
return;
}
String[] sqlKeywords = StringUtils.split(SQL_REGEX, "\\|");
for (String sqlKeyword : sqlKeywords)
{
if (StringUtils.indexOfIgnoreCase(value, sqlKeyword) > -1)
{
throw new UtilException("参数存在SQL注入风险");
}
}
}
} }

View File

@ -1,86 +0,0 @@
package com.ruoyi.common.core.utils.uuid;
import java.util.concurrent.atomic.AtomicInteger;
import com.ruoyi.common.core.utils.DateUtils;
import com.ruoyi.common.core.utils.StringUtils;
/**
* @author ruoyi 序列生成类
*/
public class Seq
{
// 通用序列类型
public static final String commSeqType = "COMMON";
// 上传序列类型
public static final String uploadSeqType = "UPLOAD";
// 通用接口序列数
private static AtomicInteger commSeq = new AtomicInteger(1);
// 上传接口序列数
private static AtomicInteger uploadSeq = new AtomicInteger(1);
// 机器标识
private static final String machineCode = "A";
/**
* 获取通用序列号
*
* @return 序列值
*/
public static String getId()
{
return getId(commSeqType);
}
/**
* 默认16位序列号 yyMMddHHmmss + 一位机器标识 + 3长度循环递增字符串
*
* @return 序列值
*/
public static String getId(String type)
{
AtomicInteger atomicInt = commSeq;
if (uploadSeqType.equals(type))
{
atomicInt = uploadSeq;
}
return getId(atomicInt, 3);
}
/**
* 通用接口序列号 yyMMddHHmmss + 一位机器标识 + length长度循环递增字符串
*
* @param atomicInt 序列数
* @param length 数值长度
* @return 序列值
*/
public static String getId(AtomicInteger atomicInt, int length)
{
String result = DateUtils.dateTimeNow();
result += machineCode;
result += getSeq(atomicInt, length);
return result;
}
/**
* 序列循环递增字符串[1, 10 (length)幂次方), 用0左补齐length位数
*
* @return 序列值
*/
private synchronized static String getSeq(AtomicInteger atomicInt, int length)
{
// 先取值再+1
int value = atomicInt.getAndIncrement();
// 如果更新后值>=10 (length)幂次方则重置为1
int maxSeq = (int) Math.pow(10, length);
if (atomicInt.get() >= maxSeq)
{
atomicInt.set(1);
}
// 转字符串用0左补齐
return StringUtils.padl(value, length);
}
}

View File

@ -7,12 +7,16 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.annotation.InitBinder;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo; import com.github.pagehelper.PageInfo;
import com.ruoyi.common.core.constant.HttpStatus; import com.ruoyi.common.core.constant.HttpStatus;
import com.ruoyi.common.core.utils.DateUtils; import com.ruoyi.common.core.utils.DateUtils;
import com.ruoyi.common.core.utils.PageUtils; import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.core.utils.sql.SqlUtil;
import com.ruoyi.common.core.web.domain.AjaxResult; import com.ruoyi.common.core.web.domain.AjaxResult;
import com.ruoyi.common.core.web.page.PageDomain;
import com.ruoyi.common.core.web.page.TableDataInfo; import com.ruoyi.common.core.web.page.TableDataInfo;
import com.ruoyi.common.core.web.page.TableSupport;
/** /**
* web层通用数据处理 * web层通用数据处理
@ -45,15 +49,14 @@ public class BaseController
*/ */
protected void startPage() protected void startPage()
{ {
PageUtils.startPage(); PageDomain pageDomain = TableSupport.buildPageRequest();
} Integer pageNum = pageDomain.getPageNum();
Integer pageSize = pageDomain.getPageSize();
/** if (StringUtils.isNotNull(pageNum) && StringUtils.isNotNull(pageSize))
* 清理分页的线程变量 {
*/ String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy());
protected void clearPage() PageHelper.startPage(pageNum, pageSize, orderBy);
{ }
PageUtils.clearPage();
} }
/** /**
@ -70,54 +73,6 @@ public class BaseController
return rspData; return rspData;
} }
/**
* 返回成功
*/
public AjaxResult success()
{
return AjaxResult.success();
}
/**
* 返回成功消息
*/
public AjaxResult success(String message)
{
return AjaxResult.success(message);
}
/**
* 返回成功消息
*/
public AjaxResult success(Object data)
{
return AjaxResult.success(data);
}
/**
* 返回失败消息
*/
public AjaxResult error()
{
return AjaxResult.error();
}
/**
* 返回失败消息
*/
public AjaxResult error(String message)
{
return AjaxResult.error(message);
}
/**
* 返回警告消息
*/
public AjaxResult warn(String message)
{
return AjaxResult.warn(message);
}
/** /**
* 响应返回结果 * 响应返回结果
* *
@ -139,4 +94,36 @@ public class BaseController
{ {
return result ? success() : error(); return result ? success() : error();
} }
/**
* 返回成功
*/
public AjaxResult success()
{
return AjaxResult.success();
}
/**
* 返回失败消息
*/
public AjaxResult error()
{
return AjaxResult.error();
}
/**
* 返回成功消息
*/
public AjaxResult success(String message)
{
return AjaxResult.success(message);
}
/**
* 返回失败消息
*/
public AjaxResult error(String message)
{
return AjaxResult.error(message);
}
} }

View File

@ -1,7 +1,6 @@
package com.ruoyi.common.core.web.domain; package com.ruoyi.common.core.web.domain;
import java.util.HashMap; import java.util.HashMap;
import java.util.Objects;
import com.ruoyi.common.core.constant.HttpStatus; import com.ruoyi.common.core.constant.HttpStatus;
import com.ruoyi.common.core.utils.StringUtils; import com.ruoyi.common.core.utils.StringUtils;
@ -58,6 +57,20 @@ public class AjaxResult extends HashMap<String, Object>
super.put(DATA_TAG, data); super.put(DATA_TAG, data);
} }
} }
/**
* 方便链式调用
*
* @param key
* @param value
* @return
*/
@Override
public AjaxResult put(String key, Object value)
{
super.put(key, value);
return this;
}
/** /**
* 返回成功消息 * 返回成功消息
@ -102,33 +115,10 @@ public class AjaxResult extends HashMap<String, Object>
return new AjaxResult(HttpStatus.SUCCESS, msg, data); return new AjaxResult(HttpStatus.SUCCESS, msg, data);
} }
/**
* 返回警告消息
*
* @param msg 返回内容
* @return 警告消息
*/
public static AjaxResult warn(String msg)
{
return AjaxResult.warn(msg, null);
}
/**
* 返回警告消息
*
* @param msg 返回内容
* @param data 数据对象
* @return 警告消息
*/
public static AjaxResult warn(String msg, Object data)
{
return new AjaxResult(HttpStatus.WARN, msg, data);
}
/** /**
* 返回错误消息 * 返回错误消息
* *
* @return 错误消息 * @return
*/ */
public static AjaxResult error() public static AjaxResult error()
{ {
@ -139,7 +129,7 @@ public class AjaxResult extends HashMap<String, Object>
* 返回错误消息 * 返回错误消息
* *
* @param msg 返回内容 * @param msg 返回内容
* @return 错误消息 * @return 警告消息
*/ */
public static AjaxResult error(String msg) public static AjaxResult error(String msg)
{ {
@ -151,7 +141,7 @@ public class AjaxResult extends HashMap<String, Object>
* *
* @param msg 返回内容 * @param msg 返回内容
* @param data 数据对象 * @param data 数据对象
* @return 错误消息 * @return 警告消息
*/ */
public static AjaxResult error(String msg, Object data) public static AjaxResult error(String msg, Object data)
{ {
@ -163,54 +153,10 @@ public class AjaxResult extends HashMap<String, Object>
* *
* @param code 状态码 * @param code 状态码
* @param msg 返回内容 * @param msg 返回内容
* @return 错误消息 * @return 警告消息
*/ */
public static AjaxResult error(int code, String msg) public static AjaxResult error(int code, String msg)
{ {
return new AjaxResult(code, msg, null); return new AjaxResult(code, msg, null);
} }
/**
* 是否为成功消息
*
* @return 结果
*/
public boolean isSuccess()
{
return Objects.equals(HttpStatus.SUCCESS, this.get(CODE_TAG));
}
/**
* 是否为警告消息
*
* @return 结果
*/
public boolean isWarn()
{
return Objects.equals(HttpStatus.WARN, this.get(CODE_TAG));
}
/**
* 是否为错误消息
*
* @return 结果
*/
public boolean isError()
{
return Objects.equals(HttpStatus.ERROR, this.get(CODE_TAG));
}
/**
* 方便链式调用
*
* @param key
* @param value
* @return
*/
@Override
public AjaxResult put(String key, Object value)
{
super.put(key, value);
return this;
}
} }

View File

@ -5,8 +5,6 @@ import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
/** /**
* Entity基类 * Entity基类
@ -18,7 +16,6 @@ public class BaseEntity implements Serializable
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** 搜索值 */ /** 搜索值 */
@JsonIgnore
private String searchValue; private String searchValue;
/** 创建者 */ /** 创建者 */
@ -39,7 +36,6 @@ public class BaseEntity implements Serializable
private String remark; private String remark;
/** 请求参数 */ /** 请求参数 */
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private Map<String, Object> params; private Map<String, Object> params;
public String getSearchValue() public String getSearchValue()

View File

@ -21,9 +21,6 @@ public class PageDomain
/** 排序的方向desc或者asc */ /** 排序的方向desc或者asc */
private String isAsc = "asc"; private String isAsc = "asc";
/** 分页参数合理化 */
private Boolean reasonable = true;
public String getOrderBy() public String getOrderBy()
{ {
if (StringUtils.isEmpty(orderByColumn)) if (StringUtils.isEmpty(orderByColumn))
@ -84,18 +81,4 @@ public class PageDomain
this.isAsc = isAsc; this.isAsc = isAsc;
} }
} }
public Boolean getReasonable()
{
if (StringUtils.isNull(reasonable))
{
return Boolean.TRUE;
}
return reasonable;
}
public void setReasonable(Boolean reasonable)
{
this.reasonable = reasonable;
}
} }

View File

@ -1,6 +1,5 @@
package com.ruoyi.common.core.web.page; package com.ruoyi.common.core.web.page;
import com.ruoyi.common.core.text.Convert;
import com.ruoyi.common.core.utils.ServletUtils; import com.ruoyi.common.core.utils.ServletUtils;
/** /**
@ -30,22 +29,16 @@ public class TableSupport
*/ */
public static final String IS_ASC = "isAsc"; public static final String IS_ASC = "isAsc";
/**
* 分页参数合理化
*/
public static final String REASONABLE = "reasonable";
/** /**
* 封装分页对象 * 封装分页对象
*/ */
public static PageDomain getPageDomain() public static PageDomain getPageDomain()
{ {
PageDomain pageDomain = new PageDomain(); PageDomain pageDomain = new PageDomain();
pageDomain.setPageNum(Convert.toInt(ServletUtils.getParameter(PAGE_NUM), 1)); pageDomain.setPageNum(ServletUtils.getParameterToInt(PAGE_NUM));
pageDomain.setPageSize(Convert.toInt(ServletUtils.getParameter(PAGE_SIZE), 10)); pageDomain.setPageSize(ServletUtils.getParameterToInt(PAGE_SIZE));
pageDomain.setOrderByColumn(ServletUtils.getParameter(ORDER_BY_COLUMN)); pageDomain.setOrderByColumn(ServletUtils.getParameter(ORDER_BY_COLUMN));
pageDomain.setIsAsc(ServletUtils.getParameter(IS_ASC)); pageDomain.setIsAsc(ServletUtils.getParameter(IS_ASC));
pageDomain.setReasonable(ServletUtils.getParameterToBool(REASONABLE));
return pageDomain; return pageDomain;
} }

View File

@ -1,27 +0,0 @@
package com.ruoyi.common.core.xss;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义xss校验注解
*
* @author ruoyi
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(value = { ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER })
@Constraint(validatedBy = { XssValidator.class })
public @interface Xss
{
String message()
default "不允许任何脚本运行";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

View File

@ -1,34 +0,0 @@
package com.ruoyi.common.core.xss;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import com.ruoyi.common.core.utils.StringUtils;
/**
* 自定义xss校验注解实现
*
* @author ruoyi
*/
public class XssValidator implements ConstraintValidator<Xss, String>
{
private static final String HTML_PATTERN = "<(\\S*?)[^>]*>.*?|<.*? />";
@Override
public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext)
{
if (StringUtils.isBlank(value))
{
return true;
}
return !containsHtml(value);
}
public static boolean containsHtml(String value)
{
Pattern pattern = Pattern.compile(HTML_PATTERN);
Matcher matcher = pattern.matcher(value);
return matcher.matches();
}
}

View File

@ -0,0 +1,4 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.ruoyi.common.core.utils.SpringUtils

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common</artifactId> <artifactId>ruoyi-common</artifactId>
<version>3.6.3</version> <version>3.1.0</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -25,9 +25,4 @@ public @interface DataScope
* 用户表的别名 * 用户表的别名
*/ */
public String userAlias() default ""; public String userAlias() default "";
/**
* 权限字符用于多个角色匹配符合要求的权限默认根据权限注解@RequiresPermissions获取多个权限用逗号分隔开来
*/
public String permission() default "";
} }

View File

@ -1,17 +1,18 @@
package com.ruoyi.common.datascope.aspect; package com.ruoyi.common.datascope.aspect;
import java.util.ArrayList; import java.lang.reflect.Method;
import java.util.List;
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.ruoyi.common.core.context.SecurityContextHolder;
import com.ruoyi.common.core.text.Convert;
import com.ruoyi.common.core.utils.StringUtils; import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.core.web.domain.BaseEntity; import com.ruoyi.common.core.web.domain.BaseEntity;
import com.ruoyi.common.datascope.annotation.DataScope; import com.ruoyi.common.datascope.annotation.DataScope;
import com.ruoyi.common.security.utils.SecurityUtils; import com.ruoyi.common.security.service.TokenService;
import com.ruoyi.system.api.domain.SysRole; import com.ruoyi.system.api.domain.SysRole;
import com.ruoyi.system.api.domain.SysUser; import com.ruoyi.system.api.domain.SysUser;
import com.ruoyi.system.api.model.LoginUser; import com.ruoyi.system.api.model.LoginUser;
@ -55,26 +56,40 @@ public class DataScopeAspect
*/ */
public static final String DATA_SCOPE = "dataScope"; public static final String DATA_SCOPE = "dataScope";
@Before("@annotation(controllerDataScope)") @Autowired
public void doBefore(JoinPoint point, DataScope controllerDataScope) throws Throwable private TokenService tokenService;
// 配置织入点
@Pointcut("@annotation(com.ruoyi.common.datascope.annotation.DataScope)")
public void dataScopePointCut()
{ {
clearDataScope(point);
handleDataScope(point, controllerDataScope);
} }
protected void handleDataScope(final JoinPoint joinPoint, DataScope controllerDataScope) @Before("dataScopePointCut()")
public void doBefore(JoinPoint point) throws Throwable
{ {
clearDataScope(point);
handleDataScope(point);
}
protected void handleDataScope(final JoinPoint joinPoint)
{
// 获得注解
DataScope controllerDataScope = getAnnotationLog(joinPoint);
if (controllerDataScope == null)
{
return;
}
// 获取当前的用户 // 获取当前的用户
LoginUser loginUser = SecurityUtils.getLoginUser(); LoginUser loginUser = tokenService.getLoginUser();
if (StringUtils.isNotNull(loginUser)) if (StringUtils.isNotNull(loginUser))
{ {
SysUser currentUser = loginUser.getSysUser(); SysUser currentUser = loginUser.getSysUser();
// 如果是超级管理员则不过滤数据 // 如果是超级管理员则不过滤数据
if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin()) if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin())
{ {
String permission = StringUtils.defaultIfEmpty(controllerDataScope.permission(), SecurityContextHolder.getPermission());
dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(), dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(),
controllerDataScope.userAlias(), permission); controllerDataScope.userAlias());
} }
} }
} }
@ -86,29 +101,17 @@ public class DataScopeAspect
* @param user 用户 * @param user 用户
* @param deptAlias 部门别名 * @param deptAlias 部门别名
* @param userAlias 用户别名 * @param userAlias 用户别名
* @param permission 权限字符
*/ */
public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias, String permission) public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias)
{ {
StringBuilder sqlString = new StringBuilder(); StringBuilder sqlString = new StringBuilder();
List<String> conditions = new ArrayList<String>();
for (SysRole role : user.getRoles()) for (SysRole role : user.getRoles())
{ {
String dataScope = role.getDataScope(); String dataScope = role.getDataScope();
if (!DATA_SCOPE_CUSTOM.equals(dataScope) && conditions.contains(dataScope))
{
continue;
}
if (StringUtils.isNotEmpty(permission) && StringUtils.isNotEmpty(role.getPermissions())
&& !StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission)))
{
continue;
}
if (DATA_SCOPE_ALL.equals(dataScope)) if (DATA_SCOPE_ALL.equals(dataScope))
{ {
sqlString = new StringBuilder(); sqlString = new StringBuilder();
conditions.add(dataScope);
break; break;
} }
else if (DATA_SCOPE_CUSTOM.equals(dataScope)) else if (DATA_SCOPE_CUSTOM.equals(dataScope))
@ -136,16 +139,9 @@ public class DataScopeAspect
else else
{ {
// 数据权限为仅本人且没有userAlias别名不查询任何数据 // 数据权限为仅本人且没有userAlias别名不查询任何数据
sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias)); sqlString.append(" OR 1=0 ");
} }
} }
conditions.add(dataScope);
}
// 多角色情况下所有角色都不包含传递过来的权限字符这个时候sqlString也会为空所以要限制一下,不查询任何数据
if (StringUtils.isEmpty(conditions))
{
sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias));
} }
if (StringUtils.isNotBlank(sqlString.toString())) if (StringUtils.isNotBlank(sqlString.toString()))
@ -159,6 +155,22 @@ public class DataScopeAspect
} }
} }
/**
* 是否存在注解如果存在就获取
*/
private DataScope getAnnotationLog(JoinPoint joinPoint)
{
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
if (method != null)
{
return method.getAnnotation(DataScope.class);
}
return null;
}
/** /**
* 拼接权限sql前先清空params.dataScope参数防止注入 * 拼接权限sql前先清空params.dataScope参数防止注入
*/ */

View File

@ -0,0 +1,4 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.ruoyi.common.datascope.aspect.DataScopeAspect

View File

@ -5,19 +5,19 @@
<parent> <parent>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common</artifactId> <artifactId>ruoyi-common</artifactId>
<version>3.6.3</version> <version>3.1.0</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>ruoyi-common-datasource</artifactId> <artifactId>ruoyi-common-datasource</artifactId>
<description> <description>
ruoyi-common-datasource多数据源 ruoyi-common-datasource多数据源
</description> </description>
<dependencies> <dependencies>
<!-- Druid --> <!-- Druid -->
<dependency> <dependency>
<groupId>com.alibaba</groupId> <groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId> <artifactId>druid-spring-boot-starter</artifactId>
@ -30,6 +30,12 @@
<artifactId>dynamic-datasource-spring-boot-starter</artifactId> <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>${dynamic-ds.version}</version> <version>${dynamic-ds.version}</version>
</dependency> </dependency>
<!-- SpringBoot Seata -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common</artifactId> <artifactId>ruoyi-common</artifactId>
<version>3.6.3</version> <version>3.1.0</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -38,14 +38,4 @@ public @interface Log
* 是否保存请求的参数 * 是否保存请求的参数
*/ */
public boolean isSaveRequestData() default true; public boolean isSaveRequestData() default true;
/**
* 是否保存响应的参数
*/
public boolean isSaveResponseData() default true;
/**
* 排除指定的请求参数
*/
public String[] excludeParamNames() default {};
} }

View File

@ -1,32 +1,33 @@
package com.ruoyi.common.log.aspect; package com.ruoyi.common.log.aspect;
import java.lang.reflect.Method;
import java.util.Collection; import java.util.Collection;
import java.util.Iterator;
import java.util.Map; import java.util.Map;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.ArrayUtils;
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.NamedThreadLocal;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult; import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson.JSON;
import com.ruoyi.common.core.utils.SecurityUtils;
import com.ruoyi.common.core.utils.ServletUtils; import com.ruoyi.common.core.utils.ServletUtils;
import com.ruoyi.common.core.utils.StringUtils; import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.core.utils.ip.IpUtils; import com.ruoyi.common.core.utils.ip.IpUtils;
import com.ruoyi.common.log.annotation.Log; import com.ruoyi.common.log.annotation.Log;
import com.ruoyi.common.log.enums.BusinessStatus; import com.ruoyi.common.log.enums.BusinessStatus;
import com.ruoyi.common.log.filter.PropertyPreExcludeFilter;
import com.ruoyi.common.log.service.AsyncLogService; import com.ruoyi.common.log.service.AsyncLogService;
import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.system.api.domain.SysOperLog; import com.ruoyi.system.api.domain.SysOperLog;
/** /**
@ -39,23 +40,14 @@ import com.ruoyi.system.api.domain.SysOperLog;
public class LogAspect public class LogAspect
{ {
private static final Logger log = LoggerFactory.getLogger(LogAspect.class); private static final Logger log = LoggerFactory.getLogger(LogAspect.class);
/** 排除敏感属性字段 */
public static final String[] EXCLUDE_PROPERTIES = { "password", "oldPassword", "newPassword", "confirmPassword" };
/** 计算操作消耗时间 */
private static final ThreadLocal<Long> TIME_THREADLOCAL = new NamedThreadLocal<Long>("Cost Time");
@Autowired @Autowired
private AsyncLogService asyncLogService; private AsyncLogService asyncLogService;
/** // 配置织入点
* 处理请求前执行 @Pointcut("@annotation(com.ruoyi.common.log.annotation.Log)")
*/ public void logPointCut()
@Before(value = "@annotation(controllerLog)")
public void boBefore(JoinPoint joinPoint, Log controllerLog)
{ {
TIME_THREADLOCAL.set(System.currentTimeMillis());
} }
/** /**
@ -63,10 +55,10 @@ public class LogAspect
* *
* @param joinPoint 切点 * @param joinPoint 切点
*/ */
@AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult") @AfterReturning(pointcut = "logPointCut()", returning = "jsonResult")
public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult) public void doAfterReturning(JoinPoint joinPoint, Object jsonResult)
{ {
handleLog(joinPoint, controllerLog, null, jsonResult); handleLog(joinPoint, null, jsonResult);
} }
/** /**
@ -75,23 +67,33 @@ public class LogAspect
* @param joinPoint 切点 * @param joinPoint 切点
* @param e 异常 * @param e 异常
*/ */
@AfterThrowing(value = "@annotation(controllerLog)", throwing = "e") @AfterThrowing(value = "logPointCut()", throwing = "e")
public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e) public void doAfterThrowing(JoinPoint joinPoint, Exception e)
{ {
handleLog(joinPoint, controllerLog, e, null); handleLog(joinPoint, e, null);
} }
protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) protected void handleLog(final JoinPoint joinPoint, final Exception e, Object jsonResult)
{ {
try try
{ {
// 获得注解
Log controllerLog = getAnnotationLog(joinPoint);
if (controllerLog == null)
{
return;
}
// *========数据库日志=========*// // *========数据库日志=========*//
SysOperLog operLog = new SysOperLog(); SysOperLog operLog = new SysOperLog();
operLog.setStatus(BusinessStatus.SUCCESS.ordinal()); operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
// 请求的地址 // 请求的地址
String ip = IpUtils.getIpAddr(); String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
operLog.setOperIp(ip); operLog.setOperIp(ip);
operLog.setOperUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255)); // 返回参数
operLog.setJsonResult(JSON.toJSONString(jsonResult));
operLog.setOperUrl(ServletUtils.getRequest().getRequestURI());
String username = SecurityUtils.getUsername(); String username = SecurityUtils.getUsername();
if (StringUtils.isNotBlank(username)) if (StringUtils.isNotBlank(username))
{ {
@ -110,22 +112,17 @@ public class LogAspect
// 设置请求方式 // 设置请求方式
operLog.setRequestMethod(ServletUtils.getRequest().getMethod()); operLog.setRequestMethod(ServletUtils.getRequest().getMethod());
// 处理设置注解上的参数 // 处理设置注解上的参数
getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult); getControllerMethodDescription(joinPoint, controllerLog, operLog);
// 设置消耗时间
operLog.setCostTime(System.currentTimeMillis() - TIME_THREADLOCAL.get());
// 保存数据库 // 保存数据库
asyncLogService.saveSysLog(operLog); asyncLogService.saveSysLog(operLog);
} }
catch (Exception exp) catch (Exception exp)
{ {
// 记录本地异常日志 // 记录本地异常日志
log.error("==前置通知异常==");
log.error("异常信息:{}", exp.getMessage()); log.error("异常信息:{}", exp.getMessage());
exp.printStackTrace(); exp.printStackTrace();
} }
finally
{
TIME_THREADLOCAL.remove();
}
} }
/** /**
@ -135,7 +132,7 @@ public class LogAspect
* @param operLog 操作日志 * @param operLog 操作日志
* @throws Exception * @throws Exception
*/ */
public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOperLog operLog, Object jsonResult) throws Exception public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOperLog operLog) throws Exception
{ {
// 设置action动作 // 设置action动作
operLog.setBusinessType(log.businessType().ordinal()); operLog.setBusinessType(log.businessType().ordinal());
@ -147,12 +144,7 @@ public class LogAspect
if (log.isSaveRequestData()) if (log.isSaveRequestData())
{ {
// 获取参数的信息传入到数据库中 // 获取参数的信息传入到数据库中
setRequestValue(joinPoint, operLog, log.excludeParamNames()); setRequestValue(joinPoint, operLog);
}
// 是否需要保存response参数和值
if (log.isSaveResponseData() && StringUtils.isNotNull(jsonResult))
{
operLog.setJsonResult(StringUtils.substring(JSON.toJSONString(jsonResult), 0, 2000));
} }
} }
@ -162,37 +154,47 @@ public class LogAspect
* @param operLog 操作日志 * @param operLog 操作日志
* @throws Exception 异常 * @throws Exception 异常
*/ */
private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog, String[] excludeParamNames) throws Exception private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog) throws Exception
{ {
String requestMethod = operLog.getRequestMethod(); String requestMethod = operLog.getRequestMethod();
Map<?, ?> paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest()); if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod))
if (StringUtils.isEmpty(paramsMap)
&& (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)))
{ {
String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames); String params = argsArrayToString(joinPoint.getArgs());
operLog.setOperParam(StringUtils.substring(params, 0, 2000)); operLog.setOperParam(StringUtils.substring(params, 0, 2000));
} }
else }
/**
* 是否存在注解如果存在就获取
*/
private Log getAnnotationLog(JoinPoint joinPoint) throws Exception
{
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
if (method != null)
{ {
operLog.setOperParam(StringUtils.substring(JSON.toJSONString(paramsMap, excludePropertyPreFilter(excludeParamNames)), 0, 2000)); return method.getAnnotation(Log.class);
} }
return null;
} }
/** /**
* 参数拼装 * 参数拼装
*/ */
private String argsArrayToString(Object[] paramsArray, String[] excludeParamNames) private String argsArrayToString(Object[] paramsArray)
{ {
String params = ""; String params = "";
if (paramsArray != null && paramsArray.length > 0) if (paramsArray != null && paramsArray.length > 0)
{ {
for (Object o : paramsArray) for (int i = 0; i < paramsArray.length; i++)
{ {
if (StringUtils.isNotNull(o) && !isFilterObject(o)) if (StringUtils.isNotNull(paramsArray[i]) && !isFilterObject(paramsArray[i]))
{ {
try try
{ {
String jsonObj = JSON.toJSONString(o, excludePropertyPreFilter(excludeParamNames)); Object jsonObj = JSON.toJSON(paramsArray[i]);
params += jsonObj.toString() + " "; params += jsonObj.toString() + " ";
} }
catch (Exception e) catch (Exception e)
@ -204,14 +206,6 @@ public class LogAspect
return params.trim(); return params.trim();
} }
/**
* 忽略敏感属性
*/
public PropertyPreExcludeFilter excludePropertyPreFilter(String[] excludeParamNames)
{
return new PropertyPreExcludeFilter().addExcludes(ArrayUtils.addAll(EXCLUDE_PROPERTIES, excludeParamNames));
}
/** /**
* 判断是否需要过滤的对象 * 判断是否需要过滤的对象
* *
@ -229,17 +223,17 @@ public class LogAspect
else if (Collection.class.isAssignableFrom(clazz)) else if (Collection.class.isAssignableFrom(clazz))
{ {
Collection collection = (Collection) o; Collection collection = (Collection) o;
for (Object value : collection) for (Iterator iter = collection.iterator(); iter.hasNext();)
{ {
return value instanceof MultipartFile; return iter.next() instanceof MultipartFile;
} }
} }
else if (Map.class.isAssignableFrom(clazz)) else if (Map.class.isAssignableFrom(clazz))
{ {
Map map = (Map) o; Map map = (Map) o;
for (Object value : map.entrySet()) for (Iterator iter = map.entrySet().iterator(); iter.hasNext();)
{ {
Map.Entry entry = (Map.Entry) value; Map.Entry entry = (Map.Entry) iter.next();
return entry.getValue() instanceof MultipartFile; return entry.getValue() instanceof MultipartFile;
} }
} }

View File

@ -1,24 +0,0 @@
package com.ruoyi.common.log.filter;
import com.alibaba.fastjson2.filter.SimplePropertyPreFilter;
/**
* 排除JSON敏感属性
*
* @author ruoyi
*/
public class PropertyPreExcludeFilter extends SimplePropertyPreFilter
{
public PropertyPreExcludeFilter()
{
}
public PropertyPreExcludeFilter addExcludes(String... filters)
{
for (int i = 0; i < filters.length; i++)
{
this.getExcludes().add(filters[i]);
}
return this;
}
}

View File

@ -22,7 +22,7 @@ public class AsyncLogService
* 保存系统日志记录 * 保存系统日志记录
*/ */
@Async @Async
public void saveSysLog(SysOperLog sysOperLog) throws Exception public void saveSysLog(SysOperLog sysOperLog)
{ {
remoteLogService.saveLog(sysOperLog, SecurityConstants.INNER); remoteLogService.saveLog(sysOperLog, SecurityConstants.INNER);
} }

View File

@ -0,0 +1,3 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.ruoyi.common.log.service.AsyncLogService,\
com.ruoyi.common.log.aspect.LogAspect

View File

@ -1,2 +0,0 @@
com.ruoyi.common.log.service.AsyncLogService
com.ruoyi.common.log.aspect.LogAspect

Some files were not shown because too many files have changed in this diff Show More