Merge remote-tracking branch 'origin/master'

# Conflicts:
#	ruoyi-modules/ruoyi-gen/src/main/resources/bootstrap.yml
#	ruoyi-modules/ruoyi-job/src/main/resources/bootstrap.yml
#	ruoyi-modules/ruoyi-system/src/main/resources/bootstrap.yml
#	sql/ry_config_20210128.sql
pull/47/head
Chuanjian Lai 5 years ago
commit 32a8f7effb

@ -1,13 +1,16 @@
## 平台简介 ## 平台简介
若依是一套全部开源的快速开发平台,毫无保留给个人及企业免费使用。
* 采用前后端分离的模式,微服务版本前端(基于 [RuoYi-Vue](https://gitee.com/y_project/RuoYi-Vue))。 * 采用前后端分离的模式,微服务版本前端(基于 [RuoYi-Vue](https://gitee.com/y_project/RuoYi-Vue))。
* 后端采用Spring Boot、Spring Cloud & Alibaba。 * 后端采用Spring Boot、Spring Cloud & Alibaba。
* 注册中心、配置中心选型Nacos权限认证使用OAuth2。 * 注册中心、配置中心选型Nacos权限认证使用Redis。
* 流量控制框架选型Sentinel。 * 流量控制框架选型Sentinel分布式事务选型Seata。
* 感谢[ruoyi-cloud-design](https://gitee.com/zhangmrit/ruoyi-cloud)[pig](https://gitee.com/log4j/pig)。
* 如需不分离应用,请移步 [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)
* 阿里云优惠券:[点我进入](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)   * 阿里云折扣场:[点我进入](http://aly.ruoyi.vip),腾讯云秒杀场:[点我进入](http://txy.ruoyi.vip)  
* 阿里云优惠券:[点我领取](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)  
#### 友情链接 [若依/RuoYi-Cloud](https://gitee.com/zhangmrit/ruoyi-cloud) Ant Design版本。
## 系统模块 ## 系统模块
@ -21,6 +24,7 @@ com.ruoyi
├── ruoyi-common // 通用模块 ├── ruoyi-common // 通用模块
│ └── ruoyi-common-core // 核心模块 │ └── ruoyi-common-core // 核心模块
│ └── ruoyi-common-datascope // 权限范围 │ └── ruoyi-common-datascope // 权限范围
│ └── ruoyi-common-datasource // 多数据源
│ └── ruoyi-common-log // 日志记录 │ └── ruoyi-common-log // 日志记录
│ └── ruoyi-common-redis // 缓存服务 │ └── ruoyi-common-redis // 缓存服务
│ └── ruoyi-common-security // 安全模块 │ └── ruoyi-common-security // 安全模块
@ -29,6 +33,7 @@ com.ruoyi
│ └── ruoyi-system // 系统模块 [9201] │ └── ruoyi-system // 系统模块 [9201]
│ └── ruoyi-gen // 代码生成 [9202] │ └── ruoyi-gen // 代码生成 [9202]
│ └── ruoyi-job // 定时任务 [9203] │ └── ruoyi-job // 定时任务 [9203]
│ └── ruoyi-file // 文件服务 [9300]
├── ruoyi-visual // 图形化管理模块 ├── ruoyi-visual // 图形化管理模块
│ └── ruoyi-visual-monitor // 监控中心 [9100] │ └── ruoyi-visual-monitor // 监控中心 [9100]
├──pom.xml // 公共依赖 ├──pom.xml // 公共依赖
@ -36,7 +41,7 @@ com.ruoyi
## 架构图 ## 架构图
<img src="https://oscimg.oschina.net/oscnet/up-aaa2d885b0fba37e52b56f0948edde1c4fe.png"/> <img src="https://oscimg.oschina.net/oscnet/up-82e9722ecb846786405a904bafcf19f73f3.png"/>
## 内置功能 ## 内置功能
@ -74,27 +79,27 @@ com.ruoyi
<td><img src="https://oscimg.oschina.net/oscnet/1cbcf0e6f257c7d3a063c0e3f2ff989e4b3.jpg"/></td> <td><img src="https://oscimg.oschina.net/oscnet/1cbcf0e6f257c7d3a063c0e3f2ff989e4b3.jpg"/></td>
</tr> </tr>
<tr> <tr>
<td><img src="https://oscimg.oschina.net/oscnet/707825ad3f29de74a8d6d02fbd73ad631ea.jpg"/></td> <td><img src="https://oscimg.oschina.net/oscnet/up-8074972883b5ba0622e13246738ebba237a.png"/></td>
<td><img src="https://oscimg.oschina.net/oscnet/46be40cc6f01aa300eed53a19b5012bf484.jpg"/></td> <td><img src="https://oscimg.oschina.net/oscnet/up-9f88719cdfca9af2e58b352a20e23d43b12.png"/></td>
</tr> </tr>
<tr> <tr>
<td><img src="https://oscimg.oschina.net/oscnet/4284796d4cea240d181b8f2201813dda710.jpg"/></td> <td><img src="https://oscimg.oschina.net/oscnet/up-39bf2584ec3a529b0d5a3b70d15c9b37646.png"/></td>
<td><img src="https://oscimg.oschina.net/oscnet/3ecfac87a049f7fe36abbcaafb2c40d36cf.jpg"/></td> <td><img src="https://oscimg.oschina.net/oscnet/up-4148b24f58660a9dc347761e4cf6162f28f.png"/></td>
</tr> </tr>
<tr> <tr>
<td><img src="https://oscimg.oschina.net/oscnet/71c2d48905221a09a728df4aff4160b8607.jpg"/></td> <td><img src="https://oscimg.oschina.net/oscnet/up-b2d62ceb95d2dd9b3fbe157bb70d26001e9.png"/></td>
<td><img src="https://oscimg.oschina.net/oscnet/c14c1ee9a64a6a9c2c22f67d43198767dbe.jpg"/></td> <td><img src="https://oscimg.oschina.net/oscnet/up-d67451d308b7a79ad6819723396f7c3d77a.png"/></td>
</tr> </tr>
<tr> <tr>
<td><img src="https://oscimg.oschina.net/oscnet/5e8c387724954459291aafd5eb52b456f53.jpg"/></td> <td><img src="https://oscimg.oschina.net/oscnet/5e8c387724954459291aafd5eb52b456f53.jpg"/></td>
<td><img src="https://oscimg.oschina.net/oscnet/644e78da53c2e92a95dfda4f76e6d117c4b.jpg"/></td> <td><img src="https://oscimg.oschina.net/oscnet/644e78da53c2e92a95dfda4f76e6d117c4b.jpg"/></td>
</tr> </tr>
<tr> <tr>
<td><img src="https://oscimg.oschina.net/oscnet/fdea1d8bb8625c27bf964176a2c8ebc6945.jpg"/></td> <td><img src="https://oscimg.oschina.net/oscnet/up-8370a0d02977eebf6dbf854c8450293c937.png"/></td>
<td><img src="https://oscimg.oschina.net/oscnet/509d2708cfd762b6e6339364cac1cc1970c.jpg"/></td> <td><img src="https://oscimg.oschina.net/oscnet/up-49003ed83f60f633e7153609a53a2b644f7.png"/></td>
</tr> </tr>
<tr> <tr>
<td><img src="https://oscimg.oschina.net/oscnet/up-f1fd681cc9d295db74e85ad6d2fe4389454.png"/></td> <td><img src="https://oscimg.oschina.net/oscnet/up-d4fe726319ece268d4746602c39cffc0621.png"/></td>
<td><img src="https://oscimg.oschina.net/oscnet/up-c195234bbcd30be6927f037a6755e6ab69c.png"/></td> <td><img src="https://oscimg.oschina.net/oscnet/up-c195234bbcd30be6927f037a6755e6ab69c.png"/></td>
</tr> </tr>
<tr> <tr>
@ -102,12 +107,12 @@ com.ruoyi
<td><img src="https://oscimg.oschina.net/oscnet/up-92ffb7f3835855cff100fa0f754a6be0d99.png"/></td> <td><img src="https://oscimg.oschina.net/oscnet/up-92ffb7f3835855cff100fa0f754a6be0d99.png"/></td>
</tr> </tr>
<tr> <tr>
<td><img src="https://oscimg.oschina.net/oscnet/up-d69a19493e5aff7ecdd5d02410862bcea40.png"/></td> <td><img src="https://oscimg.oschina.net/oscnet/up-ff9e3066561574aca73005c5730c6a41f15.png"/></td>
<td><img src="https://oscimg.oschina.net/oscnet/up-6d73c2140ce694e3de4c05035fdc1868d4c.png"/></td> <td><img src="https://oscimg.oschina.net/oscnet/up-5e4daac0bb59612c5038448acbcef235e3a.png"/></td>
</tr> </tr>
</table> </table>
## 若依微服务交流群 ## 若依微服务交流群
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群 [![加入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) 点击按钮入群。

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

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

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

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

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

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

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

@ -6,31 +6,35 @@
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi</artifactId> <artifactId>ruoyi</artifactId>
<version>2.0.0</version> <version>2.5.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>2.0.0</ruoyi.version> <ruoyi.version>2.5.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.2.6.RELEASE</spring-boot.version> <spring-boot.version>2.3.7.RELEASE</spring-boot.version>
<spring-cloud.version>Hoxton.SR4</spring-cloud.version> <spring-cloud.version>Hoxton.SR9</spring-cloud.version>
<spring-boot-admin.version>2.2.3</spring-boot-admin.version> <spring-cloud-alibaba.version>2.2.5.RELEASE</spring-cloud-alibaba.version>
<spring-boot.mybatis>2.1.2</spring-boot.mybatis> <spring-boot-admin.version>2.3.1</spring-boot-admin.version>
<nacos.version>1.3.0</nacos.version> <spring-boot.mybatis>2.1.3</spring-boot.mybatis>
<swagger.fox.version>2.9.2</swagger.fox.version> <swagger.fox.version>2.9.2</swagger.fox.version>
<swagger.core.version>1.5.24</swagger.core.version> <swagger.core.version>1.5.24</swagger.core.version>
<tobato.version>1.26.5</tobato.version>
<kaptcha.version>2.3.2</kaptcha.version> <kaptcha.version>2.3.2</kaptcha.version>
<pagehelper.boot.version>1.2.12</pagehelper.boot.version> <pagehelper.boot.version>1.3.0</pagehelper.boot.version>
<druid.version>1.2.4</druid.version>
<dynamic-ds.version>3.2.1</dynamic-ds.version>
<commons.io.version>2.5</commons.io.version> <commons.io.version>2.5</commons.io.version>
<commons.fileupload.version>1.3.3</commons.fileupload.version> <commons.fileupload.version>1.3.3</commons.fileupload.version>
<velocity.version>1.7</velocity.version> <velocity.version>1.7</velocity.version>
<fastjson.version>1.2.70</fastjson.version> <fastjson.version>1.2.75</fastjson.version>
<poi.version>3.17</poi.version> <minio.version>8.0.3</minio.version>
<poi.version>4.1.2</poi.version>
<common-pool.version>2.6.2</common-pool.version> <common-pool.version>2.6.2</common-pool.version>
</properties> </properties>
@ -51,18 +55,11 @@
<dependency> <dependency>
<groupId>com.alibaba.cloud</groupId> <groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId> <artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.1.RELEASE</version> <version>${spring-cloud-alibaba.version}</version>
<type>pom</type> <type>pom</type>
<scope>import</scope> <scope>import</scope>
</dependency> </dependency>
<!-- Alibaba Nacos 配置 -->
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>${nacos.version}</version>
</dependency>
<!-- SpringBoot 依赖配置 --> <!-- SpringBoot 依赖配置 -->
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
@ -79,6 +76,13 @@
<version>${spring-boot-admin.version}</version> <version>${spring-boot-admin.version}</version>
</dependency> </dependency>
<!-- FastDFS 分布式文件系统 -->
<dependency>
<groupId>com.github.tobato</groupId>
<artifactId>fastdfs-client</artifactId>
<version>${tobato.version}</version>
</dependency>
<!-- Mybatis 依赖配置 --> <!-- Mybatis 依赖配置 -->
<dependency> <dependency>
<groupId>org.mybatis.spring.boot</groupId> <groupId>org.mybatis.spring.boot</groupId>
@ -182,6 +186,13 @@
<version>${ruoyi.version}</version> <version>${ruoyi.version}</version>
</dependency> </dependency>
<!-- 多数据源 -->
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common-datasource</artifactId>
<version>${ruoyi.version}</version>
</dependency>
<!-- 日志记录 --> <!-- 日志记录 -->
<dependency> <dependency>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>

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

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

@ -0,0 +1,29 @@
package com.ruoyi.system.api;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.multipart.MultipartFile;
import com.ruoyi.common.core.constant.ServiceNameConstants;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.system.api.domain.SysFile;
import com.ruoyi.system.api.factory.RemoteFileFallbackFactory;
/**
*
*
* @author ruoyi
*/
@FeignClient(contextId = "remoteFileService", value = ServiceNameConstants.FILE_SERVICE, fallbackFactory = RemoteFileFallbackFactory.class)
public interface RemoteFileService
{
/**
*
*
* @param file
* @return
*/
@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public R<SysFile> upload(@RequestPart(value = "file") MultipartFile file);
}

@ -6,7 +6,7 @@ import org.springframework.web.bind.annotation.PathVariable;
import com.ruoyi.common.core.constant.ServiceNameConstants; import com.ruoyi.common.core.constant.ServiceNameConstants;
import com.ruoyi.common.core.domain.R; import com.ruoyi.common.core.domain.R;
import com.ruoyi.system.api.factory.RemoteUserFallbackFactory; import com.ruoyi.system.api.factory.RemoteUserFallbackFactory;
import com.ruoyi.system.api.model.UserInfo; import com.ruoyi.system.api.model.LoginUser;
/** /**
* *
@ -23,5 +23,5 @@ public interface RemoteUserService
* @return * @return
*/ */
@GetMapping(value = "/user/info/{username}") @GetMapping(value = "/user/info/{username}")
public R<UserInfo> getUserInfo(@PathVariable("username") String username); public R<LoginUser> getUserInfo(@PathVariable("username") String username);
} }

@ -0,0 +1,50 @@
package com.ruoyi.system.api.domain;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
/**
*
*
* @author ruoyi
*/
public class SysFile
{
/**
*
*/
private String name;
/**
*
*/
private String url;
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public String getUrl()
{
return url;
}
public void setUrl(String url)
{
this.url = url;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("name", getName())
.append("url", getUrl())
.toString();
}
}

@ -37,6 +37,12 @@ public class SysRole extends BaseEntity
@Excel(name = "数据范围", readConverterExp = "1=所有数据权限,2=自定义数据权限,3=本部门数据权限,4=本部门及以下数据权限") @Excel(name = "数据范围", readConverterExp = "1=所有数据权限,2=自定义数据权限,3=本部门数据权限,4=本部门及以下数据权限")
private String dataScope; private String dataScope;
/** 菜单树选择项是否关联显示( 0父子不互相关联显示 1父子互相关联显示 */
private boolean menuCheckStrictly;
/** 部门树选择项是否关联显示0父子不互相关联显示 1父子互相关联显示 */
private boolean deptCheckStrictly;
/** 角色状态0正常 1停用 */ /** 角色状态0正常 1停用 */
@Excel(name = "角色状态", readConverterExp = "0=正常,1=停用") @Excel(name = "角色状态", readConverterExp = "0=正常,1=停用")
private String status; private String status;
@ -128,6 +134,26 @@ public class SysRole extends BaseEntity
this.dataScope = dataScope; this.dataScope = dataScope;
} }
public boolean isMenuCheckStrictly()
{
return menuCheckStrictly;
}
public void setMenuCheckStrictly(boolean menuCheckStrictly)
{
this.menuCheckStrictly = menuCheckStrictly;
}
public boolean isDeptCheckStrictly()
{
return deptCheckStrictly;
}
public void setDeptCheckStrictly(boolean deptCheckStrictly)
{
this.deptCheckStrictly = deptCheckStrictly;
}
public String getStatus() public String getStatus()
{ {
return status; return status;
@ -185,6 +211,8 @@ public class SysRole extends BaseEntity
.append("roleKey", getRoleKey()) .append("roleKey", getRoleKey())
.append("roleSort", getRoleSort()) .append("roleSort", getRoleSort())
.append("dataScope", getDataScope()) .append("dataScope", getDataScope())
.append("menuCheckStrictly", isMenuCheckStrictly())
.append("deptCheckStrictly", isDeptCheckStrictly())
.append("status", getStatus()) .append("status", getStatus())
.append("delFlag", getDelFlag()) .append("delFlag", getDelFlag())
.append("createBy", getCreateBy()) .append("createBy", getCreateBy())

@ -67,12 +67,12 @@ public class SysUser extends BaseEntity
/** 删除标志0代表存在 2代表删除 */ /** 删除标志0代表存在 2代表删除 */
private String delFlag; private String delFlag;
/** 最后登IP */ /** 最后登IP */
@Excel(name = "最后登IP", type = Type.EXPORT) @Excel(name = "最后登IP", type = Type.EXPORT)
private String loginIp; private String loginIp;
/** 最后登时间 */ /** 最后登时间 */
@Excel(name = "最后登时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss", type = Type.EXPORT) @Excel(name = "最后登时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss", type = Type.EXPORT)
private Date loginDate; private Date loginDate;
/** 部门对象 */ /** 部门对象 */

@ -0,0 +1,35 @@
package com.ruoyi.system.api.factory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.system.api.RemoteFileService;
import com.ruoyi.system.api.domain.SysFile;
import feign.hystrix.FallbackFactory;
/**
*
*
* @author ruoyi
*/
@Component
public class RemoteFileFallbackFactory implements FallbackFactory<RemoteFileService>
{
private static final Logger log = LoggerFactory.getLogger(RemoteFileFallbackFactory.class);
@Override
public RemoteFileService create(Throwable throwable)
{
log.error("文件服务调用失败:{}", throwable.getMessage());
return new RemoteFileService()
{
@Override
public R<SysFile> upload(MultipartFile file)
{
return R.fail("上传文件失败:" + throwable.getMessage());
}
};
}
}

@ -5,7 +5,7 @@ import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.ruoyi.common.core.domain.R; import com.ruoyi.common.core.domain.R;
import com.ruoyi.system.api.RemoteUserService; import com.ruoyi.system.api.RemoteUserService;
import com.ruoyi.system.api.model.UserInfo; import com.ruoyi.system.api.model.LoginUser;
import feign.hystrix.FallbackFactory; import feign.hystrix.FallbackFactory;
/** /**
@ -25,9 +25,9 @@ public class RemoteUserFallbackFactory implements FallbackFactory<RemoteUserServ
return new RemoteUserService() return new RemoteUserService()
{ {
@Override @Override
public R<UserInfo> getUserInfo(String username) public R<LoginUser> getUserInfo(String username)
{ {
return null; return R.fail("获取用户失败:" + throwable.getMessage());
} }
}; };
} }

@ -0,0 +1,150 @@
package com.ruoyi.system.api.model;
import java.io.Serializable;
import java.util.Set;
import com.ruoyi.system.api.domain.SysUser;
/**
*
*
* @author ruoyi
*/
public class LoginUser implements Serializable
{
private static final long serialVersionUID = 1L;
/**
*
*/
private String token;
/**
* id
*/
private Long userid;
/**
*
*/
private String username;
/**
*
*/
private Long loginTime;
/**
*
*/
private Long expireTime;
/**
* IP
*/
private String ipaddr;
/**
*
*/
private Set<String> permissions;
/**
*
*/
private Set<String> roles;
/**
*
*/
private SysUser sysUser;
public String getToken()
{
return token;
}
public void setToken(String token)
{
this.token = token;
}
public Long getUserid()
{
return userid;
}
public void setUserid(Long userid)
{
this.userid = userid;
}
public String getUsername()
{
return username;
}
public void setUsername(String username)
{
this.username = username;
}
public Long getLoginTime()
{
return loginTime;
}
public void setLoginTime(Long loginTime)
{
this.loginTime = loginTime;
}
public Long getExpireTime()
{
return expireTime;
}
public void setExpireTime(Long expireTime)
{
this.expireTime = expireTime;
}
public String getIpaddr()
{
return ipaddr;
}
public void setIpaddr(String ipaddr)
{
this.ipaddr = ipaddr;
}
public Set<String> getPermissions()
{
return permissions;
}
public void setPermissions(Set<String> permissions)
{
this.permissions = permissions;
}
public Set<String> getRoles()
{
return roles;
}
public void setRoles(Set<String> roles)
{
this.roles = roles;
}
public SysUser getSysUser()
{
return sysUser;
}
public void setSysUser(SysUser sysUser)
{
this.sysUser = sysUser;
}
}

@ -1,60 +0,0 @@
package com.ruoyi.system.api.model;
import java.io.Serializable;
import java.util.Set;
import com.ruoyi.system.api.domain.SysUser;
/**
*
*
* @author ruoyi
*/
public class UserInfo implements Serializable
{
private static final long serialVersionUID = 1L;
/**
*
*/
private SysUser sysUser;
/**
*
*/
private Set<String> permissions;
/**
*
*/
private Set<String> roles;
public SysUser getSysUser()
{
return sysUser;
}
public void setSysUser(SysUser sysUser)
{
this.sysUser = sysUser;
}
public Set<String> getPermissions()
{
return permissions;
}
public void setPermissions(Set<String> permissions)
{
this.permissions = permissions;
}
public Set<String> getRoles()
{
return roles;
}
public void setRoles(Set<String> roles)
{
this.roles = roles;
}
}

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

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi</artifactId> <artifactId>ruoyi</artifactId>
<version>2.0.0</version> <version>2.5.0</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@ -28,10 +28,10 @@
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency> </dependency>
<!-- SpringCloud Netflix Hystrix --> <!-- SpringCloud Ailibaba Sentinel -->
<dependency> <dependency>
<groupId>org.springframework.cloud</groupId> <groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency> </dependency>
<!-- SpringBoot Web --> <!-- SpringBoot Web -->
@ -40,6 +40,12 @@
<artifactId>spring-boot-starter-web</artifactId> <artifactId>spring-boot-starter-web</artifactId>
</dependency> </dependency>
<!-- SpringBoot Actuator -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- Mysql Connector --> <!-- Mysql Connector -->
<dependency> <dependency>
<groupId>mysql</groupId> <groupId>mysql</groupId>
@ -52,15 +58,10 @@
<artifactId>ruoyi-common-security</artifactId> <artifactId>ruoyi-common-security</artifactId>
</dependency> </dependency>
<!-- RuoYi Common Redis-->
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common-redis</artifactId>
</dependency>
</dependencies> </dependencies>
<build> <build>
<finalName>${project.artifactId}</finalName>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>

@ -1,131 +0,0 @@
package com.ruoyi.auth.config;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
import com.ruoyi.auth.exception.CustomWebResponseExceptionTranslator;
import com.ruoyi.common.core.constant.CacheConstants;
import com.ruoyi.common.core.constant.SecurityConstants;
import com.ruoyi.common.security.domain.LoginUser;
import com.ruoyi.common.security.service.RedisClientDetailsService;
/**
* OAuth2
*
* @author ruoyi
*/
@Configuration
@EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter
{
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private DataSource dataSource;
@Autowired
private RedisConnectionFactory redisConnectionFactory;
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private TokenEnhancer tokenEnhancer;
/**
*
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
{
endpoints
// 请求方式
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)
// 指定token存储位置
.tokenStore(tokenStore())
// 自定义生成令牌
.tokenEnhancer(tokenEnhancer)
// 用户账号密码认证
.userDetailsService(userDetailsService)
// 指定认证管理器
.authenticationManager(authenticationManager)
// 是否重复使用 refresh_token
.reuseRefreshTokens(false)
// 自定义异常处理
.exceptionTranslator(new CustomWebResponseExceptionTranslator());
}
/**
* (Token Endpoint)
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer)
{
oauthServer.allowFormAuthenticationForClients().checkTokenAccess("permitAll()");
}
/**
* ClientDetails
*/
public RedisClientDetailsService clientDetailsService()
{
RedisClientDetailsService clientDetailsService = new RedisClientDetailsService(dataSource);
return clientDetailsService;
}
/**
*
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception
{
clients.withClientDetails(clientDetailsService());
}
/**
* Redis
*/
@Bean
public TokenStore tokenStore()
{
RedisTokenStore tokenStore = new RedisTokenStore(redisConnectionFactory);
tokenStore.setPrefix(CacheConstants.OAUTH_ACCESS);
return tokenStore;
}
/**
*
*/
@Bean
public TokenEnhancer tokenEnhancer()
{
return (accessToken, authentication) -> {
if (authentication.getUserAuthentication() != null)
{
Map<String, Object> additionalInformation = new LinkedHashMap<String, Object>();
LoginUser user = (LoginUser) authentication.getUserAuthentication().getPrincipal();
additionalInformation.put(SecurityConstants.DETAILS_USER_ID, user.getUserId());
additionalInformation.put(SecurityConstants.DETAILS_USERNAME, user.getUsername());
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInformation);
}
return accessToken;
};
}
}

@ -1,59 +0,0 @@
package com.ruoyi.auth.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* Security
* Oauth2Security WebSecurityConfigResourceServerConfig
*
* @author ruoyi
*/
@Order(99)
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter
{
@Autowired
private UserDetailsService userDetailsService;
@Bean
public PasswordEncoder passwordEncoder()
{
return new BCryptPasswordEncoder();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception
{
return super.authenticationManagerBean();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception
{
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception
{
http
.authorizeRequests()
.antMatchers(
"/actuator/**",
"/oauth/*",
"/token/**").permitAll()
.anyRequest().authenticated()
.and().csrf().disable();
}
}

@ -1,20 +1,17 @@
package com.ruoyi.auth.controller; package com.ruoyi.auth.controller;
import java.util.Map; import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2RefreshToken;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.core.constant.Constants; import com.ruoyi.auth.form.LoginBody;
import com.ruoyi.common.core.constant.SecurityConstants; 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.StringUtils; import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.system.api.RemoteLogService; import com.ruoyi.common.security.service.TokenService;
import com.ruoyi.system.api.model.LoginUser;
/** /**
* token * token
@ -22,42 +19,47 @@ import com.ruoyi.system.api.RemoteLogService;
* @author ruoyi * @author ruoyi
*/ */
@RestController @RestController
@RequestMapping("/token")
public class TokenController public class TokenController
{ {
@Autowired @Autowired
private TokenStore tokenStore; private TokenService tokenService;
@Autowired @Autowired
private RemoteLogService remoteLogService; private SysLoginService sysLoginService;
@DeleteMapping("/logout") @PostMapping("login")
public R<?> logout(@RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authHeader) public R<?> login(@RequestBody LoginBody form)
{ {
if (StringUtils.isEmpty(authHeader)) // 用户登录
{ LoginUser userInfo = sysLoginService.login(form.getUsername(), form.getPassword());
return R.ok(); // 获取登录token
return R.ok(tokenService.createToken(userInfo));
} }
String tokenValue = authHeader.replace(OAuth2AccessToken.BEARER_TYPE, StringUtils.EMPTY).trim(); @DeleteMapping("logout")
OAuth2AccessToken accessToken = tokenStore.readAccessToken(tokenValue); public R<?> logout(HttpServletRequest request)
if (accessToken == null || StringUtils.isEmpty(accessToken.getValue())) {
LoginUser loginUser = tokenService.getLoginUser(request);
if (StringUtils.isNotNull(loginUser))
{ {
String username = loginUser.getUsername();
// 删除用户缓存记录
tokenService.delLoginUser(loginUser.getToken());
// 记录用户退出日志
sysLoginService.logout(username);
}
return R.ok(); return R.ok();
} }
// 清空 access token @PostMapping("refresh")
tokenStore.removeAccessToken(accessToken); public R<?> refresh(HttpServletRequest request)
// 清空 refresh token
OAuth2RefreshToken refreshToken = accessToken.getRefreshToken();
tokenStore.removeRefreshToken(refreshToken);
Map<String, ?> map = accessToken.getAdditionalInformation();
if (map.containsKey(SecurityConstants.DETAILS_USERNAME))
{ {
String username = (String) map.get(SecurityConstants.DETAILS_USERNAME); LoginUser loginUser = tokenService.getLoginUser(request);
// 记录用户退出日志 if (StringUtils.isNotNull(loginUser))
remoteLogService.saveLogininfor(username, Constants.LOGOUT, "退出成功"); {
// 刷新令牌有效期
tokenService.refreshToken(loginUser);
return R.ok();
} }
return R.ok(); return R.ok();
} }

@ -1,21 +0,0 @@
package com.ruoyi.auth.controller;
import java.security.Principal;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
*
*
* @author ruoyi
*/
@RestController
@RequestMapping("/oauth")
public class UserController
{
@RequestMapping("/user")
public Principal user(Principal user)
{
return user;
}
}

@ -1,20 +0,0 @@
package com.ruoyi.auth.exception;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
/**
* oauth2
*
* @author ruoyi
**/
@JsonSerialize(using = CustomOauthExceptionSerializer.class)
public class CustomOauthException extends OAuth2Exception
{
private static final long serialVersionUID = 1L;
public CustomOauthException(String msg)
{
super(msg);
}
}

@ -1,48 +0,0 @@
package com.ruoyi.auth.exception;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import com.ruoyi.common.core.constant.HttpStatus;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.core.web.domain.AjaxResult;
/**
*
*
* @author ruoyi
**/
public class CustomOauthExceptionSerializer extends StdSerializer<CustomOauthException>
{
private static final long serialVersionUID = 1L;
private static final Logger log = LoggerFactory.getLogger(CustomOauthExceptionSerializer.class);
public static final String BAD_CREDENTIALS = "Bad credentials";
public CustomOauthExceptionSerializer()
{
super(CustomOauthException.class);
}
@Override
public void serialize(CustomOauthException e, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
throws IOException
{
jsonGenerator.writeStartObject();
jsonGenerator.writeNumberField(AjaxResult.CODE_TAG, HttpStatus.ERROR);
if (StringUtils.equals(e.getMessage(), BAD_CREDENTIALS))
{
jsonGenerator.writeStringField(AjaxResult.MSG_TAG, "用户名或密码错误");
}
else
{
log.warn("oauth2 认证异常 {} ", e);
jsonGenerator.writeStringField(AjaxResult.MSG_TAG, e.getMessage());
}
jsonGenerator.writeEndObject();
}
}

@ -1,20 +0,0 @@
package com.ruoyi.auth.exception;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
/**
* OAuth2
*
* @author ruoyi
*/
public class CustomWebResponseExceptionTranslator implements WebResponseExceptionTranslator<OAuth2Exception>
{
@Override
public ResponseEntity<OAuth2Exception> translate(Exception e)
{
return ResponseEntity.status(HttpStatus.OK).body(new CustomOauthException(e.getMessage()));
}
}

@ -0,0 +1,39 @@
package com.ruoyi.auth.form;
/**
*
*
* @author ruoyi
*/
public class LoginBody
{
/**
*
*/
private String username;
/**
*
*/
private String password;
public String getUsername()
{
return username;
}
public void setUsername(String username)
{
this.username = username;
}
public String getPassword()
{
return password;
}
public void setPassword(String password)
{
this.password = password;
}
}

@ -1,39 +0,0 @@
package com.ruoyi.auth.handler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.security.authentication.event.AuthenticationSuccessEvent;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
import com.ruoyi.common.core.constant.Constants;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.security.domain.LoginUser;
import com.ruoyi.system.api.RemoteLogService;
/**
*
*
* @author ruoyi
*/
@Component
public class AuthenticationSuccessEventHandler implements ApplicationListener<AuthenticationSuccessEvent>
{
@Autowired
private RemoteLogService remoteLogService;
@Override
public void onApplicationEvent(AuthenticationSuccessEvent event)
{
Authentication authentication = (Authentication) event.getSource();
if (StringUtils.isNotEmpty(authentication.getAuthorities())
&& authentication.getPrincipal() instanceof LoginUser)
{
LoginUser user = (LoginUser) authentication.getPrincipal();
String username = user.getUsername();
// 记录用户登录日志
remoteLogService.saveLogininfor(username, Constants.LOGIN_SUCCESS, "登录成功");
}
}
}

@ -0,0 +1,95 @@
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.UserConstants;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.enums.UserStatus;
import com.ruoyi.common.core.exception.BaseException;
import com.ruoyi.common.core.utils.SecurityUtils;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.system.api.RemoteLogService;
import com.ruoyi.system.api.RemoteUserService;
import com.ruoyi.system.api.domain.SysUser;
import com.ruoyi.system.api.model.LoginUser;
/**
*
*
* @author ruoyi
*/
@Component
public class SysLoginService
{
@Autowired
private RemoteLogService remoteLogService;
@Autowired
private RemoteUserService remoteUserService;
/**
*
*/
public LoginUser login(String username, String password)
{
// 用户名或密码为空 错误
if (StringUtils.isAnyBlank(username, password))
{
remoteLogService.saveLogininfor(username, Constants.LOGIN_FAIL, "用户/密码必须填写");
throw new BaseException("用户/密码必须填写");
}
// 密码如果不在指定范围内 错误
if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
|| password.length() > UserConstants.PASSWORD_MAX_LENGTH)
{
remoteLogService.saveLogininfor(username, Constants.LOGIN_FAIL, "用户密码不在指定范围");
throw new BaseException("用户密码不在指定范围");
}
// 用户名不在指定范围内 错误
if (username.length() < UserConstants.USERNAME_MIN_LENGTH
|| username.length() > UserConstants.USERNAME_MAX_LENGTH)
{
remoteLogService.saveLogininfor(username, Constants.LOGIN_FAIL, "用户名不在指定范围");
throw new BaseException("用户名不在指定范围");
}
// 查询用户信息
R<LoginUser> userResult = remoteUserService.getUserInfo(username);
if (R.FAIL == userResult.getCode())
{
throw new BaseException(userResult.getMsg());
}
if (StringUtils.isNull(userResult) || StringUtils.isNull(userResult.getData()))
{
remoteLogService.saveLogininfor(username, Constants.LOGIN_FAIL, "登录用户不存在");
throw new BaseException("登录用户:" + username + " 不存在");
}
LoginUser userInfo = userResult.getData();
SysUser user = userResult.getData().getSysUser();
if (UserStatus.DELETED.getCode().equals(user.getDelFlag()))
{
remoteLogService.saveLogininfor(username, Constants.LOGIN_FAIL, "对不起,您的账号已被删除");
throw new BaseException("对不起,您的账号:" + username + " 已被删除");
}
if (UserStatus.DISABLE.getCode().equals(user.getStatus()))
{
remoteLogService.saveLogininfor(username, Constants.LOGIN_FAIL, "用户已停用,请联系管理员");
throw new BaseException("对不起,您的账号:" + username + " 已停用");
}
if (!SecurityUtils.matchesPassword(password, user.getPassword()))
{
remoteLogService.saveLogininfor(username, Constants.LOGIN_FAIL, "用户密码错误");
throw new BaseException("用户不存在/密码错误");
}
remoteLogService.saveLogininfor(username, Constants.LOGIN_SUCCESS, "登录成功");
return userInfo;
}
public void logout(String loginName)
{
remoteLogService.saveLogininfor(loginName, Constants.LOGOUT, "退出成功");
}
}

@ -21,4 +21,5 @@ spring:
# 配置文件格式 # 配置文件格式
file-extension: yml file-extension: yml
# 共享配置 # 共享配置
shared-dataids: application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} shared-configs:
- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<!-- 日志存放路径 -->
<property name="log.path" value="logs/ruoyi-auth" />
<!-- 日志输出格式 -->
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
<!-- 控制台输出 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>
<!-- 系统日志输出 -->
<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/info.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/info.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>INFO</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/error.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/error.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>ERROR</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 系统模块日志级别控制 -->
<logger name="com.ruoyi" level="info" />
<!-- Spring日志级别控制 -->
<logger name="org.springframework" level="warn" />
<root level="info">
<appender-ref ref="console" />
</root>
<!--系统操作日志-->
<root level="info">
<appender-ref ref="file_info" />
<appender-ref ref="file_error" />
</root>
</configuration>

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi</artifactId> <artifactId>ruoyi</artifactId>
<version>2.0.0</version> <version>2.5.0</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@ -15,6 +15,7 @@
<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>
</modules> </modules>
<artifactId>ruoyi-common</artifactId> <artifactId>ruoyi-common</artifactId>

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common</artifactId> <artifactId>ruoyi-common</artifactId>
<version>2.0.0</version> <version>2.5.0</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@ -47,10 +47,10 @@
<artifactId>pagehelper-spring-boot-starter</artifactId> <artifactId>pagehelper-spring-boot-starter</artifactId>
</dependency> </dependency>
<!-- Java Validation --> <!-- Hibernate Validator -->
<dependency> <dependency>
<groupId>javax.validation</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>validation-api</artifactId> <artifactId>spring-boot-starter-validation</artifactId>
</dependency> </dependency>
<!-- Jackson --> <!-- Jackson -->

@ -4,6 +4,7 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; 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;
/** /**
* Excel * Excel
@ -34,6 +35,21 @@ public @interface Excel
*/ */
public String readConverterExp() default ""; public String readConverterExp() default "";
/**
*
*/
public String separator() default ",";
/**
* BigDecimal :-1(BigDecimal)
*/
public int scale() default -1;
/**
* BigDecimal :BigDecimal.ROUND_HALF_EVEN
*/
public int roundingMode() default BigDecimal.ROUND_HALF_EVEN;
/** /**
* 0 1 * 0 1
*/ */
@ -79,6 +95,32 @@ public @interface Excel
*/ */
public String targetAttr() default ""; public String targetAttr() default "";
/**
* ,
*/
public boolean isStatistics() default false;
/**
* 0123
*/
Align align() default Align.AUTO;
public enum Align
{
AUTO(0), LEFT(1), CENTER(2), RIGHT(3);
private final int value;
Align(int value)
{
this.value = value;
}
public int value()
{
return this.value;
}
}
/** /**
* 012 * 012
*/ */
@ -102,7 +144,7 @@ public @interface Excel
public enum ColumnType public enum ColumnType
{ {
NUMERIC(0), STRING(1); NUMERIC(0), STRING(1), IMAGE(2);
private final int value; private final int value;
ColumnType(int value) ColumnType(int value)

@ -8,12 +8,32 @@ package com.ruoyi.common.core.constant;
public class CacheConstants public class CacheConstants
{ {
/** /**
* oauth *
*/ */
public static final String OAUTH_ACCESS = "oauth:access:"; public static final String HEADER = "Authorization";
/** /**
* oauth *
*/ */
public static final String CLIENT_DETAILS_KEY = "oauth:client:details"; public static final String TOKEN_PREFIX = "Bearer ";
/**
*
*/
public final static String LOGIN_TOKEN_KEY = "login_tokens:";
/**
* ID
*/
public static final String DETAILS_USER_ID = "user_id";
/**
*
*/
public static final String DETAILS_USERNAME = "username";
/**
*
*/
public static final String AUTHORIZATION_HEADER = "authorization";
} }

@ -85,7 +85,12 @@ public class Constants
/** /**
* *
*/ */
public static final Integer CAPTCHA_EXPIRATION = 2; public static final long CAPTCHA_EXPIRATION = 2;
/**
*
*/
public final static long TOKEN_EXPIRE = 720;
/** /**
* cache key * cache key

@ -13,6 +13,9 @@ public class GenConstants
/** 树表(增删改查) */ /** 树表(增删改查) */
public static final String TPL_TREE = "tree"; public static final String TPL_TREE = "tree";
/** 主子表(增删改查) */
public static final String TPL_SUB = "sub";
/** 树编码字段 */ /** 树编码字段 */
public static final String TREE_CODE = "treeCode"; public static final String TREE_CODE = "treeCode";
@ -22,16 +25,24 @@ public class GenConstants
/** 树名称字段 */ /** 树名称字段 */
public static final String TREE_NAME = "treeName"; public static final String TREE_NAME = "treeName";
/** 上级菜单ID字段 */
public static final String PARENT_MENU_ID = "parentMenuId";
/** 上级菜单名称字段 */
public static final String PARENT_MENU_NAME = "parentMenuName";
/** 数据库字符串类型 */ /** 数据库字符串类型 */
public static final String[] COLUMNTYPE_STR = { "char", "varchar", "narchar", "varchar2", "tinytext", "text", public static final String[] COLUMNTYPE_STR = { "char", "varchar", "nvarchar", "varchar2" };
"mediumtext", "longtext" };
/** 数据库文本类型 */
public static final String[] COLUMNTYPE_TEXT = { "tinytext", "text", "mediumtext", "longtext" };
/** 数据库时间类型 */ /** 数据库时间类型 */
public static final String[] COLUMNTYPE_TIME = { "datetime", "time", "date", "timestamp" }; public static final String[] COLUMNTYPE_TIME = { "datetime", "time", "date", "timestamp" };
/** 数据库数字类型 */ /** 数据库数字类型 */
public static final String[] COLUMNTYPE_NUMBER = { "tinyint", "smallint", "mediumint", "int", "number", "integer", public static final String[] COLUMNTYPE_NUMBER = { "tinyint", "smallint", "mediumint", "int", "number", "integer",
"bigint", "float", "float", "double", "decimal" }; "bigint", "float", "double", "decimal" };
/** 页面不需要编辑字段 */ /** 页面不需要编辑字段 */
public static final String[] COLUMNNAME_NOT_EDIT = { "id", "create_by", "create_time", "del_flag" }; public static final String[] COLUMNNAME_NOT_EDIT = { "id", "create_by", "create_time", "del_flag" };
@ -68,6 +79,15 @@ public class GenConstants
/** 日期控件 */ /** 日期控件 */
public static final String HTML_DATETIME = "datetime"; public static final String HTML_DATETIME = "datetime";
/** 图片上传控件 */
public static final String HTML_IMAGE_UPLOAD = "imageUpload";
/** 文件上传控件 */
public static final String HTML_FILE_UPLOAD = "fileUpload";
/** 富文本控件 */
public static final String HTML_EDITOR = "editor";
/** 字符串类型 */ /** 字符串类型 */
public static final String TYPE_STRING = "String"; public static final String TYPE_STRING = "String";

@ -1,56 +0,0 @@
package com.ruoyi.common.core.constant;
/**
*
*
* @author ruoyi
*/
public class SecurityConstants
{
/**
*
*/
public static final String BEARER_TOKEN_TYPE = "Bearer";
/**
* token url
*/
public static final String AUTH_TOKEN = "/oauth/token";
/**
* token url
*/
public static final String TOKEN_LOGOUT = "/token/logout";
/**
* ID
*/
public static final String DETAILS_USER_ID = "user_id";
/**
*
*/
public static final String DETAILS_USERNAME = "username";
/**
* sys_oauth_client_details client_idclient_secret
*/
public static final String CLIENT_FIELDS = "client_id, client_secret, resource_ids, scope, "
+ "authorized_grant_types, web_server_redirect_uri, authorities, access_token_validity, "
+ "refresh_token_validity, additional_information, autoapprove";
/**
* JdbcClientDetailsService
*/
public static final String BASE_FIND_STATEMENT = "select " + CLIENT_FIELDS + " from sys_oauth_client_details";
/**
* client_id
*/
public static final String DEFAULT_SELECT_STATEMENT = BASE_FIND_STATEMENT + " where client_id = ?";
/**
*
*/
public static final String DEFAULT_FIND_STATEMENT = BASE_FIND_STATEMENT + " order by client_id";
}

@ -16,4 +16,9 @@ public class ServiceNameConstants
* serviceid * serviceid
*/ */
public static final String SYSTEM_SERVICE = "ruoyi-system"; public static final String SYSTEM_SERVICE = "ruoyi-system";
/**
* serviceid
*/
public static final String FILE_SERVICE = "ruoyi-file";
} }

@ -54,7 +54,25 @@ public class UserConstants
/** Layout组件标识 */ /** Layout组件标识 */
public final static String LAYOUT = "Layout"; public final static String LAYOUT = "Layout";
/** ParentView组件标识 */
public final static String PARENT_VIEW = "ParentView";
/** 校验返回结果码 */ /** 校验返回结果码 */
public final static String UNIQUE = "0"; public final static String UNIQUE = "0";
public final static String NOT_UNIQUE = "1"; public final static String NOT_UNIQUE = "1";
/**
*
*/
public static final int USERNAME_MIN_LENGTH = 2;
public static final int USERNAME_MAX_LENGTH = 20;
/**
*
*/
public static final int PASSWORD_MIN_LENGTH = 5;
public static final int PASSWORD_MAX_LENGTH = 20;
} }

@ -0,0 +1,15 @@
package com.ruoyi.common.core.exception;
/**
*
*
* @author ruoyi
*/
public class PreAuthorizeException extends RuntimeException
{
private static final long serialVersionUID = 1L;
public PreAuthorizeException()
{
}
}

@ -0,0 +1,19 @@
package com.ruoyi.common.core.exception.file;
import com.ruoyi.common.core.exception.BaseException;
/**
*
*
* @author ruoyi
*/
public class FileException extends BaseException
{
private static final long serialVersionUID = 1L;
public FileException(String code, Object[] args)
{
super("file", code, args, null);
}
}

@ -0,0 +1,16 @@
package com.ruoyi.common.core.exception.file;
/**
*
*
* @author ruoyi
*/
public class FileNameLengthLimitExceededException extends FileException
{
private static final long serialVersionUID = 1L;
public FileNameLengthLimitExceededException(int defaultFileNameLength)
{
super("upload.filename.exceed.length", new Object[] { defaultFileNameLength });
}
}

@ -0,0 +1,16 @@
package com.ruoyi.common.core.exception.file;
/**
*
*
* @author ruoyi
*/
public class FileSizeLimitExceededException extends FileException
{
private static final long serialVersionUID = 1L;
public FileSizeLimitExceededException(long defaultMaxSize)
{
super("upload.exceed.maxSize", new Object[] { defaultMaxSize });
}
}

@ -0,0 +1,81 @@
package com.ruoyi.common.core.exception.file;
import java.util.Arrays;
import org.apache.commons.fileupload.FileUploadException;
/**
*
*
* @author ruoyi
*/
public class InvalidExtensionException extends FileUploadException
{
private static final long serialVersionUID = 1L;
private String[] allowedExtension;
private String extension;
private String filename;
public InvalidExtensionException(String[] allowedExtension, String extension, String filename)
{
super("filename : [" + filename + "], extension : [" + extension + "], allowed extension : [" + Arrays.toString(allowedExtension) + "]");
this.allowedExtension = allowedExtension;
this.extension = extension;
this.filename = filename;
}
public String[] getAllowedExtension()
{
return allowedExtension;
}
public String getExtension()
{
return extension;
}
public String getFilename()
{
return filename;
}
public static class InvalidImageExtensionException extends InvalidExtensionException
{
private static final long serialVersionUID = 1L;
public InvalidImageExtensionException(String[] allowedExtension, String extension, String filename)
{
super(allowedExtension, extension, filename);
}
}
public static class InvalidFlashExtensionException extends InvalidExtensionException
{
private static final long serialVersionUID = 1L;
public InvalidFlashExtensionException(String[] allowedExtension, String extension, String filename)
{
super(allowedExtension, extension, filename);
}
}
public static class InvalidMediaExtensionException extends InvalidExtensionException
{
private static final long serialVersionUID = 1L;
public InvalidMediaExtensionException(String[] allowedExtension, String extension, String filename)
{
super(allowedExtension, extension, filename);
}
}
public static class InvalidVideoExtensionException extends InvalidExtensionException
{
private static final long serialVersionUID = 1L;
public InvalidVideoExtensionException(String[] allowedExtension, String extension, String filename)
{
super(allowedExtension, extension, filename);
}
}
}

@ -66,7 +66,7 @@ public class CharsetKit
if (null == destCharset) if (null == destCharset)
{ {
srcCharset = StandardCharsets.UTF_8; destCharset = StandardCharsets.UTF_8;
} }
if (StringUtils.isEmpty(source) || srcCharset.equals(destCharset)) if (StringUtils.isEmpty(source) || srcCharset.equals(destCharset))

@ -1,9 +1,9 @@
package com.ruoyi.common.security.utils; package com.ruoyi.common.core.utils;
import org.springframework.security.core.Authentication; import javax.servlet.http.HttpServletRequest;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import com.ruoyi.common.security.domain.LoginUser; import com.ruoyi.common.core.constant.CacheConstants;
import com.ruoyi.common.core.text.Convert;
/** /**
* *
@ -13,45 +13,52 @@ import com.ruoyi.common.security.domain.LoginUser;
public class SecurityUtils public class SecurityUtils
{ {
/** /**
* Authentication *
*/ */
public static Authentication getAuthentication() public static String getUsername()
{ {
return SecurityContextHolder.getContext().getAuthentication(); String username = ServletUtils.getRequest().getHeader(CacheConstants.DETAILS_USERNAME);
return ServletUtils.urlDecode(username);
} }
/** /**
* * ID
*/ */
public static String getUsername() public static Long getUserId()
{ {
return getLoginUser().getUsername(); return Convert.toLong(ServletUtils.getRequest().getHeader(CacheConstants.DETAILS_USER_ID));
} }
/** /**
* * token
*/ */
public static LoginUser getLoginUser(Authentication authentication) public static String getToken()
{
Object principal = authentication.getPrincipal();
if (principal instanceof LoginUser)
{ {
return (LoginUser) principal; return getToken(ServletUtils.getRequest());
}
return null;
} }
/** /**
* * requesttoken
*/ */
public static LoginUser getLoginUser() public static String getToken(HttpServletRequest request)
{ {
Authentication authentication = getAuthentication(); String token = ServletUtils.getRequest().getHeader(CacheConstants.HEADER);
if (authentication == null) if (StringUtils.isNotEmpty(token) && token.startsWith(CacheConstants.TOKEN_PREFIX))
{ {
return null; token = token.replace(CacheConstants.TOKEN_PREFIX, "");
} }
return getLoginUser(authentication); return token;
}
/**
*
*
* @param userId ID
* @return
*/
public static boolean isAdmin(Long userId)
{
return userId != null && 1L == userId;
} }
/** /**
@ -78,15 +85,4 @@ public class SecurityUtils
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
return passwordEncoder.matches(rawPassword, encodedPassword); return passwordEncoder.matches(rawPassword, encodedPassword);
} }
/**
*
*
* @param userId ID
* @return
*/
public static boolean isAdmin(Long userId)
{
return userId != null && 1L == userId;
}
} }

@ -1,12 +1,19 @@
package com.ruoyi.common.core.utils; package com.ruoyi.common.core.utils;
import java.io.IOException; import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Enumeration;
import java.util.LinkedHashMap;
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 javax.servlet.http.HttpSession; import javax.servlet.http.HttpSession;
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.ruoyi.common.core.constant.Constants;
import com.ruoyi.common.core.text.Convert; import com.ruoyi.common.core.text.Convert;
/** /**
@ -52,17 +59,31 @@ public class ServletUtils
* request * request
*/ */
public static HttpServletRequest getRequest() public static HttpServletRequest getRequest()
{
try
{ {
return getRequestAttributes().getRequest(); return getRequestAttributes().getRequest();
} }
catch (Exception e)
{
return null;
}
}
/** /**
* response * response
*/ */
public static HttpServletResponse getResponse() public static HttpServletResponse getResponse()
{
try
{ {
return getRequestAttributes().getResponse(); return getRequestAttributes().getResponse();
} }
catch (Exception e)
{
return null;
}
}
/** /**
* session * session
@ -73,10 +94,33 @@ public class ServletUtils
} }
public static ServletRequestAttributes getRequestAttributes() public static ServletRequestAttributes getRequestAttributes()
{
try
{ {
RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
return (ServletRequestAttributes) attributes; return (ServletRequestAttributes) attributes;
} }
catch (Exception e)
{
return null;
}
}
public static Map<String, String> getHeaders(HttpServletRequest request)
{
Map<String, String> map = new LinkedHashMap<>();
Enumeration<String> enumeration = request.getHeaderNames();
if (enumeration != null)
{
while (enumeration.hasMoreElements())
{
String key = enumeration.nextElement();
String value = request.getHeader(key);
map.put(key, value);
}
}
return map;
}
/** /**
* *
@ -133,4 +177,40 @@ public class ServletUtils
} }
return false; return false;
} }
/**
*
*
* @param str
* @return
*/
public static String urlEncode(String str)
{
try
{
return URLEncoder.encode(str, Constants.UTF8);
}
catch (UnsupportedEncodingException e)
{
return "";
}
}
/**
*
*
* @param str
* @return
*/
public static String urlDecode(String str)
{
try
{
return URLDecoder.decode(str, Constants.UTF8);
}
catch (UnsupportedEncodingException e)
{
return "";
}
}
} }

@ -1,6 +1,7 @@
package com.ruoyi.common.core.utils; package com.ruoyi.common.core.utils;
import java.util.Collection; import java.util.Collection;
import java.util.List;
import java.util.Map; import java.util.Map;
import com.ruoyi.common.core.text.StrFormatter; import com.ruoyi.common.core.text.StrFormatter;
@ -17,6 +18,9 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils
/** 下划线 */ /** 下划线 */
private static final char SEPARATOR = '_'; private static final char SEPARATOR = '_';
/** 星号 */
private static final String START = "*";
/** /**
* *
* *
@ -396,6 +400,121 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils
return sb.toString(); return sb.toString();
} }
/**
*
*
* @param str
* @param strs
* @return
*/
public static boolean matches(String str, List<String> strs)
{
if (isEmpty(str) || isEmpty(strs))
{
return false;
}
for (String testStr : strs)
{
if (matches(str, testStr))
{
return true;
}
}
return false;
}
/**
*
*
* @param str
* @param strs
* @return
*/
public static boolean matches(String str, String... strs)
{
if (isEmpty(str) || isEmpty(strs))
{
return false;
}
for (String testStr : strs)
{
if (matches(str, testStr))
{
return true;
}
}
return false;
}
/**
*
*
* @param str
* @param pattern
* @return
*/
public static boolean matches(String str, String pattern)
{
if (isEmpty(pattern) || isEmpty(str))
{
return false;
}
pattern = pattern.replaceAll("\\s*", ""); // 替换空格
int beginOffset = 0; // pattern截取开始位置
int formerStarOffset = -1; // 前星号的偏移位置
int latterStarOffset = -1; // 后星号的偏移位置
String remainingURI = str;
String prefixPattern = "";
String suffixPattern = "";
boolean result = false;
do
{
formerStarOffset = indexOf(pattern, START, beginOffset);
prefixPattern = substring(pattern, beginOffset, formerStarOffset > -1 ? formerStarOffset : pattern.length());
// 匹配前缀Pattern
result = remainingURI.contains(prefixPattern);
// 已经没有星号,直接返回
if (formerStarOffset == -1)
{
return result;
}
// 匹配失败,直接返回
if (!result)
return false;
if (!isEmpty(prefixPattern))
{
remainingURI = substringAfter(str, prefixPattern);
}
// 匹配后缀Pattern
latterStarOffset = indexOf(pattern, START, formerStarOffset + 1);
suffixPattern = substring(pattern, formerStarOffset + 1, latterStarOffset > -1 ? latterStarOffset : pattern.length());
result = remainingURI.contains(suffixPattern);
// 匹配失败,直接返回
if (!result)
return false;
if (!isEmpty(suffixPattern))
{
remainingURI = substringAfter(str, suffixPattern);
}
// 移动指针
beginOffset = latterStarOffset + 1;
}
while (!isEmpty(suffixPattern) && !isEmpty(remainingURI));
return true;
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static <T> T cast(Object obj) public static <T> T cast(Object obj)
{ {

@ -0,0 +1,76 @@
package com.ruoyi.common.core.utils.file;
import java.io.File;
import org.apache.commons.lang3.StringUtils;
/**
*
*
* @author ruoyi
*/
public class FileTypeUtils
{
/**
*
* <p>
* : ruoyi.txt, : txt
*
* @param file
* @return ".")
*/
public static String getFileType(File file)
{
if (null == file)
{
return StringUtils.EMPTY;
}
return getFileType(file.getName());
}
/**
*
* <p>
* : ruoyi.txt, : txt
*
* @param fileName
* @return ".")
*/
public static String getFileType(String fileName)
{
int separatorIndex = fileName.lastIndexOf(".");
if (separatorIndex < 0)
{
return "";
}
return fileName.substring(separatorIndex + 1).toLowerCase();
}
/**
*
*
* @param photoByte
* @return ".")
*/
public static String getFileExtendName(byte[] photoByte)
{
String strFileExtendName = "JPG";
if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56)
&& ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97))
{
strFileExtendName = "GIF";
}
else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70))
{
strFileExtendName = "JPG";
}
else if ((photoByte[0] == 66) && (photoByte[1] == 77))
{
strFileExtendName = "BMP";
}
else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71))
{
strFileExtendName = "PNG";
}
return strFileExtendName;
}
}

@ -0,0 +1,260 @@
package com.ruoyi.common.core.utils.file;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.ArrayUtils;
import com.ruoyi.common.core.utils.StringUtils;
/**
*
*
* @author ruoyi
*/
public class FileUtils extends org.apache.commons.io.FileUtils
{
/** 字符常量:斜杠 {@code '/'} */
public static final char SLASH = '/';
/** 字符常量:反斜杠 {@code '\\'} */
public static final char BACKSLASH = '\\';
public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+";
/**
* byte
*
* @param filePath
* @param os
* @return
*/
public static void writeBytes(String filePath, OutputStream os) throws IOException
{
FileInputStream fis = null;
try
{
File file = new File(filePath);
if (!file.exists())
{
throw new FileNotFoundException(filePath);
}
fis = new FileInputStream(file);
byte[] b = new byte[1024];
int length;
while ((length = fis.read(b)) > 0)
{
os.write(b, 0, length);
}
}
catch (IOException e)
{
throw e;
}
finally
{
if (os != null)
{
try
{
os.close();
}
catch (IOException e1)
{
e1.printStackTrace();
}
}
if (fis != null)
{
try
{
fis.close();
}
catch (IOException e1)
{
e1.printStackTrace();
}
}
}
}
/**
*
*
* @param filePath
* @return
*/
public static boolean deleteFile(String filePath)
{
boolean flag = false;
File file = new File(filePath);
// 路径为文件且不为空则进行删除
if (file.isFile() && file.exists())
{
file.delete();
flag = true;
}
return flag;
}
/**
*
*
* @param filename
* @return true false
*/
public static boolean isValidFilename(String filename)
{
return filename.matches(FILENAME_PATTERN);
}
/**
*
*
* @param resource
* @return true false
*/
public static boolean checkAllowDownload(String resource)
{
// 禁止目录上跳级别
if (StringUtils.contains(resource, ".."))
{
return false;
}
// 检查允许下载的文件规则
if (ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource)))
{
return true;
}
// 不在允许下载的文件规则
return false;
}
/**
*
*
* @param request
* @param fileName
* @return
*/
public static String setFileDownloadHeader(HttpServletRequest request, String fileName) throws UnsupportedEncodingException
{
final String agent = request.getHeader("USER-AGENT");
String filename = fileName;
if (agent.contains("MSIE"))
{
// IE浏览器
filename = URLEncoder.encode(filename, "utf-8");
filename = filename.replace("+", " ");
}
else if (agent.contains("Firefox"))
{
// 火狐浏览器
filename = new String(fileName.getBytes(), "ISO8859-1");
}
else if (agent.contains("Chrome"))
{
// google浏览器
filename = URLEncoder.encode(filename, "utf-8");
}
else
{
// 其它浏览器
filename = URLEncoder.encode(filename, "utf-8");
}
return filename;
}
/**
*
*
* @param filePath
* @return
*/
public static String getName(String filePath)
{
if (null == filePath)
{
return null;
}
int len = filePath.length();
if (0 == len)
{
return filePath;
}
if (isFileSeparator(filePath.charAt(len - 1)))
{
// 以分隔符结尾的去掉结尾分隔符
len--;
}
int begin = 0;
char c;
for (int i = len - 1; i > -1; i--)
{
c = filePath.charAt(i);
if (isFileSeparator(c))
{
// 查找最后一个路径分隔符(/或者\
begin = i + 1;
break;
}
}
return filePath.substring(begin, len);
}
/**
* WindowsLinuxUnix<br>
* Windows\LinuxUnix/
*
* @param c
* @return WindowsLinuxUnix
*/
public static boolean isFileSeparator(char c)
{
return SLASH == c || BACKSLASH == c;
}
/**
*
*
* @param response
* @param realFileName
* @return
*/
public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException
{
String percentEncodedFileName = percentEncode(realFileName);
StringBuilder contentDispositionValue = new StringBuilder();
contentDispositionValue.append("attachment; filename=")
.append(percentEncodedFileName)
.append(";")
.append("filename*=")
.append("utf-8''")
.append(percentEncodedFileName);
response.setHeader("Content-disposition", contentDispositionValue.toString());
}
/**
*
*
* @param s
* @return
*/
public static String percentEncode(String s) throws UnsupportedEncodingException
{
String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString());
return encode.replaceAll("\\+", "%20");
}
}

@ -0,0 +1,86 @@
package com.ruoyi.common.core.utils.file;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.Arrays;
import org.apache.poi.util.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
*
* @author ruoyi
*/
public class ImageUtils
{
private static final Logger log = LoggerFactory.getLogger(ImageUtils.class);
public static byte[] getImage(String imagePath)
{
InputStream is = getFile(imagePath);
try
{
return IOUtils.toByteArray(is);
}
catch (Exception e)
{
log.error("图片加载异常 {}", e);
return null;
}
finally
{
IOUtils.closeQuietly(is);
}
}
public static InputStream getFile(String imagePath)
{
try
{
byte[] result = readFile(imagePath);
result = Arrays.copyOf(result, result.length);
return new ByteArrayInputStream(result);
}
catch (Exception e)
{
log.error("获取图片异常 {}", e);
}
return null;
}
/**
*
*
* @param key
* @return
*/
public static byte[] readFile(String url)
{
InputStream in = null;
ByteArrayOutputStream baos = null;
try
{
// 网络地址
URL urlObj = new URL(url);
URLConnection urlConnection = urlObj.openConnection();
urlConnection.setConnectTimeout(30 * 1000);
urlConnection.setReadTimeout(60 * 1000);
urlConnection.setDoInput(true);
in = urlConnection.getInputStream();
return IOUtils.toByteArray(in);
}
catch (Exception e)
{
log.error("访问文件异常 {}", e);
return null;
}
finally
{
IOUtils.closeQuietly(baos);
}
}
}

@ -0,0 +1,59 @@
package com.ruoyi.common.core.utils.file;
/**
*
*
* @author ruoyi
*/
public class MimeTypeUtils
{
public static final String IMAGE_PNG = "image/png";
public static final String IMAGE_JPG = "image/jpg";
public static final String IMAGE_JPEG = "image/jpeg";
public static final String IMAGE_BMP = "image/bmp";
public static final String IMAGE_GIF = "image/gif";
public static final String[] IMAGE_EXTENSION = { "bmp", "gif", "jpg", "jpeg", "png" };
public static final String[] FLASH_EXTENSION = { "swf", "flv" };
public static final String[] MEDIA_EXTENSION = { "swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg",
"asf", "rm", "rmvb" };
public static final String[] VIDEO_EXTENSION = { "mp4", "avi", "rmvb" };
public static final String[] DEFAULT_ALLOWED_EXTENSION = {
// 图片
"bmp", "gif", "jpg", "jpeg", "png",
// word excel powerpoint
"doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt",
// 压缩文件
"rar", "zip", "gz", "bz2",
// 视频格式
"mp4", "avi", "rmvb",
// pdf
"pdf" };
public static String getExtension(String prefix)
{
switch (prefix)
{
case IMAGE_PNG:
return "png";
case IMAGE_JPG:
return "jpg";
case IMAGE_JPEG:
return "jpeg";
case IMAGE_BMP:
return "bmp";
case IMAGE_GIF:
return "gif";
default:
return "";
}
}
}

@ -14,34 +14,43 @@ public class IpUtils
{ {
public static String getIpAddr(HttpServletRequest request) public static String getIpAddr(HttpServletRequest request)
{ {
if (request == null) String ip = null;
// X-Forwarded-ForSquid 服务代理
String ipAddresses = request.getHeader("X-Forwarded-For");
if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses))
{ {
return "unknown"; // Proxy-Client-IPapache 服务代理
ipAddresses = request.getHeader("Proxy-Client-IP");
} }
String ip = request.getHeader("x-forwarded-for"); if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses))
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
{ {
ip = request.getHeader("Proxy-Client-IP"); // WL-Proxy-Client-IPweblogic 服务代理
ipAddresses = request.getHeader("WL-Proxy-Client-IP");
} }
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses))
{ {
ip = request.getHeader("X-Forwarded-For"); // HTTP_CLIENT_IP有些代理服务器
ipAddresses = request.getHeader("HTTP_CLIENT_IP");
} }
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses))
{ {
ip = request.getHeader("WL-Proxy-Client-IP"); // X-Real-IPnginx服务代理
ipAddresses = request.getHeader("X-Real-IP");
} }
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
// 有些网络通过多层代理那么获取到的ip就会有多个一般都是通过逗号,分割开来并且第一个ip为客户端的真实IP
if (ipAddresses != null && ipAddresses.length() != 0)
{ {
ip = request.getHeader("X-Real-IP"); ip = ipAddresses.split(",")[0];
} }
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) // 还是不能获取到最后再通过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" : ip;
} }
public static boolean internalIp(String ip) public static boolean internalIp(String ip)

@ -4,7 +4,6 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.util.ArrayList; import java.util.ArrayList;
@ -14,17 +13,19 @@ import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.apache.poi.ss.usermodel.BorderStyle; import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.CellType; import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.ClientAnchor;
import org.apache.poi.ss.usermodel.DataValidation; import org.apache.poi.ss.usermodel.DataValidation;
import org.apache.poi.ss.usermodel.DataValidationConstraint; import org.apache.poi.ss.usermodel.DataValidationConstraint;
import org.apache.poi.ss.usermodel.DataValidationHelper; import org.apache.poi.ss.usermodel.DataValidationHelper;
import org.apache.poi.ss.usermodel.DateUtil; import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.usermodel.Drawing;
import org.apache.poi.ss.usermodel.FillPatternType; import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.Font; import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.HorizontalAlignment; import org.apache.poi.ss.usermodel.HorizontalAlignment;
@ -36,6 +37,7 @@ import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory; import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.apache.poi.ss.util.CellRangeAddressList; import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.xssf.streaming.SXSSFWorkbook; import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
import org.apache.poi.xssf.usermodel.XSSFDataValidation; import org.apache.poi.xssf.usermodel.XSSFDataValidation;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -46,6 +48,8 @@ import com.ruoyi.common.core.annotation.Excels;
import com.ruoyi.common.core.text.Convert; import com.ruoyi.common.core.text.Convert;
import com.ruoyi.common.core.utils.DateUtils; import com.ruoyi.common.core.utils.DateUtils;
import com.ruoyi.common.core.utils.StringUtils; import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.core.utils.file.FileTypeUtils;
import com.ruoyi.common.core.utils.file.ImageUtils;
import com.ruoyi.common.core.utils.reflect.ReflectUtils; import com.ruoyi.common.core.utils.reflect.ReflectUtils;
/** /**
@ -97,6 +101,21 @@ public class ExcelUtil<T>
*/ */
private List<Object[]> fields; private List<Object[]> fields;
/**
*
*/
private short maxHeight;
/**
*
*/
private Map<Integer, Double> statistics = new HashMap<Integer, Double>();
/**
*
*/
private static final DecimalFormat DOUBLE_FORMAT = new DecimalFormat("######0.00");
/** /**
* *
*/ */
@ -194,9 +213,12 @@ public class ExcelUtil<T>
// 设置类的私有字段属性可访问. // 设置类的私有字段属性可访问.
field.setAccessible(true); field.setAccessible(true);
Integer column = cellMap.get(attr.name()); Integer column = cellMap.get(attr.name());
if (column != null)
{
fieldsMap.put(column, field); fieldsMap.put(column, field);
} }
} }
}
for (int i = 1; i < rows; i++) for (int i = 1; i < rows; i++)
{ {
// 从第2行开始取数据,默认第一行是表头. // 从第2行开始取数据,默认第一行是表头.
@ -220,23 +242,31 @@ public class ExcelUtil<T>
val = StringUtils.substringBefore(s, ".0"); val = StringUtils.substringBefore(s, ".0");
} }
else else
{
String dateFormat = field.getAnnotation(Excel.class).dateFormat();
if (StringUtils.isNotEmpty(dateFormat))
{
val = DateUtils.parseDateToStr(dateFormat, (Date) val);
}
else
{ {
val = Convert.toStr(val); val = Convert.toStr(val);
} }
} }
else if ((Integer.TYPE == fieldType) || (Integer.class == fieldType)) }
else if ((Integer.TYPE == fieldType || Integer.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val)))
{ {
val = Convert.toInt(val); val = Convert.toInt(val);
} }
else if ((Long.TYPE == fieldType) || (Long.class == fieldType)) else if (Long.TYPE == fieldType || Long.class == fieldType)
{ {
val = Convert.toLong(val); val = Convert.toLong(val);
} }
else if ((Double.TYPE == fieldType) || (Double.class == fieldType)) else if (Double.TYPE == fieldType || Double.class == fieldType)
{ {
val = Convert.toDouble(val); val = Convert.toDouble(val);
} }
else if ((Float.TYPE == fieldType) || (Float.class == fieldType)) else if (Float.TYPE == fieldType || Float.class == fieldType)
{ {
val = Convert.toFloat(val); val = Convert.toFloat(val);
} }
@ -255,6 +285,10 @@ public class ExcelUtil<T>
val = DateUtil.getJavaDate((Double) val); val = DateUtil.getJavaDate((Double) val);
} }
} }
else if (Boolean.TYPE == fieldType || Boolean.class == fieldType)
{
val = Convert.toBool(val, false);
}
if (StringUtils.isNotNull(fieldType)) if (StringUtils.isNotNull(fieldType))
{ {
Excel attr = field.getAnnotation(Excel.class); Excel attr = field.getAnnotation(Excel.class);
@ -265,7 +299,7 @@ public class ExcelUtil<T>
} }
else if (StringUtils.isNotEmpty(attr.readConverterExp())) else if (StringUtils.isNotEmpty(attr.readConverterExp()))
{ {
val = reverseByExp(String.valueOf(val), attr.readConverterExp()); val = reverseByExp(Convert.toStr(val), attr.readConverterExp(), attr.separator());
} }
ReflectUtils.invokeSetter(entity, propertyName, val); ReflectUtils.invokeSetter(entity, propertyName, val);
} }
@ -334,6 +368,7 @@ public class ExcelUtil<T>
if (Type.EXPORT.equals(type)) if (Type.EXPORT.equals(type))
{ {
fillExcelData(index, row); fillExcelData(index, row);
addStatisticsRow();
} }
} }
wb.write(outputStream); wb.write(outputStream);
@ -437,6 +472,30 @@ public class ExcelUtil<T>
style.setFont(headerFont); style.setFont(headerFont);
styles.put("header", style); styles.put("header", style);
style = wb.createCellStyle();
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
Font totalFont = wb.createFont();
totalFont.setFontName("Arial");
totalFont.setFontHeightInPoints((short) 10);
style.setFont(totalFont);
styles.put("total", style);
style = wb.createCellStyle();
style.cloneStyleFrom(styles.get("data"));
style.setAlignment(HorizontalAlignment.LEFT);
styles.put("data1", style);
style = wb.createCellStyle();
style.cloneStyleFrom(styles.get("data"));
style.setAlignment(HorizontalAlignment.CENTER);
styles.put("data2", style);
style = wb.createCellStyle();
style.cloneStyleFrom(styles.get("data"));
style.setAlignment(HorizontalAlignment.RIGHT);
styles.put("data3", style);
return styles; return styles;
} }
@ -465,14 +524,53 @@ public class ExcelUtil<T>
{ {
if (ColumnType.STRING == attr.cellType()) if (ColumnType.STRING == attr.cellType())
{ {
cell.setCellType(CellType.NUMERIC);
cell.setCellValue(StringUtils.isNull(value) ? attr.defaultValue() : value + attr.suffix()); cell.setCellValue(StringUtils.isNull(value) ? attr.defaultValue() : value + attr.suffix());
} }
else if (ColumnType.NUMERIC == attr.cellType()) else if (ColumnType.NUMERIC == attr.cellType())
{ {
cell.setCellType(CellType.NUMERIC); cell.setCellValue(StringUtils.contains(Convert.toStr(value), ".") ? Convert.toDouble(value) : Convert.toInt(value));
cell.setCellValue(Integer.parseInt(value + "")); }
else if (ColumnType.IMAGE == attr.cellType())
{
ClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + 1),
cell.getRow().getRowNum() + 1);
String imagePath = Convert.toStr(value);
if (StringUtils.isNotEmpty(imagePath))
{
byte[] data = ImageUtils.getImage(imagePath);
getDrawingPatriarch(cell.getSheet()).createPicture(anchor,
cell.getSheet().getWorkbook().addPicture(data, getImageType(data)));
}
}
}
/**
*
*/
public static Drawing<?> getDrawingPatriarch(Sheet sheet)
{
if (sheet.getDrawingPatriarch() == null)
{
sheet.createDrawingPatriarch();
}
return sheet.getDrawingPatriarch();
}
/**
* ,
*/
public int getImageType(byte[] value)
{
String type = FileTypeUtils.getFileExtendName(value);
if ("JPG".equalsIgnoreCase(type))
{
return Workbook.PICTURE_TYPE_JPEG;
}
else if ("PNG".equalsIgnoreCase(type))
{
return Workbook.PICTURE_TYPE_PNG;
} }
return Workbook.PICTURE_TYPE_JPEG;
} }
/** /**
@ -488,7 +586,6 @@ public class ExcelUtil<T>
{ {
// 设置列宽 // 设置列宽
sheet.setColumnWidth(column, (int) ((attr.width() + 0.72) * 256)); sheet.setColumnWidth(column, (int) ((attr.width() + 0.72) * 256));
row.setHeight((short) (attr.height() * 20));
} }
// 如果设置了提示信息则鼠标放上去提示. // 如果设置了提示信息则鼠标放上去提示.
if (StringUtils.isNotEmpty(attr.prompt())) if (StringUtils.isNotEmpty(attr.prompt()))
@ -513,31 +610,38 @@ public class ExcelUtil<T>
try try
{ {
// 设置行高 // 设置行高
row.setHeight((short) (attr.height() * 20)); row.setHeight(maxHeight);
// 根据Excel中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列. // 根据Excel中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列.
if (attr.isExport()) if (attr.isExport())
{ {
// 创建cell // 创建cell
cell = row.createCell(column); cell = row.createCell(column);
cell.setCellStyle(styles.get("data")); int align = attr.align().value();
cell.setCellStyle(styles.get("data" + (align >= 1 && align <= 3 ? align : "")));
// 用于读取对象中的属性 // 用于读取对象中的属性
Object value = getTargetValue(vo, field, attr); Object value = getTargetValue(vo, field, attr);
String dateFormat = attr.dateFormat(); String dateFormat = attr.dateFormat();
String readConverterExp = attr.readConverterExp(); String readConverterExp = attr.readConverterExp();
String separator = attr.separator();
if (StringUtils.isNotEmpty(dateFormat) && StringUtils.isNotNull(value)) if (StringUtils.isNotEmpty(dateFormat) && StringUtils.isNotNull(value))
{ {
cell.setCellValue(DateUtils.parseDateToStr(dateFormat, (Date) value)); cell.setCellValue(DateUtils.parseDateToStr(dateFormat, (Date) value));
} }
else if (StringUtils.isNotEmpty(readConverterExp) && StringUtils.isNotNull(value)) else if (StringUtils.isNotEmpty(readConverterExp) && StringUtils.isNotNull(value))
{ {
cell.setCellValue(convertByExp(String.valueOf(value), readConverterExp)); cell.setCellValue(convertByExp(Convert.toStr(value), readConverterExp, separator));
}
else if (value instanceof BigDecimal && -1 != attr.scale())
{
cell.setCellValue((((BigDecimal) value).setScale(attr.scale(), attr.roundingMode())).toString());
} }
else else
{ {
// 设置列类型 // 设置列类型
setCellVo(value, attr, cell); setCellVo(value, attr, cell);
} }
addStatisticsData(column, Convert.toStr(value), attr);
} }
} }
catch (Exception e) catch (Exception e)
@ -609,28 +713,36 @@ public class ExcelUtil<T>
* *
* @param propertyValue * @param propertyValue
* @param converterExp * @param converterExp
* @param separator
* @return * @return
* @throws Exception
*/ */
public static String convertByExp(String propertyValue, String converterExp) throws Exception public static String convertByExp(String propertyValue, String converterExp, String separator)
{
try
{ {
StringBuilder propertyString = new StringBuilder();
String[] convertSource = converterExp.split(","); String[] convertSource = converterExp.split(",");
for (String item : convertSource) for (String item : convertSource)
{ {
String[] itemArray = item.split("="); String[] itemArray = item.split("=");
if (itemArray[0].equals(propertyValue)) if (StringUtils.containsAny(separator, propertyValue))
{ {
return itemArray[1]; for (String value : propertyValue.split(separator))
{
if (itemArray[0].equals(value))
{
propertyString.append(itemArray[1] + separator);
break;
} }
} }
} }
catch (Exception e) else
{ {
throw e; if (itemArray[0].equals(propertyValue))
{
return itemArray[1];
}
} }
return propertyValue; }
return StringUtils.stripEnd(propertyString.toString(), separator);
} }
/** /**
@ -638,28 +750,83 @@ public class ExcelUtil<T>
* *
* @param propertyValue * @param propertyValue
* @param converterExp * @param converterExp
* @param separator
* @return * @return
* @throws Exception
*/ */
public static String reverseByExp(String propertyValue, String converterExp) throws Exception public static String reverseByExp(String propertyValue, String converterExp, String separator)
{
try
{ {
StringBuilder propertyString = new StringBuilder();
String[] convertSource = converterExp.split(","); String[] convertSource = converterExp.split(",");
for (String item : convertSource) for (String item : convertSource)
{ {
String[] itemArray = item.split("="); String[] itemArray = item.split("=");
if (StringUtils.containsAny(separator, propertyValue))
{
for (String value : propertyValue.split(separator))
{
if (itemArray[1].equals(value))
{
propertyString.append(itemArray[0] + separator);
break;
}
}
}
else
{
if (itemArray[1].equals(propertyValue)) if (itemArray[1].equals(propertyValue))
{ {
return itemArray[0]; return itemArray[0];
} }
} }
} }
catch (Exception e) return StringUtils.stripEnd(propertyString.toString(), separator);
}
/**
*
*/
private void addStatisticsData(Integer index, String text, Excel entity)
{ {
throw e; if (entity != null && entity.isStatistics())
{
Double temp = 0D;
if (!statistics.containsKey(index))
{
statistics.put(index, temp);
}
try
{
temp = Double.valueOf(text);
}
catch (NumberFormatException e)
{
}
statistics.put(index, statistics.get(index) + temp);
}
}
/**
*
*/
public void addStatisticsRow()
{
if (statistics.size() > 0)
{
Cell cell = null;
Row row = sheet.createRow(sheet.getLastRowNum() + 1);
Set<Integer> keys = statistics.keySet();
cell = row.createCell(0);
cell.setCellStyle(styles.get("total"));
cell.setCellValue("合计");
for (Integer key : keys)
{
cell = row.createCell(key);
cell.setCellStyle(styles.get("total"));
cell.setCellValue(DOUBLE_FORMAT.format(statistics.get(key)));
}
statistics.clear();
} }
return propertyValue;
} }
/** /**
@ -703,12 +870,12 @@ public class ExcelUtil<T>
*/ */
private Object getValue(Object o, String name) throws Exception private Object getValue(Object o, String name) throws Exception
{ {
if (StringUtils.isNotEmpty(name)) if (StringUtils.isNotNull(o) && StringUtils.isNotEmpty(name))
{ {
Class<?> clazz = o.getClass(); Class<?> clazz = o.getClass();
String methodName = "get" + name.substring(0, 1).toUpperCase() + name.substring(1); Field field = clazz.getDeclaredField(name);
Method method = clazz.getMethod(methodName); field.setAccessible(true);
o = method.invoke(o); o = field.get(o);
} }
return o; return o;
} }
@ -742,6 +909,21 @@ public class ExcelUtil<T>
} }
} }
this.fields = this.fields.stream().sorted(Comparator.comparing(objects -> ((Excel) objects[1]).sort())).collect(Collectors.toList()); this.fields = this.fields.stream().sorted(Comparator.comparing(objects -> ((Excel) objects[1]).sort())).collect(Collectors.toList());
this.maxHeight = getRowHeight();
}
/**
*
*/
public short getRowHeight()
{
double maxHeight = 0;
for (Object[] os : this.fields)
{
Excel excel = (Excel) os[1];
maxHeight = maxHeight > excel.height() ? maxHeight : excel.height();
}
return (short) (maxHeight * 20);
} }
/** /**
@ -803,18 +985,18 @@ public class ExcelUtil<T>
Cell cell = row.getCell(column); Cell cell = row.getCell(column);
if (StringUtils.isNotNull(cell)) if (StringUtils.isNotNull(cell))
{ {
if (cell.getCellTypeEnum() == CellType.NUMERIC || cell.getCellTypeEnum() == CellType.FORMULA) if (cell.getCellType() == CellType.NUMERIC || cell.getCellType() == CellType.FORMULA)
{ {
val = cell.getNumericCellValue(); val = cell.getNumericCellValue();
if (HSSFDateUtil.isCellDateFormatted(cell)) if (DateUtil.isCellDateFormatted(cell))
{ {
val = DateUtil.getJavaDate((Double) val); // POI Excel 日期格式转换 val = DateUtil.getJavaDate((Double) val); // POI Excel 日期格式转换
} }
else else
{ {
if ((Double) val % 1 > 0) if ((Double) val % 1 != 0)
{ {
val = new DecimalFormat("0.00").format(val); val = new BigDecimal(val.toString());
} }
else else
{ {
@ -822,15 +1004,15 @@ public class ExcelUtil<T>
} }
} }
} }
else if (cell.getCellTypeEnum() == CellType.STRING) else if (cell.getCellType() == CellType.STRING)
{ {
val = cell.getStringCellValue(); val = cell.getStringCellValue();
} }
else if (cell.getCellTypeEnum() == CellType.BOOLEAN) else if (cell.getCellType() == CellType.BOOLEAN)
{ {
val = cell.getBooleanCellValue(); val = cell.getBooleanCellValue();
} }
else if (cell.getCellTypeEnum() == CellType.ERROR) else if (cell.getCellType() == CellType.ERROR)
{ {
val = cell.getErrorCellValue(); val = cell.getErrorCellValue();
} }

@ -204,6 +204,10 @@ public class ReflectUtils
args[i] = DateUtil.getJavaDate((Double) args[i]); args[i] = DateUtil.getJavaDate((Double) args[i]);
} }
} }
else if (cs[i] == boolean.class || cs[i] == Boolean.class)
{
args[i] = Convert.toBool(args[i]);
}
} }
} }
return (E) method.invoke(obj, args); return (E) method.invoke(obj, args);

@ -1,5 +1,6 @@
package com.ruoyi.common.core.utils.sql; package com.ruoyi.common.core.utils.sql;
import com.ruoyi.common.core.exception.BaseException;
import com.ruoyi.common.core.utils.StringUtils; import com.ruoyi.common.core.utils.StringUtils;
/** /**
@ -10,9 +11,9 @@ import com.ruoyi.common.core.utils.StringUtils;
public class SqlUtil public class SqlUtil
{ {
/** /**
* 线 * 线
*/ */
public static String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,]+"; public static String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+";
/** /**
* *
@ -21,7 +22,7 @@ public class SqlUtil
{ {
if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value)) if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value))
{ {
return StringUtils.EMPTY; throw new BaseException("参数不符合规范,不能进行查询");
} }
return value; return value;
} }

@ -58,6 +58,20 @@ public class AjaxResult extends HashMap<String, Object>
} }
} }
/**
* 便
*
* @param key
* @param value
* @return
*/
@Override
public AjaxResult put(String key, Object value)
{
super.put(key, value);
return this;
}
/** /**
* *
* *

@ -5,7 +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;
/** /**
* Entity * Entity
@ -36,14 +35,6 @@ public class BaseEntity implements Serializable
/** 备注 */ /** 备注 */
private String remark; private String remark;
/** 开始时间 */
@JsonIgnore
private String beginTime;
/** 结束时间 */
@JsonIgnore
private String endTime;
/** 请求参数 */ /** 请求参数 */
private Map<String, Object> params; private Map<String, Object> params;
@ -107,26 +98,6 @@ public class BaseEntity implements Serializable
this.remark = remark; this.remark = remark;
} }
public String getBeginTime()
{
return beginTime;
}
public void setBeginTime(String beginTime)
{
this.beginTime = beginTime;
}
public String getEndTime()
{
return endTime;
}
public void setEndTime(String endTime)
{
this.endTime = endTime;
}
public Map<String, Object> getParams() public Map<String, Object> getParams()
{ {
if (params == null) if (params == null)

@ -17,9 +17,9 @@ public class PageDomain
/** 排序列 */ /** 排序列 */
private String orderByColumn; private String orderByColumn;
/** 排序的方向 "desc" 或者 "asc". */
private String isAsc; /** 排序的方向desc或者asc */
private String isAsc = "asc";
public String getOrderBy() public String getOrderBy()
{ {

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

@ -12,10 +12,10 @@ import org.springframework.stereotype.Component;
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.datascope.service.AwaitUserService; 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.UserInfo; import com.ruoyi.system.api.model.LoginUser;
/** /**
* *
@ -57,7 +57,7 @@ public class DataScopeAspect
public static final String DATA_SCOPE = "dataScope"; public static final String DATA_SCOPE = "dataScope";
@Autowired @Autowired
private AwaitUserService awaitUserService; private TokenService tokenService;
// 配置织入点 // 配置织入点
@Pointcut("@annotation(com.ruoyi.common.datascope.annotation.DataScope)") @Pointcut("@annotation(com.ruoyi.common.datascope.annotation.DataScope)")
@ -80,12 +80,12 @@ public class DataScopeAspect
return; return;
} }
// 获取当前的用户 // 获取当前的用户
UserInfo loginUser = awaitUserService.info(); LoginUser loginUser = tokenService.getLoginUser();
SysUser currentUser = loginUser.getSysUser(); if (StringUtils.isNotNull(loginUser))
if (currentUser != null)
{ {
SysUser currentUser = loginUser.getSysUser();
// 如果是超级管理员,则不过滤数据 // 如果是超级管理员,则不过滤数据
if (!currentUser.isAdmin()) if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin())
{ {
dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(), dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(),
controllerDataScope.userAlias()); controllerDataScope.userAlias());
@ -145,10 +145,14 @@ public class DataScopeAspect
if (StringUtils.isNotBlank(sqlString.toString())) if (StringUtils.isNotBlank(sqlString.toString()))
{ {
BaseEntity baseEntity = (BaseEntity) joinPoint.getArgs()[0]; Object params = joinPoint.getArgs()[0];
if (StringUtils.isNotNull(params) && params instanceof BaseEntity)
{
BaseEntity baseEntity = (BaseEntity) params;
baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")"); baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")");
} }
} }
}
/** /**
* *

@ -1,42 +0,0 @@
package com.ruoyi.common.datascope.service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.system.api.RemoteUserService;
import com.ruoyi.system.api.model.UserInfo;
/**
*
*
* @author ruoyi
*/
@Service
public class AwaitUserService
{
private static final Logger log = LoggerFactory.getLogger(AwaitUserService.class);
@Autowired
private RemoteUserService remoteUserService;
/**
*
*
* @return
*/
public UserInfo info()
{
String username = SecurityUtils.getUsername();
R<UserInfo> userResult = remoteUserService.getUserInfo(username);
if (StringUtils.isNull(userResult) || StringUtils.isNull(userResult.getData()))
{
log.info("数据权限范围查询用户:{} 不存在.", username);
return null;
}
return userResult.getData();
}
}

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

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common</artifactId>
<version>2.5.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ruoyi-common-datasource</artifactId>
<description>
ruoyi-common-datasource多数据源
</description>
<dependencies>
<!-- Druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<!-- Dynamic DataSource -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>${dynamic-ds.version}</version>
</dependency>
<!-- SpringBoot Seata -->
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
</dependency>
</dependencies>
</project>

@ -0,0 +1,22 @@
package com.ruoyi.common.datasource.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.baomidou.dynamic.datasource.annotation.DS;
/**
*
*
* @author ruoyi
*/
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@DS("master")
public @interface Master
{
}

@ -0,0 +1,22 @@
package com.ruoyi.common.datasource.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.baomidou.dynamic.datasource.annotation.DS;
/**
*
*
* @author ruoyi
*/
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@DS("slave")
public @interface Slave
{
}

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

@ -1,6 +1,8 @@
package com.ruoyi.common.log.aspect; package com.ruoyi.common.log.aspect;
import java.lang.reflect.Method; import java.lang.reflect.Method;
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;
@ -17,16 +19,14 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.HandlerMapping;
import com.alibaba.fastjson.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.service.AsyncLogService; import com.ruoyi.common.log.service.AsyncLogService;
import com.ruoyi.common.security.domain.LoginUser;
import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.system.api.domain.SysOperLog; import com.ruoyi.system.api.domain.SysOperLog;
/** /**
@ -83,9 +83,6 @@ public class LogAspect
return; return;
} }
// 获取当前的用户
LoginUser loginUser = SecurityUtils.getLoginUser();
// *========数据库日志=========*// // *========数据库日志=========*//
SysOperLog operLog = new SysOperLog(); SysOperLog operLog = new SysOperLog();
operLog.setStatus(BusinessStatus.SUCCESS.ordinal()); operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
@ -96,9 +93,10 @@ public class LogAspect
operLog.setJsonResult(JSON.toJSONString(jsonResult)); operLog.setJsonResult(JSON.toJSONString(jsonResult));
operLog.setOperUrl(ServletUtils.getRequest().getRequestURI()); operLog.setOperUrl(ServletUtils.getRequest().getRequestURI());
if (loginUser != null) String username = SecurityUtils.getUsername();
if (StringUtils.isNotBlank(username))
{ {
operLog.setOperName(loginUser.getUsername()); operLog.setOperName(username);
} }
if (e != null) if (e != null)
@ -163,11 +161,6 @@ public class LogAspect
String params = argsArrayToString(joinPoint.getArgs()); String params = argsArrayToString(joinPoint.getArgs());
operLog.setOperParam(StringUtils.substring(params, 0, 2000)); operLog.setOperParam(StringUtils.substring(params, 0, 2000));
} }
else
{
Map<?, ?> paramsMap = (Map<?, ?>) ServletUtils.getRequest().getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
operLog.setOperParam(StringUtils.substring(paramsMap.toString(), 0, 2000));
}
} }
/** /**
@ -197,10 +190,16 @@ public class LogAspect
for (int i = 0; i < paramsArray.length; i++) for (int i = 0; i < paramsArray.length; i++)
{ {
if (!isFilterObject(paramsArray[i])) if (!isFilterObject(paramsArray[i]))
{
try
{ {
Object jsonObj = JSON.toJSON(paramsArray[i]); Object jsonObj = JSON.toJSON(paramsArray[i]);
params += jsonObj.toString() + " "; params += jsonObj.toString() + " ";
} }
catch (Exception e)
{
}
}
} }
} }
return params.trim(); return params.trim();
@ -212,8 +211,31 @@ public class LogAspect
* @param o * @param o
* @return truefalse * @return truefalse
*/ */
@SuppressWarnings("rawtypes")
public boolean isFilterObject(final Object o) public boolean isFilterObject(final Object o)
{ {
Class<?> clazz = o.getClass();
if (clazz.isArray())
{
return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
}
else if (Collection.class.isAssignableFrom(clazz))
{
Collection collection = (Collection) o;
for (Iterator iter = collection.iterator(); iter.hasNext();)
{
return iter.next() instanceof MultipartFile;
}
}
else if (Map.class.isAssignableFrom(clazz))
{
Map map = (Map) o;
for (Iterator iter = map.entrySet().iterator(); iter.hasNext();)
{
Map.Entry entry = (Map.Entry) iter.next();
return entry.getValue() instanceof MultipartFile;
}
}
return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse; return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse;
} }
} }

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

@ -1,11 +1,13 @@
package com.ruoyi.common.redis.service; package com.ruoyi.common.redis.service;
import java.util.Collection; import java.util.Collection;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.HashOperations; import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations; import org.springframework.data.redis.core.ValueOperations;
@ -42,7 +44,7 @@ public class RedisService
* @param timeout * @param timeout
* @param timeUnit * @param timeUnit
*/ */
public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) public <T> void setCacheObject(final String key, final T value, final Long timeout, final TimeUnit timeUnit)
{ {
redisTemplate.opsForValue().set(key, value, timeout, timeUnit); redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
} }
@ -109,7 +111,7 @@ public class RedisService
* List * List
* *
* @param key * @param key
* @param values List * @param dataList List
* @return * @return
*/ */
public <T> long setCacheList(final String key, final List<T> dataList) public <T> long setCacheList(final String key, final List<T> dataList)
@ -136,10 +138,15 @@ public class RedisService
* @param dataSet * @param dataSet
* @return * @return
*/ */
public <T> long setCacheSet(final String key, final Set<T> dataSet) public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet)
{ {
Long count = redisTemplate.opsForSet().add(key, dataSet); BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
return count == null ? 0 : count; Iterator<T> it = dataSet.iterator();
while (it.hasNext())
{
setOperation.add(it.next());
}
return setOperation;
} }
/** /**

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common</artifactId> <artifactId>ruoyi-common</artifactId>
<version>2.0.0</version> <version>2.5.0</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@ -16,16 +16,16 @@
<dependencies> <dependencies>
<!-- Spring Security Oauth2 --> <!-- RuoYi Api System -->
<dependency> <dependency>
<groupId>org.springframework.cloud</groupId> <groupId>com.ruoyi</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId> <artifactId>ruoyi-api-system</artifactId>
</dependency> </dependency>
<!-- RuoYi Api System --> <!-- RuoYi Common Redis-->
<dependency> <dependency>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi-api-system</artifactId> <artifactId>ruoyi-common-redis</artifactId>
</dependency> </dependency>
</dependencies> </dependencies>

@ -1,13 +1,17 @@
package com.ruoyi.common.security.annotation; package com.ruoyi.common.security.annotation;
import java.lang.annotation.*; import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.mybatis.spring.annotation.MapperScan; import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableAsync;
import com.ruoyi.common.security.feign.OAuth2FeignConfig;
import com.ruoyi.common.security.config.ApplicationConfig; import com.ruoyi.common.security.config.ApplicationConfig;
import com.ruoyi.common.security.config.SecurityImportBeanDefinitionRegistrar; import com.ruoyi.common.security.feign.FeignAutoConfiguration;
@Target(ElementType.TYPE) @Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@ -20,7 +24,7 @@ import com.ruoyi.common.security.config.SecurityImportBeanDefinitionRegistrar;
// 开启线程异步执行 // 开启线程异步执行
@EnableAsync @EnableAsync
// 自动加载类 // 自动加载类
@Import({ SecurityImportBeanDefinitionRegistrar.class, OAuth2FeignConfig.class, ApplicationConfig.class }) @Import({ ApplicationConfig.class, FeignAutoConfiguration.class })
public @interface EnableCustomConfig public @interface EnableCustomConfig
{ {

@ -0,0 +1,46 @@
package com.ruoyi.common.security.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
*
*
* @author ruoyi
*/
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface PreAuthorize
{
/**
*
*/
public String hasPermi() default "";
/**
* hasPermi
*/
public String lacksPermi() default "";
/**
*
*/
public String[] hasAnyPermi() default {};
/**
*
*/
public String hasRole() default "";
/**
* isRole
*/
public String lacksRole() default "";
/**
*
*/
public String[] hasAnyRoles() default {};
}

@ -0,0 +1,225 @@
package com.ruoyi.common.security.aspect;
import java.lang.reflect.Method;
import java.util.Collection;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.PatternMatchUtils;
import org.springframework.util.StringUtils;
import com.ruoyi.common.core.exception.PreAuthorizeException;
import com.ruoyi.common.security.annotation.PreAuthorize;
import com.ruoyi.common.security.service.TokenService;
import com.ruoyi.system.api.model.LoginUser;
/**
*
*
* @author ruoyi
*/
@Aspect
@Component
public class PreAuthorizeAspect
{
@Autowired
private TokenService tokenService;
/** 所有权限标识 */
private static final String ALL_PERMISSION = "*:*:*";
/** 管理员角色权限标识 */
private static final String SUPER_ADMIN = "admin";
/** 数组为0时 */
private static final Integer ARRAY_EMPTY = 0;
@Around("@annotation(com.ruoyi.common.security.annotation.PreAuthorize)")
public Object around(ProceedingJoinPoint point) throws Throwable
{
Signature signature = point.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
PreAuthorize annotation = method.getAnnotation(PreAuthorize.class);
if (annotation == null)
{
return point.proceed();
}
if (!StringUtils.isEmpty(annotation.hasPermi()))
{
if (hasPermi(annotation.hasPermi()))
{
return point.proceed();
}
throw new PreAuthorizeException();
}
else if (!StringUtils.isEmpty(annotation.lacksPermi()))
{
if (lacksPermi(annotation.lacksPermi()))
{
return point.proceed();
}
throw new PreAuthorizeException();
}
else if (ARRAY_EMPTY < annotation.hasAnyPermi().length)
{
if (hasAnyPermi(annotation.hasAnyPermi()))
{
return point.proceed();
}
throw new PreAuthorizeException();
}
else if (!StringUtils.isEmpty(annotation.hasRole()))
{
if (hasRole(annotation.hasRole()))
{
return point.proceed();
}
throw new PreAuthorizeException();
}
else if (!StringUtils.isEmpty(annotation.lacksRole()))
{
if (lacksRole(annotation.lacksRole()))
{
return point.proceed();
}
throw new PreAuthorizeException();
}
else if (ARRAY_EMPTY < annotation.hasAnyRoles().length)
{
if (hasAnyRoles(annotation.hasAnyRoles()))
{
return point.proceed();
}
throw new PreAuthorizeException();
}
return point.proceed();
}
/**
*
*
* @param permission
* @return
*/
public boolean hasPermi(String permission)
{
LoginUser userInfo = tokenService.getLoginUser();
if (StringUtils.isEmpty(userInfo) || CollectionUtils.isEmpty(userInfo.getPermissions()))
{
return false;
}
return hasPermissions(userInfo.getPermissions(), permission);
}
/**
* hasPermi
*
* @param permission
* @return
*/
public boolean lacksPermi(String permission)
{
return hasPermi(permission) != true;
}
/**
*
*
* @param permissions
* @return
*/
public boolean hasAnyPermi(String[] permissions)
{
LoginUser userInfo = tokenService.getLoginUser();
if (StringUtils.isEmpty(userInfo) || CollectionUtils.isEmpty(userInfo.getPermissions()))
{
return false;
}
Collection<String> authorities = userInfo.getPermissions();
for (String permission : permissions)
{
if (permission != null && hasPermissions(authorities, permission))
{
return true;
}
}
return false;
}
/**
*
*
* @param role
* @return
*/
public boolean hasRole(String role)
{
LoginUser userInfo = tokenService.getLoginUser();
if (StringUtils.isEmpty(userInfo) || CollectionUtils.isEmpty(userInfo.getRoles()))
{
return false;
}
for (String roleKey : userInfo.getRoles())
{
if (SUPER_ADMIN.equals(roleKey) || roleKey.equals(role))
{
return true;
}
}
return false;
}
/**
* isRole
*
* @param role
* @return
*/
public boolean lacksRole(String role)
{
return hasRole(role) != true;
}
/**
*
*
* @param roles
* @return
*/
public boolean hasAnyRoles(String[] roles)
{
LoginUser userInfo = tokenService.getLoginUser();
if (StringUtils.isEmpty(userInfo) || CollectionUtils.isEmpty(userInfo.getRoles()))
{
return false;
}
for (String role : roles)
{
if (hasRole(role))
{
return true;
}
}
return false;
}
/**
*
*
* @param authorities
* @param permission
* @return
*/
private boolean hasPermissions(Collection<String> authorities, String permission)
{
return authorities.stream().filter(StringUtils::hasText)
.anyMatch(x -> ALL_PERMISSION.contains(x) || PatternMatchUtils.simpleMatch(permission, x));
}
}

@ -1,30 +0,0 @@
package com.ruoyi.common.security.config;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
*
*
* @author ruoyi
**/
@Component
@Configurable
@ConfigurationProperties(prefix = "security.oauth2.ignore")
public class AuthIgnoreConfig
{
private List<String> urls = new ArrayList<>();
public List<String> getUrls()
{
return urls;
}
public void setUrls(List<String> urls)
{
this.urls = urls;
}
}

@ -1,75 +0,0 @@
package com.ruoyi.common.security.config;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.oauth2.provider.token.UserAuthenticationConverter;
import org.springframework.util.StringUtils;
import com.ruoyi.common.core.constant.SecurityConstants;
import com.ruoyi.common.core.text.Convert;
import com.ruoyi.common.security.domain.LoginUser;
/**
* https://my.oschina.net/giegie/blog/3023768 根据checktoken 的结果转化用户信息
*
* @author lengleng
*/
public class CommonUserConverter implements UserAuthenticationConverter
{
private static final String N_A = "N/A";
/**
*
*/
@Override
public Map<String, ?> convertUserAuthentication(Authentication userAuthentication)
{
Map<String, Object> authMap = new LinkedHashMap<>();
authMap.put(USERNAME, userAuthentication.getName());
if (userAuthentication.getAuthorities() != null && !userAuthentication.getAuthorities().isEmpty())
{
authMap.put(AUTHORITIES, AuthorityUtils.authorityListToSet(userAuthentication.getAuthorities()));
}
return authMap;
}
/**
*
*/
@Override
public Authentication extractAuthentication(Map<String, ?> map)
{
if (map.containsKey(USERNAME))
{
Collection<? extends GrantedAuthority> authorities = getAuthorities(map);
Long userId = Convert.toLong(map.get(SecurityConstants.DETAILS_USER_ID));
String username = (String) map.get(SecurityConstants.DETAILS_USERNAME);
LoginUser user = new LoginUser(userId, username, N_A, true, true, true, true, authorities);
return new UsernamePasswordAuthenticationToken(user, N_A, authorities);
}
return null;
}
/**
*
*/
private Collection<? extends GrantedAuthority> getAuthorities(Map<String, ?> map)
{
Object authorities = map.get(AUTHORITIES);
if (authorities instanceof String)
{
return AuthorityUtils.commaSeparatedStringToAuthorityList((String) authorities);
}
if (authorities instanceof Collection)
{
return AuthorityUtils.commaSeparatedStringToAuthorityList(
StringUtils.collectionToCommaDelimitedString((Collection<?>) authorities));
}
throw new IllegalArgumentException("Authorities must be either a String or a Collection");
}
}

@ -1,27 +0,0 @@
package com.ruoyi.common.security.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
/**
*
* @EnableGlobalMethodSecurity(securedEnabled=true)
* @Secured
*
* @EnableGlobalMethodSecurity(jsr250Enabled=true)
* @RolesAllowed
*
* @EnableGlobalMethodSecurity(prePostEnabled=true)
* 使 4
* -@PreAuthorize ,访
* -@PostAuthorize ,false,
* -@PostFilter ,
* -@PreFilter ,
*
*/
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig
{
}

@ -1,82 +0,0 @@
package com.ruoyi.common.security.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.oauth2.OAuth2ClientProperties;
import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.RemoteTokenServices;
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
import org.springframework.security.oauth2.provider.token.UserAuthenticationConverter;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;
/**
* oauth2
*
* @author ruoyi
*/
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter
{
@Autowired
private ResourceServerProperties resourceServerProperties;
@Autowired
private OAuth2ClientProperties oAuth2ClientProperties;
@Bean
public AuthIgnoreConfig authIgnoreConfig()
{
return new AuthIgnoreConfig();
}
@Bean
@LoadBalanced
public RestTemplate restTemplate()
{
RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(new DefaultResponseErrorHandler());
return restTemplate;
}
@Bean
public ResourceServerTokenServices tokenServices()
{
RemoteTokenServices remoteTokenServices = new RemoteTokenServices();
DefaultAccessTokenConverter accessTokenConverter = new DefaultAccessTokenConverter();
UserAuthenticationConverter userTokenConverter = new CommonUserConverter();
accessTokenConverter.setUserTokenConverter(userTokenConverter);
remoteTokenServices.setCheckTokenEndpointUrl(resourceServerProperties.getTokenInfoUri());
remoteTokenServices.setClientId(oAuth2ClientProperties.getClientId());
remoteTokenServices.setClientSecret(oAuth2ClientProperties.getClientSecret());
remoteTokenServices.setRestTemplate(restTemplate());
remoteTokenServices.setAccessTokenConverter(accessTokenConverter);
return remoteTokenServices;
}
@Override
public void configure(HttpSecurity http) throws Exception
{
http.csrf().disable();
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = http
.authorizeRequests();
// 不登录可以访问
authIgnoreConfig().getUrls().forEach(url -> registry.antMatchers(url).permitAll());
registry.anyRequest().authenticated();
}
@Override
public void configure(ResourceServerSecurityConfigurer resources)
{
resources.tokenServices(tokenServices());
}
}

@ -1,24 +0,0 @@
package com.ruoyi.common.security.config;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
import com.ruoyi.common.core.utils.StringUtils;
/**
* SecurityImportBeanDefinitionRegistrar
*
* @author ruoyi
*/
public class SecurityImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar
{
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry)
{
Class<ResourceServerConfig> aClass = ResourceServerConfig.class;
String beanName = StringUtils.uncapitalize(aClass.getSimpleName());
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(ResourceServerConfig.class);
registry.registerBeanDefinition(beanName, beanDefinitionBuilder.getBeanDefinition());
}
}

@ -1,37 +0,0 @@
package com.ruoyi.common.security.domain;
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
/**
*
*
* @author ruoyi
*/
public class LoginUser extends User
{
private static final long serialVersionUID = 1L;
/**
* ID
*/
private Long userId;
public LoginUser(Long userId, String username, String password, boolean enabled, boolean accountNonExpired,
boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities)
{
super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
this.userId = userId;
}
public Long getUserId()
{
return userId;
}
public void setUserId(Long userId)
{
this.userId = userId;
}
}

@ -10,11 +10,11 @@ import feign.RequestInterceptor;
* @author ruoyi * @author ruoyi
**/ **/
@Configuration @Configuration
public class OAuth2FeignConfig public class FeignAutoConfiguration
{ {
@Bean @Bean
public RequestInterceptor requestInterceptor() public RequestInterceptor requestInterceptor()
{ {
return new OAuth2FeignRequestInterceptor(); return new FeignRequestInterceptor();
} }
} }

@ -0,0 +1,45 @@
package com.ruoyi.common.security.feign;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Component;
import com.ruoyi.common.core.constant.CacheConstants;
import com.ruoyi.common.core.utils.ServletUtils;
import com.ruoyi.common.core.utils.StringUtils;
import feign.RequestInterceptor;
import feign.RequestTemplate;
/**
* feign
*
* @author ruoyi
*/
@Component
public class FeignRequestInterceptor implements RequestInterceptor
{
@Override
public void apply(RequestTemplate requestTemplate)
{
HttpServletRequest httpServletRequest = ServletUtils.getRequest();
if (StringUtils.isNotNull(httpServletRequest))
{
Map<String, String> headers = ServletUtils.getHeaders(httpServletRequest);
// 传递用户信息请求头,防止丢失
String userId = headers.get(CacheConstants.DETAILS_USER_ID);
if (StringUtils.isNotEmpty(userId))
{
requestTemplate.header(CacheConstants.DETAILS_USER_ID, userId);
}
String userName = headers.get(CacheConstants.DETAILS_USERNAME);
if (StringUtils.isNotEmpty(userName))
{
requestTemplate.header(CacheConstants.DETAILS_USERNAME, userName);
}
String authentication = headers.get(CacheConstants.AUTHORIZATION_HEADER);
if (StringUtils.isNotEmpty(authentication))
{
requestTemplate.header(CacheConstants.AUTHORIZATION_HEADER, authentication);
}
}
}
}

@ -1,33 +0,0 @@
package com.ruoyi.common.security.feign;
import org.springframework.http.HttpHeaders;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails;
import org.springframework.stereotype.Component;
import com.ruoyi.common.core.constant.SecurityConstants;
import feign.RequestInterceptor;
import feign.RequestTemplate;
/**
* feign
*
* @author ruoyi
*/
@Component
public class OAuth2FeignRequestInterceptor implements RequestInterceptor
{
@Override
public void apply(RequestTemplate requestTemplate)
{
SecurityContext securityContext = SecurityContextHolder.getContext();
Authentication authentication = securityContext.getAuthentication();
if (authentication != null && authentication.getDetails() instanceof OAuth2AuthenticationDetails)
{
OAuth2AuthenticationDetails dateils = (OAuth2AuthenticationDetails) authentication.getDetails();
requestTemplate.header(HttpHeaders.AUTHORIZATION,
String.format("%s %s", SecurityConstants.BEARER_TOKEN_TYPE, dateils.getTokenValue()));
}
}
}

@ -1,33 +0,0 @@
package com.ruoyi.common.security.handler;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson.JSON;
import com.ruoyi.common.core.constant.HttpStatus;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.utils.ServletUtils;
/**
* 访
*
* @author ruoyi
*/
@Component
public class CustomAccessDeniedHandler extends OAuth2AccessDeniedHandler
{
private final Logger logger = LoggerFactory.getLogger(CustomAccessDeniedHandler.class);
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException authException)
{
logger.info("权限不足,请联系管理员 {}", request.getRequestURI());
String msg = authException.getMessage();
ServletUtils.renderString(response, JSON.toJSONString(R.fail(HttpStatus.FORBIDDEN, msg)));
}
}

@ -2,18 +2,14 @@ package com.ruoyi.common.security.handler;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.validation.BindException; import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;
import com.ruoyi.common.core.constant.HttpStatus;
import com.ruoyi.common.core.exception.BaseException; import com.ruoyi.common.core.exception.BaseException;
import com.ruoyi.common.core.exception.CustomException; import com.ruoyi.common.core.exception.CustomException;
import com.ruoyi.common.core.exception.DemoModeException; import com.ruoyi.common.core.exception.DemoModeException;
import com.ruoyi.common.core.exception.PreAuthorizeException;
import com.ruoyi.common.core.utils.StringUtils; import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.core.web.domain.AjaxResult; import com.ruoyi.common.core.web.domain.AjaxResult;
@ -33,7 +29,7 @@ public class GlobalExceptionHandler
@ExceptionHandler(BaseException.class) @ExceptionHandler(BaseException.class)
public AjaxResult baseException(BaseException e) public AjaxResult baseException(BaseException e)
{ {
return AjaxResult.error(e.getMessage()); return AjaxResult.error(e.getDefaultMessage());
} }
/** /**
@ -49,34 +45,6 @@ public class GlobalExceptionHandler
return AjaxResult.error(e.getCode(), e.getMessage()); return AjaxResult.error(e.getCode(), e.getMessage());
} }
@ExceptionHandler(NoHandlerFoundException.class)
public AjaxResult handlerNoFoundException(Exception e)
{
log.error(e.getMessage(), e);
return AjaxResult.error(HttpStatus.NOT_FOUND, "路径不存在,请检查路径是否正确");
}
@ExceptionHandler(AccessDeniedException.class)
public AjaxResult handleAuthorizationException(AccessDeniedException e)
{
log.error(e.getMessage());
return AjaxResult.error(HttpStatus.FORBIDDEN, "没有权限,请联系管理员授权");
}
@ExceptionHandler(AccountExpiredException.class)
public AjaxResult handleAccountExpiredException(AccountExpiredException e)
{
log.error(e.getMessage(), e);
return AjaxResult.error(e.getMessage());
}
@ExceptionHandler(UsernameNotFoundException.class)
public AjaxResult handleUsernameNotFoundException(UsernameNotFoundException e)
{
log.error(e.getMessage(), e);
return AjaxResult.error(e.getMessage());
}
@ExceptionHandler(Exception.class) @ExceptionHandler(Exception.class)
public AjaxResult handleException(Exception e) public AjaxResult handleException(Exception e)
{ {
@ -106,6 +74,15 @@ public class GlobalExceptionHandler
return AjaxResult.error(message); return AjaxResult.error(message);
} }
/**
*
*/
@ExceptionHandler(PreAuthorizeException.class)
public AjaxResult preAuthorizeException(PreAuthorizeException e)
{
return AjaxResult.error("没有权限,请联系管理员授权");
}
/** /**
* *
*/ */

@ -1,167 +0,0 @@
package com.ruoyi.common.security.service;
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.PatternMatchUtils;
import org.springframework.util.StringUtils;
import com.ruoyi.common.security.domain.LoginUser;
import com.ruoyi.common.security.utils.SecurityUtils;
/**
*
*
* @author ruoyi
*/
@Service("ss")
public class PermissionService
{
/** 所有权限标识 */
private static final String ALL_PERMISSION = "*:*:*";
/** 管理员角色权限标识 */
private static final String SUPER_ADMIN = "admin";
private static final String ROLE_DELIMETER = ",";
private static final String PERMISSION_DELIMETER = ",";
/**
*
*
* @param permission
* @return
*/
public boolean hasPermi(String permission)
{
if (StringUtils.isEmpty(permission))
{
return false;
}
LoginUser loginUser = SecurityUtils.getLoginUser();
if (StringUtils.isEmpty(loginUser) || CollectionUtils.isEmpty(loginUser.getAuthorities()))
{
return false;
}
return hasPermissions(loginUser.getAuthorities(), permission);
}
/**
* hasPermi
*
* @param permission
* @return
*/
public boolean lacksPermi(String permission)
{
return hasPermi(permission) != true;
}
/**
*
*
* @param permissions PERMISSION_NAMES_DELIMETER
* @return
*/
public boolean hasAnyPermi(String permissions)
{
if (StringUtils.isEmpty(permissions))
{
return false;
}
LoginUser loginUser = SecurityUtils.getLoginUser();
if (StringUtils.isEmpty(loginUser) || CollectionUtils.isEmpty(loginUser.getAuthorities()))
{
return false;
}
Collection<? extends GrantedAuthority> authorities = loginUser.getAuthorities();
for (String permission : permissions.split(PERMISSION_DELIMETER))
{
if (permission != null && hasPermissions(authorities, permission))
{
return true;
}
}
return false;
}
/**
*
*
* @param role
* @return
*/
public boolean hasRole(String role)
{
if (StringUtils.isEmpty(role))
{
return false;
}
LoginUser loginUser = SecurityUtils.getLoginUser();
if (StringUtils.isEmpty(loginUser) || CollectionUtils.isEmpty(loginUser.getAuthorities()))
{
return false;
}
for (GrantedAuthority authorities : loginUser.getAuthorities())
{
String roleKey = authorities.getAuthority();
if (SUPER_ADMIN.contains(roleKey) || roleKey.contains(role))
{
return true;
}
}
return false;
}
/**
* isRole
*
* @param role
* @return
*/
public boolean lacksRole(String role)
{
return hasRole(role) != true;
}
/**
*
*
* @param roles ROLE_NAMES_DELIMETER
* @return
*/
public boolean hasAnyRoles(String roles)
{
if (StringUtils.isEmpty(roles))
{
return false;
}
LoginUser loginUser = SecurityUtils.getLoginUser();
if (StringUtils.isEmpty(loginUser) || CollectionUtils.isEmpty(loginUser.getAuthorities()))
{
return false;
}
for (String role : roles.split(ROLE_DELIMETER))
{
if (hasRole(role))
{
return true;
}
}
return false;
}
/**
*
*
* @param authorities
* @param permission
* @return
*/
private boolean hasPermissions(Collection<? extends GrantedAuthority> authorities, String permission)
{
return authorities.stream().map(GrantedAuthority::getAuthority).filter(StringUtils::hasText)
.anyMatch(x -> ALL_PERMISSION.contains(x) || PatternMatchUtils.simpleMatch(permission, x));
}
}

@ -1,30 +0,0 @@
package com.ruoyi.common.security.service;
import javax.sql.DataSource;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import com.ruoyi.common.core.constant.CacheConstants;
import com.ruoyi.common.core.constant.SecurityConstants;
/**
* redis
*
* @author ruoyi
*/
public class RedisClientDetailsService extends JdbcClientDetailsService
{
public RedisClientDetailsService(DataSource dataSource)
{
super(dataSource);
super.setSelectClientDetailsSql(SecurityConstants.DEFAULT_SELECT_STATEMENT);
super.setFindClientDetailsSql(SecurityConstants.DEFAULT_FIND_STATEMENT);
}
@Override
@Cacheable(value = CacheConstants.CLIENT_DETAILS_KEY, key = "#clientId", unless = "#result == null")
public ClientDetails loadClientByClientId(String clientId)
{
return super.loadClientByClientId(clientId);
}
}

@ -0,0 +1,123 @@
package com.ruoyi.common.security.service;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.servlet.http.HttpServletRequest;
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.utils.IdUtils;
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.ip.IpUtils;
import com.ruoyi.common.redis.service.RedisService;
import com.ruoyi.system.api.model.LoginUser;
/**
* token
*
* @author ruoyi
*/
@Component
public class TokenService
{
@Autowired
private RedisService redisService;
private final static long EXPIRE_TIME = Constants.TOKEN_EXPIRE * 60;
private final static String ACCESS_TOKEN = CacheConstants.LOGIN_TOKEN_KEY;
protected static final long MILLIS_SECOND = 1000;
/**
*
*/
public Map<String, Object> createToken(LoginUser loginUser)
{
// 生成token
String token = IdUtils.fastUUID();
loginUser.setToken(token);
loginUser.setUserid(loginUser.getSysUser().getUserId());
loginUser.setUsername(loginUser.getSysUser().getUserName());
loginUser.setIpaddr(IpUtils.getIpAddr(ServletUtils.getRequest()));
refreshToken(loginUser);
// 保存或更新用户token
Map<String, Object> map = new HashMap<String, Object>();
map.put("access_token", token);
map.put("expires_in", EXPIRE_TIME);
redisService.setCacheObject(ACCESS_TOKEN + token, loginUser, EXPIRE_TIME, TimeUnit.SECONDS);
return map;
}
/**
*
*
* @return
*/
public LoginUser getLoginUser()
{
return getLoginUser(ServletUtils.getRequest());
}
/**
*
*
* @return
*/
public LoginUser getLoginUser(HttpServletRequest request)
{
// 获取请求携带的令牌
String token = SecurityUtils.getToken(request);
if (StringUtils.isNotEmpty(token))
{
String userKey = getTokenKey(token);
LoginUser user = redisService.getCacheObject(userKey);
return user;
}
return null;
}
/**
*
*/
public void setLoginUser(LoginUser loginUser)
{
if (StringUtils.isNotNull(loginUser) && StringUtils.isNotEmpty(loginUser.getToken()))
{
refreshToken(loginUser);
}
}
public void delLoginUser(String token)
{
if (StringUtils.isNotEmpty(token))
{
String userKey = getTokenKey(token);
redisService.deleteObject(userKey);
}
}
/**
*
*
* @param loginUser
*/
public void refreshToken(LoginUser loginUser)
{
loginUser.setLoginTime(System.currentTimeMillis());
loginUser.setExpireTime(loginUser.getLoginTime() + EXPIRE_TIME * MILLIS_SECOND);
// 根据uuid将loginUser缓存
String userKey = getTokenKey(loginUser.getToken());
redisService.setCacheObject(userKey, loginUser, EXPIRE_TIME, TimeUnit.SECONDS);
}
private String getTokenKey(String token)
{
return ACCESS_TOKEN + token;
}
}

@ -1,83 +0,0 @@
package com.ruoyi.common.security.service;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.enums.UserStatus;
import com.ruoyi.common.core.exception.BaseException;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.security.domain.LoginUser;
import com.ruoyi.system.api.RemoteUserService;
import com.ruoyi.system.api.domain.SysUser;
import com.ruoyi.system.api.model.UserInfo;
/**
*
*
* @author ruoyi
*/
@Service
public class UserDetailsServiceImpl implements UserDetailsService
{
private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class);
@Autowired
private RemoteUserService remoteUserService;
@Override
public UserDetails loadUserByUsername(String username)
{
R<UserInfo> userResult = remoteUserService.getUserInfo(username);
checkUser(userResult, username);
return getUserDetails(userResult);
}
public void checkUser(R<UserInfo> userResult, String username)
{
if (StringUtils.isNull(userResult) || StringUtils.isNull(userResult.getData()))
{
log.info("登录用户:{} 不存在.", username);
throw new UsernameNotFoundException("登录用户:" + username + " 不存在");
}
else if (UserStatus.DELETED.getCode().equals(userResult.getData().getSysUser().getDelFlag()))
{
log.info("登录用户:{} 已被删除.", username);
throw new BaseException("对不起,您的账号:" + username + " 已被删除");
}
else if (UserStatus.DISABLE.getCode().equals(userResult.getData().getSysUser().getStatus()))
{
log.info("登录用户:{} 已被停用.", username);
throw new BaseException("对不起,您的账号:" + username + " 已停用");
}
}
private UserDetails getUserDetails(R<UserInfo> result)
{
UserInfo info = result.getData();
Set<String> dbAuthsSet = new HashSet<String>();
if (StringUtils.isNotEmpty(info.getRoles()))
{
// 获取角色
dbAuthsSet.addAll(info.getRoles());
// 获取权限
dbAuthsSet.addAll(info.getPermissions());
}
Collection<? extends GrantedAuthority> authorities = AuthorityUtils
.createAuthorityList(dbAuthsSet.toArray(new String[0]));
SysUser user = info.getSysUser();
return new LoginUser(user.getUserId(), user.getUserName(), user.getPassword(), true, true, true, true,
authorities);
}
}

@ -1,8 +1,4 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.ruoyi.common.security.service.UserDetailsServiceImpl,\ com.ruoyi.common.security.service.TokenService,\
com.ruoyi.common.security.service.PermissionService,\ com.ruoyi.common.security.aspect.PreAuthorizeAspect,\
com.ruoyi.common.security.config.MethodSecurityConfig,\
com.ruoyi.common.security.handler.CustomAccessDeniedHandler,\
com.ruoyi.common.security.handler.GlobalExceptionHandler com.ruoyi.common.security.handler.GlobalExceptionHandler

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

@ -2,7 +2,6 @@ package com.ruoyi.common.swagger.config;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.List; import java.util.List;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@ -15,11 +14,9 @@ import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo; import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.AuthorizationScope; import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.Contact; import springfox.documentation.service.Contact;
import springfox.documentation.service.GrantType;
import springfox.documentation.service.OAuth;
import springfox.documentation.service.ResourceOwnerPasswordCredentialsGrant;
import springfox.documentation.service.SecurityReference; import springfox.documentation.service.SecurityReference;
import springfox.documentation.spi.DocumentationType; import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext; import springfox.documentation.spi.service.contexts.SecurityContext;
@ -73,22 +70,33 @@ public class SwaggerAutoConfiguration
.apis(RequestHandlerSelectors.basePackage(swaggerProperties.getBasePackage())) .apis(RequestHandlerSelectors.basePackage(swaggerProperties.getBasePackage()))
.paths(Predicates.and(Predicates.not(Predicates.or(excludePath)), Predicates.or(basePath))) .paths(Predicates.and(Predicates.not(Predicates.or(excludePath)), Predicates.or(basePath)))
.build() .build()
.securitySchemes(Collections.singletonList(securitySchema())) .securitySchemes(securitySchemes())
.securityContexts(Collections.singletonList(securityContext())) .securityContexts(securityContexts())
.pathMapping("/"); .pathMapping("/");
} }
/** /**
* URL * tokenAuthorization
* */
* @return private List<ApiKey> securitySchemes()
{
List<ApiKey> apiKeyList = new ArrayList<ApiKey>();
apiKeyList.add(new ApiKey("Authorization", "Authorization", "header"));
return apiKeyList;
}
/**
*
*/ */
private SecurityContext securityContext() private List<SecurityContext> securityContexts()
{ {
return SecurityContext.builder() List<SecurityContext> securityContexts = new ArrayList<>();
securityContexts.add(
SecurityContext.builder()
.securityReferences(defaultAuth()) .securityReferences(defaultAuth())
.forPaths(PathSelectors.regex(swaggerProperties().getAuthorization().getAuthRegex())) .forPaths(PathSelectors.regex("^(?!auth).*$"))
.build(); .build());
return securityContexts;
} }
/** /**
@ -98,22 +106,12 @@ public class SwaggerAutoConfiguration
*/ */
private List<SecurityReference> defaultAuth() private List<SecurityReference> defaultAuth()
{ {
ArrayList<AuthorizationScope> authorizationScopeList = new ArrayList<>(); AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
swaggerProperties().getAuthorization().getAuthorizationScopeList().forEach(authorizationScope -> authorizationScopeList.add(new AuthorizationScope(authorizationScope.getScope(), authorizationScope.getDescription()))); AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
AuthorizationScope[] authorizationScopes = new AuthorizationScope[authorizationScopeList.size()]; authorizationScopes[0] = authorizationScope;
return Collections.singletonList(SecurityReference.builder() List<SecurityReference> securityReferences = new ArrayList<>();
.reference(swaggerProperties().getAuthorization().getName()) securityReferences.add(new SecurityReference("Authorization", authorizationScopes));
.scopes(authorizationScopeList.toArray(authorizationScopes)) return securityReferences;
.build());
}
private OAuth securitySchema()
{
ArrayList<AuthorizationScope> authorizationScopeList = new ArrayList<>();
swaggerProperties().getAuthorization().getAuthorizationScopeList().forEach(authorizationScope -> authorizationScopeList.add(new AuthorizationScope(authorizationScope.getScope(), authorizationScope.getDescription())));
ArrayList<GrantType> grantTypes = new ArrayList<>();
swaggerProperties().getAuthorization().getTokenUrlList().forEach(tokenUrl -> grantTypes.add(new ResourceOwnerPasswordCredentialsGrant(tokenUrl)));
return new OAuth(swaggerProperties().getAuthorization().getName(), authorizationScopeList, grantTypes);
} }
private ApiInfo apiInfo(SwaggerProperties swaggerProperties) private ApiInfo apiInfo(SwaggerProperties swaggerProperties)
@ -129,4 +127,3 @@ public class SwaggerAutoConfiguration
.build(); .build();
} }
} }

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi</artifactId> <artifactId>ruoyi</artifactId>
<version>2.0.0</version> <version>2.5.0</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@ -85,6 +85,7 @@
</dependencies> </dependencies>
<build> <build>
<finalName>${project.artifactId}</finalName>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>

@ -5,6 +5,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import com.google.code.kaptcha.impl.DefaultKaptcha; import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config; import com.google.code.kaptcha.util.Config;
import static com.google.code.kaptcha.Constants.*;
/** /**
* *
@ -14,42 +15,67 @@ import com.google.code.kaptcha.util.Config;
@Configuration @Configuration
public class CaptchaConfig public class CaptchaConfig
{ {
@Bean(name = "captchaProducer")
public DefaultKaptcha getKaptchaBean()
{
DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
Properties properties = new Properties();
// 是否有边框 默认为true 我们可以自己设置yesno
properties.setProperty(KAPTCHA_BORDER, "yes");
// 验证码文本字符颜色 默认为Color.BLACK
properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "black");
// 验证码图片宽度 默认为200
properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160");
// 验证码图片高度 默认为50
properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60");
// 验证码文本字符大小 默认为40
properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "38");
// KAPTCHA_SESSION_KEY
properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCode");
// 验证码文本字符长度 默认为5
properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4");
// 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier");
// 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy
properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy");
Config config = new Config(properties);
defaultKaptcha.setConfig(config);
return defaultKaptcha;
}
@Bean(name = "captchaProducerMath") @Bean(name = "captchaProducerMath")
public DefaultKaptcha getKaptchaBeanMath() public DefaultKaptcha getKaptchaBeanMath()
{ {
DefaultKaptcha defaultKaptcha = new DefaultKaptcha(); DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
Properties properties = new Properties(); Properties properties = new Properties();
// 是否有边框 默认为true 我们可以自己设置yesno // 是否有边框 默认为true 我们可以自己设置yesno
properties.setProperty("kaptcha.border", "yes"); properties.setProperty(KAPTCHA_BORDER, "yes");
// 边框颜色 默认为Color.BLACK // 边框颜色 默认为Color.BLACK
properties.setProperty("kaptcha.border.color", "105,179,90"); properties.setProperty(KAPTCHA_BORDER_COLOR, "105,179,90");
// 验证码文本字符颜色 默认为Color.BLACK // 验证码文本字符颜色 默认为Color.BLACK
properties.setProperty("kaptcha.textproducer.font.color", "blue"); properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "blue");
// 验证码图片宽度 默认为200 // 验证码图片宽度 默认为200
properties.setProperty("kaptcha.image.width", "160"); properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160");
// 验证码图片高度 默认为50 // 验证码图片高度 默认为50
properties.setProperty("kaptcha.image.height", "60"); properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60");
// 验证码文本字符大小 默认为40 // 验证码文本字符大小 默认为40
properties.setProperty("kaptcha.textproducer.font.size", "35"); properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "35");
// KAPTCHA_SESSION_KEY // KAPTCHA_SESSION_KEY
properties.setProperty("kaptcha.session.key", "kaptchaCodeMath"); properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCodeMath");
// 验证码文本生成器 // 验证码文本生成器
properties.setProperty("kaptcha.textproducer.impl", "com.ruoyi.gateway.config.KaptchaTextCreator"); properties.setProperty(KAPTCHA_TEXTPRODUCER_IMPL, "com.ruoyi.gateway.config.KaptchaTextCreator");
// 验证码文本字符间距 默认为2 // 验证码文本字符间距 默认为2
properties.setProperty("kaptcha.textproducer.char.space", "3"); properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_SPACE, "3");
// 验证码文本字符长度 默认为5 // 验证码文本字符长度 默认为5
properties.setProperty("kaptcha.textproducer.char.length", "6"); properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "6");
// 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
// fontSize) properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier");
properties.setProperty("kaptcha.textproducer.font.names", "Arial,Courier");
// 验证码噪点颜色 默认为Color.BLACK // 验证码噪点颜色 默认为Color.BLACK
properties.setProperty("kaptcha.noise.color", "white"); properties.setProperty(KAPTCHA_NOISE_COLOR, "white");
// 干扰实现类 // 干扰实现类
properties.setProperty("kaptcha.noise.impl", "com.google.code.kaptcha.impl.NoNoise"); properties.setProperty(KAPTCHA_NOISE_IMPL, "com.google.code.kaptcha.impl.NoNoise");
// 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy
// 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy");
// 阴影com.google.code.kaptcha.impl.ShadowGimpy
properties.setProperty("kaptcha.obscurificator.impl", "com.google.code.kaptcha.impl.ShadowGimpy");
Config config = new Config(properties); Config config = new Config(properties);
defaultKaptcha.setConfig(config); defaultKaptcha.setConfig(config);
return defaultKaptcha; return defaultKaptcha;

@ -10,6 +10,11 @@ import org.springframework.stereotype.Component;
import springfox.documentation.swagger.web.SwaggerResource; import springfox.documentation.swagger.web.SwaggerResource;
import springfox.documentation.swagger.web.SwaggerResourcesProvider; import springfox.documentation.swagger.web.SwaggerResourcesProvider;
/**
*
*
* @author ruoyi
*/
@Component @Component
public class SwaggerProvider implements SwaggerResourcesProvider public class SwaggerProvider implements SwaggerResourcesProvider
{ {

@ -0,0 +1,33 @@
package com.ruoyi.gateway.config.properties;
import java.util.ArrayList;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;
/**
*
*
* @author ruoyi
*/
@Configuration
@RefreshScope
@ConfigurationProperties(prefix = "ignore")
public class IgnoreWhiteProperties
{
/**
*
*/
private List<String> whites = new ArrayList<>();
public List<String> getWhites()
{
return whites;
}
public void setWhites(List<String> whites)
{
this.whites = whites;
}
}

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

Loading…
Cancel
Save