Pre Merge pull request !346 from potato/master
This commit is contained in:
commit
0f5f632465
14
README.md
14
README.md
|
|
@ -1,3 +1,17 @@
|
||||||
|
|
||||||
|
# 源码更新技巧
|
||||||
|
## 1、切换同步源码地址 (保持与官方同步),然后更新本地代码
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git remote set-url origin https://gitee.com/czq1ac/RuoYi-Cloud_2023.git
|
||||||
|
```
|
||||||
|
## 2、更新本地代码成功后,切换会原先的仓库地址,push更新
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git remote set-url origin https://gitee.com/czq1ac/RuoYi-Cloud.git
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img alt="logo" src="https://oscimg.oschina.net/oscnet/up-b99b286755aef70355a7084753f89cdb7c9.png">
|
<img alt="logo" src="https://oscimg.oschina.net/oscnet/up-b99b286755aef70355a7084753f89cdb7c9.png">
|
||||||
</p>
|
</p>
|
||||||
|
|
|
||||||
1
pom.xml
1
pom.xml
|
|
@ -216,6 +216,7 @@
|
||||||
<module>ruoyi-modules</module>
|
<module>ruoyi-modules</module>
|
||||||
<module>ruoyi-api</module>
|
<module>ruoyi-api</module>
|
||||||
<module>ruoyi-common</module>
|
<module>ruoyi-common</module>
|
||||||
|
<module>ruoyi-web</module>
|
||||||
</modules>
|
</modules>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ import com.ruoyi.system.api.factory.RemoteFileFallbackFactory;
|
||||||
*
|
*
|
||||||
* @author ruoyi
|
* @author ruoyi
|
||||||
*/
|
*/
|
||||||
@FeignClient(contextId = "remoteFileService", value = ServiceNameConstants.FILE_SERVICE, fallbackFactory = RemoteFileFallbackFactory.class)
|
@FeignClient(contextId = "remoteFileService",url = "${feign.debug.url.file:}", value = ServiceNameConstants.FILE_SERVICE, fallbackFactory = RemoteFileFallbackFactory.class)
|
||||||
public interface RemoteFileService
|
public interface RemoteFileService
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ import com.ruoyi.system.api.factory.RemoteLogFallbackFactory;
|
||||||
*
|
*
|
||||||
* @author ruoyi
|
* @author ruoyi
|
||||||
*/
|
*/
|
||||||
@FeignClient(contextId = "remoteLogService", value = ServiceNameConstants.SYSTEM_SERVICE, fallbackFactory = RemoteLogFallbackFactory.class)
|
@FeignClient(contextId = "remoteLogService",url = "${feign.debug.url.system:}", value = ServiceNameConstants.SYSTEM_SERVICE, fallbackFactory = RemoteLogFallbackFactory.class)
|
||||||
public interface RemoteLogService
|
public interface RemoteLogService
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ import com.ruoyi.system.api.model.LoginUser;
|
||||||
*
|
*
|
||||||
* @author ruoyi
|
* @author ruoyi
|
||||||
*/
|
*/
|
||||||
@FeignClient(contextId = "remoteUserService", value = ServiceNameConstants.SYSTEM_SERVICE, fallbackFactory = RemoteUserFallbackFactory.class)
|
@FeignClient(contextId = "remoteUserService",url = "${feign.debug.url.system:}", value = ServiceNameConstants.SYSTEM_SERVICE, fallbackFactory = RemoteUserFallbackFactory.class)
|
||||||
public interface RemoteUserService
|
public interface RemoteUserService
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
"author": "若依",
|
"author": "若依",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vue-cli-service serve",
|
"dev": "export NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve",
|
||||||
"build:prod": "vue-cli-service build",
|
"build:prod": "vue-cli-service build",
|
||||||
"build:stage": "vue-cli-service build --mode staging",
|
"build:stage": "vue-cli-service build --mode staging",
|
||||||
"preview": "node build/index.js --preview",
|
"preview": "node build/index.js --preview",
|
||||||
|
|
|
||||||
|
|
@ -87,11 +87,11 @@ export default {
|
||||||
],
|
],
|
||||||
code: [{ required: true, trigger: "change", message: "请输入验证码" }]
|
code: [{ required: true, trigger: "change", message: "请输入验证码" }]
|
||||||
},
|
},
|
||||||
loading: false,
|
// loading: true,
|
||||||
// 验证码开关
|
// 验证码开关
|
||||||
captchaEnabled: true,
|
captchaEnabled: true,
|
||||||
// 注册开关
|
// 注册开关
|
||||||
register: false,
|
register: true,
|
||||||
redirect: undefined
|
redirect: undefined
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>ruoyi</artifactId>
|
||||||
|
<groupId>com.ruoyi</groupId>
|
||||||
|
<version>3.6.3</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>ruoyi-web</artifactId>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
|
<description>
|
||||||
|
ruoyi-单体应用业务模块
|
||||||
|
</description>
|
||||||
|
<modules>
|
||||||
|
<module>ruoyi-web-admin</module>
|
||||||
|
</modules>
|
||||||
|
|
||||||
|
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,176 @@
|
||||||
|
# RuoYi-Cloud SPRING BOOT单体项目改造 问题记录
|
||||||
|
## 1、项目配置集中管理;删除无用不配置;不注册服务中心
|
||||||
|
## 2、项目单体路由规则修改,和cloud项目路由规则保持一致
|
||||||
|
参考 com.ruoyi.web.admin.config.AutoPrefixUrlMapping [AutoPrefixUrlMapping](src/main/java/com/ruoyi/web/admin/config/AutoPrefixUrlMapping.java)
|
||||||
|
```java
|
||||||
|
// # 认证中心 uri: lb://ruoyi-auth
|
||||||
|
routesMap.put("com.ruoyi.auth.controller", "/auth");
|
||||||
|
// # 代码生成 uri: lb://ruoyi-gen
|
||||||
|
routesMap.put("com.ruoyi.gen.controller", "/code");
|
||||||
|
// # 定时任务 uri: lb://ruoyi-job
|
||||||
|
routesMap.put("com.ruoyi.job.controller", "/schedule");
|
||||||
|
// # 系统模块 uri: lb://ruoyi-system
|
||||||
|
routesMap.put("com.ruoyi.system.controller", "/system");
|
||||||
|
// # 文件服务 uri: lb://ruoyi-file
|
||||||
|
routesMap.put("com.ruoyi.file.controller", "/file");
|
||||||
|
|
||||||
|
```
|
||||||
|
## 3、fegin 本地调用配置切换;增加url 属性
|
||||||
|
|
||||||
|
```java
|
||||||
|
@FeignClient(contextId = "remoteFileService",url = "${feign.debug.url.file:}", value = ServiceNameConstants.FILE_SERVICE, fallbackFactory = RemoteFileFallbackFactory.class)
|
||||||
|
public interface RemoteFileService
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
## 3、gateway 验证码生成和校验功能迁移
|
||||||
|
## 4、gateway 权限认证过滤器功能迁移
|
||||||
|
## 5、解决验证码校验问题和 filter异常处理问题
|
||||||
|
## 6、验证码防止重复刷新漏洞登录采用字符验证码;开启注册
|
||||||
|
|
||||||
|
|
||||||
|
# 源码更新技巧
|
||||||
|
## 1、切换同步源码地址 (保持与官方同步),然后更新本地代码
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git remote set-url origin https://gitee.com/czq1ac/RuoYi-Cloud_2023.git
|
||||||
|
```
|
||||||
|
## 2、更新本地代码成功后,切换会原先的仓库地址,push更新
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git remote set-url origin https://gitee.com/czq1ac/RuoYi-Cloud.git
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img alt="logo" src="https://oscimg.oschina.net/oscnet/up-b99b286755aef70355a7084753f89cdb7c9.png">
|
||||||
|
</p>
|
||||||
|
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">RuoYi v3.6.3</h1>
|
||||||
|
<h4 align="center">基于 Vue/Element UI 和 Spring Boot/Spring Cloud & Alibaba 前后端分离的分布式微服务架构</h4>
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://gitee.com/y_project/RuoYi-Cloud/stargazers"><img src="https://gitee.com/y_project/RuoYi-Cloud/badge/star.svg?theme=dark"></a>
|
||||||
|
<a href="https://gitee.com/y_project/RuoYi-Cloud"><img src="https://img.shields.io/badge/RuoYi-v3.6.3-brightgreen.svg"></a>
|
||||||
|
<a href="https://gitee.com/y_project/RuoYi-Cloud/blob/master/LICENSE"><img src="https://img.shields.io/github/license/mashape/apistatus.svg"></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
## 平台简介
|
||||||
|
|
||||||
|
若依是一套全部开源的快速开发平台,毫无保留给个人及企业免费使用。
|
||||||
|
|
||||||
|
* 采用前后端分离的模式,微服务版本前端(基于 [RuoYi-Vue](https://gitee.com/y_project/RuoYi-Vue))。
|
||||||
|
* 后端采用Spring Boot、Spring Cloud & Alibaba。
|
||||||
|
* 注册中心、配置中心选型Nacos,权限认证使用Redis。
|
||||||
|
* 流量控制框架选型Sentinel,分布式事务选型Seata。
|
||||||
|
* 提供了技术栈([Vue3](https://v3.cn.vuejs.org) [Element Plus](https://element-plus.org/zh-CN) [Vite](https://cn.vitejs.dev))版本[RuoYi-Cloud-Vue3](https://github.com/yangzongzhuan/RuoYi-Cloud-Vue3),保持同步更新。
|
||||||
|
* 如需不分离应用,请移步 [RuoYi](https://gitee.com/y_project/RuoYi),如需分离应用,请移步 [RuoYi-Vue](https://gitee.com/y_project/RuoYi-Vue)
|
||||||
|
* 阿里云折扣场:[点我进入](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版本。
|
||||||
|
|
||||||
|
## 系统模块
|
||||||
|
|
||||||
|
~~~
|
||||||
|
com.ruoyi
|
||||||
|
├── ruoyi-ui // 前端框架 [80]
|
||||||
|
├── ruoyi-gateway // 网关模块 [8080]
|
||||||
|
├── ruoyi-auth // 认证中心 [9200]
|
||||||
|
├── ruoyi-api // 接口模块
|
||||||
|
│ └── ruoyi-api-system // 系统接口
|
||||||
|
├── ruoyi-common // 通用模块
|
||||||
|
│ └── ruoyi-common-core // 核心模块
|
||||||
|
│ └── ruoyi-common-datascope // 权限范围
|
||||||
|
│ └── ruoyi-common-datasource // 多数据源
|
||||||
|
│ └── ruoyi-common-log // 日志记录
|
||||||
|
│ └── ruoyi-common-redis // 缓存服务
|
||||||
|
│ └── ruoyi-common-seata // 分布式事务
|
||||||
|
│ └── ruoyi-common-security // 安全模块
|
||||||
|
│ └── ruoyi-common-swagger // 系统接口
|
||||||
|
├── ruoyi-modules // 业务模块
|
||||||
|
│ └── ruoyi-system // 系统模块 [9201]
|
||||||
|
│ └── ruoyi-gen // 代码生成 [9202]
|
||||||
|
│ └── ruoyi-job // 定时任务 [9203]
|
||||||
|
│ └── ruoyi-file // 文件服务 [9300]
|
||||||
|
├── ruoyi-visual // 图形化管理模块
|
||||||
|
│ └── ruoyi-visual-monitor // 监控中心 [9100]
|
||||||
|
├──pom.xml // 公共依赖
|
||||||
|
~~~
|
||||||
|
|
||||||
|
## 架构图
|
||||||
|
|
||||||
|
<img src="https://oscimg.oschina.net/oscnet/up-82e9722ecb846786405a904bafcf19f73f3.png"/>
|
||||||
|
|
||||||
|
## 内置功能
|
||||||
|
|
||||||
|
1. 用户管理:用户是系统操作者,该功能主要完成系统用户配置。
|
||||||
|
2. 部门管理:配置系统组织机构(公司、部门、小组),树结构展现支持数据权限。
|
||||||
|
3. 岗位管理:配置系统用户所属担任职务。
|
||||||
|
4. 菜单管理:配置系统菜单,操作权限,按钮权限标识等。
|
||||||
|
5. 角色管理:角色菜单权限分配、设置角色按机构进行数据范围权限划分。
|
||||||
|
6. 字典管理:对系统中经常使用的一些较为固定的数据进行维护。
|
||||||
|
7. 参数管理:对系统动态配置常用参数。
|
||||||
|
8. 通知公告:系统通知公告信息发布维护。
|
||||||
|
9. 操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。
|
||||||
|
10. 登录日志:系统登录日志记录查询包含登录异常。
|
||||||
|
11. 在线用户:当前系统中活跃用户状态监控。
|
||||||
|
12. 定时任务:在线(添加、修改、删除)任务调度包含执行结果日志。
|
||||||
|
13. 代码生成:前后端代码的生成(java、html、xml、sql)支持CRUD下载 。
|
||||||
|
14. 系统接口:根据业务代码自动生成相关的api接口文档。
|
||||||
|
15. 服务监控:监视当前系统CPU、内存、磁盘、堆栈等相关信息。
|
||||||
|
16. 在线构建器:拖动表单元素生成相应的HTML代码。
|
||||||
|
17. 连接池监视:监视当前系统数据库连接池状态,可进行分析SQL找出系统性能瓶颈。
|
||||||
|
|
||||||
|
## 在线体验
|
||||||
|
|
||||||
|
- admin/admin123
|
||||||
|
- 陆陆续续收到一些打赏,为了更好的体验已用于演示服务器升级。谢谢各位小伙伴。
|
||||||
|
|
||||||
|
演示地址:http://ruoyi.vip
|
||||||
|
文档地址:http://doc.ruoyi.vip
|
||||||
|
|
||||||
|
## 演示图
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td><img src="https://oscimg.oschina.net/oscnet/cd1f90be5f2684f4560c9519c0f2a232ee8.jpg"/></td>
|
||||||
|
<td><img src="https://oscimg.oschina.net/oscnet/1cbcf0e6f257c7d3a063c0e3f2ff989e4b3.jpg"/></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><img src="https://oscimg.oschina.net/oscnet/up-8074972883b5ba0622e13246738ebba237a.png"/></td>
|
||||||
|
<td><img src="https://oscimg.oschina.net/oscnet/up-9f88719cdfca9af2e58b352a20e23d43b12.png"/></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><img src="https://oscimg.oschina.net/oscnet/up-39bf2584ec3a529b0d5a3b70d15c9b37646.png"/></td>
|
||||||
|
<td><img src="https://oscimg.oschina.net/oscnet/up-4148b24f58660a9dc347761e4cf6162f28f.png"/></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><img src="https://oscimg.oschina.net/oscnet/up-b2d62ceb95d2dd9b3fbe157bb70d26001e9.png"/></td>
|
||||||
|
<td><img src="https://oscimg.oschina.net/oscnet/up-d67451d308b7a79ad6819723396f7c3d77a.png"/></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><img src="https://oscimg.oschina.net/oscnet/5e8c387724954459291aafd5eb52b456f53.jpg"/></td>
|
||||||
|
<td><img src="https://oscimg.oschina.net/oscnet/644e78da53c2e92a95dfda4f76e6d117c4b.jpg"/></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><img src="https://oscimg.oschina.net/oscnet/up-8370a0d02977eebf6dbf854c8450293c937.png"/></td>
|
||||||
|
<td><img src="https://oscimg.oschina.net/oscnet/up-49003ed83f60f633e7153609a53a2b644f7.png"/></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><img src="https://oscimg.oschina.net/oscnet/up-d4fe726319ece268d4746602c39cffc0621.png"/></td>
|
||||||
|
<td><img src="https://oscimg.oschina.net/oscnet/up-c195234bbcd30be6927f037a6755e6ab69c.png"/></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><img src="https://oscimg.oschina.net/oscnet/up-ece3fd37a3d4bb75a3926e905a3c5629055.png"/></td>
|
||||||
|
<td><img src="https://oscimg.oschina.net/oscnet/up-92ffb7f3835855cff100fa0f754a6be0d99.png"/></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><img src="https://oscimg.oschina.net/oscnet/up-ff9e3066561574aca73005c5730c6a41f15.png"/></td>
|
||||||
|
<td><img src="https://oscimg.oschina.net/oscnet/up-5e4daac0bb59612c5038448acbcef235e3a.png"/></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
## 若依微服务交流群
|
||||||
|
|
||||||
|
QQ群: [](https://jq.qq.com/?_wv=1027&k=yqInfq0S) [](https://jq.qq.com/?_wv=1027&k=Oy1mb3p8) [](https://jq.qq.com/?_wv=1027&k=rvxkJtXK) [](https://jq.qq.com/?_wv=1027&k=0Ck3PvTe) [](https://jq.qq.com/?_wv=1027&k=FnHHP4TT) [](https://jq.qq.com/?_wv=1027&k=qdT1Ojpz) [](https://jq.qq.com/?_wv=1027&k=nw3OiyXs) [](https://jq.qq.com/?_wv=1027&k=kiU5WDls) [](https://jq.qq.com/?_wv=1027&k=MtBy6YfT) [](https://jq.qq.com/?_wv=1027&k=FqImHgH2) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=G4jZ4EtdT50PhnMBudTnEwgonxkXOscJ&authKey=FkGHYfoTKlGE6wHdKdjH9bVoOgQjtLP9WM%2Fj7pqGY1msoqw9uxDiBo39E2mLgzYg&noverify=0&group_code=128355254) 点击按钮入群。
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>ruoyi-web</artifactId>
|
||||||
|
<groupId>com.ruoyi</groupId>
|
||||||
|
<version>3.6.3</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>ruoyi-web-admin</artifactId>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
|
||||||
|
<!-- ruoyi-modules-system系统模块 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.ruoyi</groupId>
|
||||||
|
<artifactId>ruoyi-modules-system</artifactId>
|
||||||
|
<version>${ruoyi.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- ruoyi-modules-job定时任务 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.ruoyi</groupId>
|
||||||
|
<artifactId>ruoyi-modules-job</artifactId>
|
||||||
|
<version>${ruoyi.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- ruoyi-modules-gen代码生成 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.ruoyi</groupId>
|
||||||
|
<artifactId>ruoyi-modules-gen</artifactId>
|
||||||
|
<version>${ruoyi.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- ruoyi-modules-file文件服务 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.ruoyi</groupId>
|
||||||
|
<artifactId>ruoyi-modules-file</artifactId>
|
||||||
|
<version>${ruoyi.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- ruoyi-auth认证授权中心 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.ruoyi</groupId>
|
||||||
|
<artifactId>ruoyi-auth</artifactId>
|
||||||
|
<version>${ruoyi.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!--验证码 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>pro.fessional</groupId>
|
||||||
|
<artifactId>kaptcha</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
package com.ruoyi.web.admin;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
|
|
||||||
|
import com.ruoyi.common.security.annotation.EnableCustomConfig;
|
||||||
|
import com.ruoyi.common.security.annotation.EnableRyFeignClients;
|
||||||
|
import com.ruoyi.common.swagger.annotation.EnableCustomSwagger2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统模块
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
@EnableCustomConfig
|
||||||
|
@EnableCustomSwagger2
|
||||||
|
@EnableRyFeignClients
|
||||||
|
@SpringBootApplication
|
||||||
|
@ComponentScan("com.ruoyi")
|
||||||
|
public class RuoYiWebAdminApplication
|
||||||
|
{
|
||||||
|
public static void main(String[] args)
|
||||||
|
{
|
||||||
|
SpringApplication.run(RuoYiWebAdminApplication.class, args);
|
||||||
|
System.out.println("(♥◠‿◠)ノ゙ 系统模块启动成功 ლ(´ڡ`ლ)゙ \n" +
|
||||||
|
" .-------. ____ __ \n" +
|
||||||
|
" | _ _ \\ \\ \\ / / \n" +
|
||||||
|
" | ( ' ) | \\ _. / ' \n" +
|
||||||
|
" |(_ o _) / _( )_ .' \n" +
|
||||||
|
" | (_,_).' __ ___(_ o _)' \n" +
|
||||||
|
" | |\\ \\ | || |(_,_)' \n" +
|
||||||
|
" | | \\ `' /| `-' / \n" +
|
||||||
|
" | | \\ / \\ / \n" +
|
||||||
|
" ''-' `'-' `-..-' ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
package com.ruoyi.web.admin.config;
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自动补全路由前缀配置类
|
||||||
|
* @author 1763113879@qq.com
|
||||||
|
* @version V2.1
|
||||||
|
* @since 2.1.0 2023/11/15 14:48
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class AutoPrefixConfiguration implements WebMvcRegistrations {
|
||||||
|
@Override
|
||||||
|
public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
|
||||||
|
return new AutoPrefixUrlMapping();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
package com.ruoyi.web.admin.config;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
|
||||||
|
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按照目录结构/包名添加前缀
|
||||||
|
*
|
||||||
|
* @author 1763113879@qq.com
|
||||||
|
* @version V2.1
|
||||||
|
* @since 2.1.0 2023/11/15 14:20
|
||||||
|
*/
|
||||||
|
public class AutoPrefixUrlMapping extends RequestMappingHandlerMapping {
|
||||||
|
|
||||||
|
// 路由映射
|
||||||
|
static Map<String, String> routesMap = new HashMap<String, String>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
// # 认证中心 uri: lb://ruoyi-auth
|
||||||
|
routesMap.put("com.ruoyi.auth.controller", "/auth");
|
||||||
|
// # 代码生成 uri: lb://ruoyi-gen
|
||||||
|
routesMap.put("com.ruoyi.gen.controller", "/code");
|
||||||
|
// # 定时任务 uri: lb://ruoyi-job
|
||||||
|
routesMap.put("com.ruoyi.job.controller", "/schedule");
|
||||||
|
// # 系统模块 uri: lb://ruoyi-system
|
||||||
|
routesMap.put("com.ruoyi.system.controller", "/system");
|
||||||
|
// # 文件服务 uri: lb://ruoyi-file
|
||||||
|
routesMap.put("com.ruoyi.file.controller", "/file");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重写方法路由获取
|
||||||
|
*
|
||||||
|
* @param method
|
||||||
|
* @param handlerType
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
|
||||||
|
RequestMappingInfo mappingInfo = super.getMappingForMethod(method, handlerType);
|
||||||
|
|
||||||
|
if (Objects.nonNull(mappingInfo)) {
|
||||||
|
String prefix = this.getPrefix(handlerType);
|
||||||
|
|
||||||
|
if (prefix != null) {
|
||||||
|
String[] paths = mappingInfo.getPatternValues()
|
||||||
|
.stream()
|
||||||
|
.map(path -> prefix + path)
|
||||||
|
.toArray(String[]::new);
|
||||||
|
|
||||||
|
return mappingInfo.mutate()
|
||||||
|
.paths(paths)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mappingInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取方法路由前缀
|
||||||
|
*
|
||||||
|
* @param handleType
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private String getPrefix(Class<?> handleType) {
|
||||||
|
String packageName = handleType.getPackage()
|
||||||
|
.getName();
|
||||||
|
// 使用foreach循环遍历HashMap 符合路由规则的添加前缀
|
||||||
|
for (String key : routesMap.keySet()) {
|
||||||
|
if (packageName.startsWith(key)) {
|
||||||
|
return routesMap.get(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,99 @@
|
||||||
|
package com.ruoyi.web.admin.config;
|
||||||
|
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import com.google.code.kaptcha.impl.DefaultKaptcha;
|
||||||
|
import com.google.code.kaptcha.util.Config;
|
||||||
|
|
||||||
|
import static com.google.code.kaptcha.Constants.KAPTCHA_BORDER;
|
||||||
|
import static com.google.code.kaptcha.Constants.KAPTCHA_BORDER_COLOR;
|
||||||
|
import static com.google.code.kaptcha.Constants.KAPTCHA_IMAGE_HEIGHT;
|
||||||
|
import static com.google.code.kaptcha.Constants.KAPTCHA_IMAGE_WIDTH;
|
||||||
|
import static com.google.code.kaptcha.Constants.KAPTCHA_NOISE_COLOR;
|
||||||
|
import static com.google.code.kaptcha.Constants.KAPTCHA_NOISE_IMPL;
|
||||||
|
import static com.google.code.kaptcha.Constants.KAPTCHA_OBSCURIFICATOR_IMPL;
|
||||||
|
import static com.google.code.kaptcha.Constants.KAPTCHA_SESSION_CONFIG_KEY;
|
||||||
|
import static com.google.code.kaptcha.Constants.KAPTCHA_TEXTPRODUCER_CHAR_LENGTH;
|
||||||
|
import static com.google.code.kaptcha.Constants.KAPTCHA_TEXTPRODUCER_CHAR_SPACE;
|
||||||
|
import static com.google.code.kaptcha.Constants.KAPTCHA_TEXTPRODUCER_FONT_COLOR;
|
||||||
|
import static com.google.code.kaptcha.Constants.KAPTCHA_TEXTPRODUCER_FONT_NAMES;
|
||||||
|
import static com.google.code.kaptcha.Constants.KAPTCHA_TEXTPRODUCER_FONT_SIZE;
|
||||||
|
import static com.google.code.kaptcha.Constants.KAPTCHA_TEXTPRODUCER_IMPL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码配置
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class CaptchaConfig
|
||||||
|
{
|
||||||
|
@Bean(name = "captchaProducer")
|
||||||
|
public DefaultKaptcha getKaptchaBean()
|
||||||
|
{
|
||||||
|
DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
|
||||||
|
Properties properties = new Properties();
|
||||||
|
// 是否有边框 默认为true 我们可以自己设置yes,no
|
||||||
|
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")
|
||||||
|
public DefaultKaptcha getKaptchaBeanMath()
|
||||||
|
{
|
||||||
|
DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
|
||||||
|
Properties properties = new Properties();
|
||||||
|
// 是否有边框 默认为true 我们可以自己设置yes,no
|
||||||
|
properties.setProperty(KAPTCHA_BORDER, "yes");
|
||||||
|
// 边框颜色 默认为Color.BLACK
|
||||||
|
properties.setProperty(KAPTCHA_BORDER_COLOR, "105,179,90");
|
||||||
|
// 验证码文本字符颜色 默认为Color.BLACK
|
||||||
|
properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "blue");
|
||||||
|
// 验证码图片宽度 默认为200
|
||||||
|
properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160");
|
||||||
|
// 验证码图片高度 默认为50
|
||||||
|
properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60");
|
||||||
|
// 验证码文本字符大小 默认为40
|
||||||
|
properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "35");
|
||||||
|
// KAPTCHA_SESSION_KEY
|
||||||
|
properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCodeMath");
|
||||||
|
// 验证码文本生成器
|
||||||
|
properties.setProperty(KAPTCHA_TEXTPRODUCER_IMPL, "com.ruoyi.web.admin.config.KaptchaTextCreator");
|
||||||
|
// 验证码文本字符间距 默认为2
|
||||||
|
properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_SPACE, "3");
|
||||||
|
// 验证码文本字符长度 默认为5
|
||||||
|
properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "6");
|
||||||
|
// 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
|
||||||
|
properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier");
|
||||||
|
// 验证码噪点颜色 默认为Color.BLACK
|
||||||
|
properties.setProperty(KAPTCHA_NOISE_COLOR, "white");
|
||||||
|
// 干扰实现类
|
||||||
|
properties.setProperty(KAPTCHA_NOISE_IMPL, "com.google.code.kaptcha.impl.NoNoise");
|
||||||
|
// 图片样式 水纹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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
package com.ruoyi.web.admin.config;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletRequestWrapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author 1763113879@qq.com
|
||||||
|
* @version V2.1
|
||||||
|
* @since 2.1.0 2023/11/16 13:07
|
||||||
|
*/
|
||||||
|
public class CustomHttpServletRequest extends HttpServletRequestWrapper {
|
||||||
|
|
||||||
|
private Map<String,String> headers=new HashMap<>();
|
||||||
|
|
||||||
|
public CustomHttpServletRequest(HttpServletRequest request){
|
||||||
|
super(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addHeader(String name,String value){
|
||||||
|
headers.put(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getHeader(String name) {
|
||||||
|
String value=super.getHeader(name);
|
||||||
|
|
||||||
|
if (headers.containsKey(name)){
|
||||||
|
value=headers.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Enumeration<String> getHeaderNames() {
|
||||||
|
List<String> names= Collections.list(super.getHeaderNames());
|
||||||
|
names.addAll(headers.keySet());
|
||||||
|
|
||||||
|
return Collections.enumeration(names);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Enumeration<String> getHeaders(String name) {
|
||||||
|
List<String> list= Collections.list(super.getHeaders(name));
|
||||||
|
|
||||||
|
if (headers.containsKey(name)){
|
||||||
|
list.add(headers.get(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Collections.enumeration(list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
package com.ruoyi.web.admin.config;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import com.google.code.kaptcha.text.impl.DefaultTextCreator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码文本生成器
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class KaptchaTextCreator extends DefaultTextCreator
|
||||||
|
{
|
||||||
|
private static final String[] CNUMBERS = "0,1,2,3,4,5,6,7,8,9,10".split(",");
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getText()
|
||||||
|
{
|
||||||
|
Integer result = 0;
|
||||||
|
Random random = new Random();
|
||||||
|
int x = random.nextInt(10);
|
||||||
|
int y = random.nextInt(10);
|
||||||
|
StringBuilder suChinese = new StringBuilder();
|
||||||
|
int randomoperands = random.nextInt(3);
|
||||||
|
if (randomoperands == 0)
|
||||||
|
{
|
||||||
|
result = x * y;
|
||||||
|
suChinese.append(CNUMBERS[x]);
|
||||||
|
suChinese.append("*");
|
||||||
|
suChinese.append(CNUMBERS[y]);
|
||||||
|
}
|
||||||
|
else if (randomoperands == 1)
|
||||||
|
{
|
||||||
|
if ((x != 0) && y % x == 0)
|
||||||
|
{
|
||||||
|
result = y / x;
|
||||||
|
suChinese.append(CNUMBERS[y]);
|
||||||
|
suChinese.append("/");
|
||||||
|
suChinese.append(CNUMBERS[x]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = x + y;
|
||||||
|
suChinese.append(CNUMBERS[x]);
|
||||||
|
suChinese.append("+");
|
||||||
|
suChinese.append(CNUMBERS[y]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (randomoperands == 2)
|
||||||
|
{
|
||||||
|
if (x >= y)
|
||||||
|
{
|
||||||
|
result = x - y;
|
||||||
|
suChinese.append(CNUMBERS[x]);
|
||||||
|
suChinese.append("-");
|
||||||
|
suChinese.append(CNUMBERS[y]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = y - x;
|
||||||
|
suChinese.append(CNUMBERS[y]);
|
||||||
|
suChinese.append("-");
|
||||||
|
suChinese.append(CNUMBERS[x]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = x + y;
|
||||||
|
suChinese.append(CNUMBERS[x]);
|
||||||
|
suChinese.append("+");
|
||||||
|
suChinese.append(CNUMBERS[y]);
|
||||||
|
}
|
||||||
|
suChinese.append("=?@" + result);
|
||||||
|
return suChinese.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
package com.ruoyi.web.admin.config.properties;
|
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.cloud.context.config.annotation.RefreshScope;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码配置
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@ConfigurationProperties(prefix = "security.captcha")
|
||||||
|
public class CaptchaProperties
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 验证码开关
|
||||||
|
*/
|
||||||
|
private Boolean enabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码类型(math 数组计算 char 字符)
|
||||||
|
*/
|
||||||
|
private String type;
|
||||||
|
|
||||||
|
public Boolean getEnabled()
|
||||||
|
{
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnabled(Boolean enabled)
|
||||||
|
{
|
||||||
|
this.enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getType()
|
||||||
|
{
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(String type)
|
||||||
|
{
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
package com.ruoyi.web.admin.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
|
||||||
|
@ConfigurationProperties(prefix = "security.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
package com.ruoyi.web.admin.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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* XSS跨站脚本配置
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@ConfigurationProperties(prefix = "security.xss")
|
||||||
|
public class XssProperties
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Xss开关
|
||||||
|
*/
|
||||||
|
private Boolean enabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排除路径
|
||||||
|
*/
|
||||||
|
private List<String> excludeUrls = new ArrayList<>();
|
||||||
|
|
||||||
|
public Boolean getEnabled()
|
||||||
|
{
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnabled(Boolean enabled)
|
||||||
|
{
|
||||||
|
this.enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getExcludeUrls()
|
||||||
|
{
|
||||||
|
return excludeUrls;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExcludeUrls(List<String> excludeUrls)
|
||||||
|
{
|
||||||
|
this.excludeUrls = excludeUrls;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
package com.ruoyi.web.admin.controller;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import com.ruoyi.common.core.web.domain.AjaxResult;
|
||||||
|
import com.ruoyi.web.admin.service.ValidateCodeService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码操作处理
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
public class CaptchaController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ValidateCodeService validateCodeService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成验证码
|
||||||
|
*/
|
||||||
|
@GetMapping("/code")
|
||||||
|
public AjaxResult getCode(HttpServletResponse response) throws IOException {
|
||||||
|
return validateCodeService.createCaptcha();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,98 @@
|
||||||
|
package com.ruoyi.web.admin.filter;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
import javax.servlet.ReadListener;
|
||||||
|
import javax.servlet.ServletInputStream;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import org.springframework.web.util.ContentCachingRequestWrapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author 1763113879@qq.com
|
||||||
|
* @version V2.1
|
||||||
|
* @since 2.1.0 2023/11/16 14:59
|
||||||
|
*/
|
||||||
|
//继承ContentCachingRequestWrapper
|
||||||
|
public class ContentCachingRequestWrapperNew extends ContentCachingRequestWrapper {
|
||||||
|
|
||||||
|
//原子变量,用来区分首次读取还是非首次
|
||||||
|
private AtomicBoolean isFirst = new AtomicBoolean(true);
|
||||||
|
|
||||||
|
public ContentCachingRequestWrapperNew(HttpServletRequest request) {
|
||||||
|
super(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ContentCachingRequestWrapperNew(HttpServletRequest request, int contentCacheLimit) {
|
||||||
|
super(request, contentCacheLimit);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ServletInputStream getInputStream() throws IOException {
|
||||||
|
|
||||||
|
if(isFirst.get()){
|
||||||
|
//首次读取直接调父类的方法,这一次执行完之后 缓存流中有数据了
|
||||||
|
//后续读取就读缓存流里的。
|
||||||
|
isFirst.set(false);
|
||||||
|
return super.getInputStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
//用缓存流构建一个新的输入流
|
||||||
|
return new ServletInputStreamNew(super.getContentAsByteArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
//参考自 DelegatingServletInputStream
|
||||||
|
class ServletInputStreamNew extends ServletInputStream{
|
||||||
|
|
||||||
|
private InputStream sourceStream;
|
||||||
|
|
||||||
|
private boolean finished = false;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public ServletInputStreamNew(byte [] bytes) {
|
||||||
|
//构建一个普通的输入流
|
||||||
|
this.sourceStream = new ByteArrayInputStream(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read() throws IOException {
|
||||||
|
int data = this.sourceStream.read();
|
||||||
|
if (data == -1) {
|
||||||
|
this.finished = true;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int available() throws IOException {
|
||||||
|
return this.sourceStream.available();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
super.close();
|
||||||
|
this.sourceStream.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFinished() {
|
||||||
|
return this.finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isReady() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setReadListener(ReadListener readListener) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,209 @@
|
||||||
|
package com.ruoyi.web.admin.filter;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.servlet.FilterChain;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
|
import org.springframework.core.annotation.Order;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
import org.springframework.web.servlet.HandlerExceptionResolver;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.JSON;
|
||||||
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
|
import com.ruoyi.common.core.constant.CacheConstants;
|
||||||
|
import com.ruoyi.common.core.constant.SecurityConstants;
|
||||||
|
import com.ruoyi.common.core.constant.TokenConstants;
|
||||||
|
import com.ruoyi.common.core.context.SecurityContextHolder;
|
||||||
|
import com.ruoyi.common.core.exception.ServiceException;
|
||||||
|
import com.ruoyi.common.core.utils.JwtUtils;
|
||||||
|
import com.ruoyi.common.core.utils.ServletUtils;
|
||||||
|
import com.ruoyi.common.core.utils.StringUtils;
|
||||||
|
import com.ruoyi.common.redis.service.RedisService;
|
||||||
|
import com.ruoyi.common.security.auth.AuthUtil;
|
||||||
|
import com.ruoyi.common.security.service.TokenService;
|
||||||
|
import com.ruoyi.system.api.model.LoginUser;
|
||||||
|
import com.ruoyi.web.admin.config.CustomHttpServletRequest;
|
||||||
|
import com.ruoyi.web.admin.config.properties.CaptchaProperties;
|
||||||
|
import com.ruoyi.web.admin.config.properties.IgnoreWhiteProperties;
|
||||||
|
import com.ruoyi.web.admin.service.ValidateCodeService;
|
||||||
|
|
||||||
|
import io.jsonwebtoken.Claims;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* token过滤器 验证token有效性
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@Order(-200)
|
||||||
|
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(JwtAuthenticationTokenFilter.class);
|
||||||
|
|
||||||
|
// 排除过滤的 uri 地址,nacos自行添加
|
||||||
|
@Autowired
|
||||||
|
private IgnoreWhiteProperties ignoreWhite;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RedisService redisService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private TokenService tokenService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ValidateCodeService validateCodeService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private CaptchaProperties captchaProperties;
|
||||||
|
|
||||||
|
private static final String CODE = "code";
|
||||||
|
|
||||||
|
private static final String UUID = "uuid";
|
||||||
|
|
||||||
|
private final static String[] VALIDATE_URL = new String[] {"/auth/login", "/auth/register"};
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
@Qualifier("handlerExceptionResolver")
|
||||||
|
private HandlerExceptionResolver resolver;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
|
||||||
|
throws ServletException, IOException {
|
||||||
|
|
||||||
|
String source = request.getHeader(SecurityConstants.FROM_SOURCE);
|
||||||
|
// 内部请求验证 不拦截
|
||||||
|
if (StringUtils.equals(SecurityConstants.INNER, source)) {
|
||||||
|
chain.doFilter(request, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String requestURI = request.getRequestURI();
|
||||||
|
|
||||||
|
// 非登录/注册请求或验证码关闭,不处理
|
||||||
|
if (StringUtils.equalsAnyIgnoreCase(requestURI, VALIDATE_URL) && captchaProperties.getEnabled()) {
|
||||||
|
|
||||||
|
//,HttpServletRequst中的body内容只会读取一次,但是可能某些情境下可能会读取多次,替换request
|
||||||
|
ContentCachingRequestWrapperNew wrappedRequest = new ContentCachingRequestWrapperNew(request);
|
||||||
|
try {
|
||||||
|
String rspStr = resolveBodyFromRequest(wrappedRequest);
|
||||||
|
JSONObject obj = JSON.parseObject(rspStr);
|
||||||
|
validateCodeService.checkCaptcha(obj.getString(CODE), obj.getString(UUID));
|
||||||
|
} catch (Exception e) {
|
||||||
|
// throw new ServiceException(e.getMessage());
|
||||||
|
resolver.resolveException(request, response, null, e);
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
request = wrappedRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 跳过不需要验证的路径
|
||||||
|
if (!StringUtils.matches(requestURI, ignoreWhite.getWhites())) {
|
||||||
|
|
||||||
|
String token = getToken(request);
|
||||||
|
if (StringUtils.isEmpty(token)) {
|
||||||
|
throw new ServiceException("令牌不能为空");
|
||||||
|
|
||||||
|
}
|
||||||
|
Claims claims = JwtUtils.parseToken(token);
|
||||||
|
if (claims == null) {
|
||||||
|
throw new ServiceException("令牌已过期或验证不正确");
|
||||||
|
|
||||||
|
}
|
||||||
|
String userkey = JwtUtils.getUserKey(claims);
|
||||||
|
boolean islogin = redisService.hasKey(getTokenKey(userkey));
|
||||||
|
if (!islogin) {
|
||||||
|
throw new ServiceException("登录状态已过期");
|
||||||
|
}
|
||||||
|
String userid = JwtUtils.getUserId(claims);
|
||||||
|
String username = JwtUtils.getUserName(claims);
|
||||||
|
if (StringUtils.isEmpty(userid) || StringUtils.isEmpty(username)) {
|
||||||
|
throw new ServiceException("令牌验证失败");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
SecurityContextHolder.setUserId(userid);
|
||||||
|
SecurityContextHolder.setUserName(username);
|
||||||
|
SecurityContextHolder.setUserKey(userkey);
|
||||||
|
|
||||||
|
CustomHttpServletRequest customHttpServletRequest = new CustomHttpServletRequest(request);
|
||||||
|
// 设置用户信息到请求
|
||||||
|
addHeader(customHttpServletRequest, SecurityConstants.USER_KEY, userkey);
|
||||||
|
addHeader(customHttpServletRequest, SecurityConstants.DETAILS_USER_ID, userid);
|
||||||
|
addHeader(customHttpServletRequest, SecurityConstants.DETAILS_USERNAME, username);
|
||||||
|
//转换为自定义的request
|
||||||
|
request = customHttpServletRequest;
|
||||||
|
|
||||||
|
if (StringUtils.isNotEmpty(token)) {
|
||||||
|
LoginUser loginUser = AuthUtil.getLoginUser(token);
|
||||||
|
if (StringUtils.isNotNull(loginUser)) {
|
||||||
|
AuthUtil.verifyLoginUserExpire(loginUser);
|
||||||
|
SecurityContextHolder.set(SecurityConstants.LOGIN_USER, loginUser);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
chain.doFilter(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取缓存key
|
||||||
|
*/
|
||||||
|
private String getTokenKey(String token) {
|
||||||
|
return CacheConstants.LOGIN_TOKEN_KEY + token;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取请求token
|
||||||
|
*/
|
||||||
|
private String getToken(HttpServletRequest request) {
|
||||||
|
String token = request.getHeader(TokenConstants.AUTHENTICATION);
|
||||||
|
// 如果前端设置了令牌前缀,则裁剪掉前缀
|
||||||
|
if (StringUtils.isNotEmpty(token) && token.startsWith(TokenConstants.PREFIX)) {
|
||||||
|
token = token.replaceFirst(TokenConstants.PREFIX, StringUtils.EMPTY);
|
||||||
|
}
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addHeader(CustomHttpServletRequest customHttpServletRequest, String name, Object value) {
|
||||||
|
if (value == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String valueStr = value.toString();
|
||||||
|
String valueEncode = ServletUtils.urlEncode(valueStr);
|
||||||
|
customHttpServletRequest.addHeader(name, valueEncode);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String resolveBodyFromRequest(HttpServletRequest request) throws IOException {
|
||||||
|
|
||||||
|
// 直接从HttpServletRequest的Reader流中获取RequestBody
|
||||||
|
BufferedReader reader = request.getReader();
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
String line = reader.readLine();
|
||||||
|
while (line != null) {
|
||||||
|
builder.append(line);
|
||||||
|
line = reader.readLine();
|
||||||
|
}
|
||||||
|
reader.close();
|
||||||
|
String reqBody = builder.toString();
|
||||||
|
return reqBody;
|
||||||
|
|
||||||
|
// String requestBody = new String(request.getContentAsByteArray());
|
||||||
|
// return requestBody;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
package com.ruoyi.web.admin.handler;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
|
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||||
|
|
||||||
|
import com.ruoyi.common.core.exception.CaptchaException;
|
||||||
|
import com.ruoyi.common.core.web.domain.AjaxResult;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全局异常处理器
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
@RestControllerAdvice
|
||||||
|
public class GlobalAdminWebExceptionHandler
|
||||||
|
{
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(GlobalAdminWebExceptionHandler.class);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码异常
|
||||||
|
*/
|
||||||
|
@ExceptionHandler(CaptchaException.class)
|
||||||
|
public AjaxResult handleCaptchaException(CaptchaException e, HttpServletRequest request)
|
||||||
|
{
|
||||||
|
String requestURI = request.getRequestURI();
|
||||||
|
log.error("请求地址'{}'", requestURI, e.getMessage());
|
||||||
|
return AjaxResult.error(e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
package com.ruoyi.web.admin.service;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import com.ruoyi.common.core.exception.CaptchaException;
|
||||||
|
import com.ruoyi.common.core.web.domain.AjaxResult;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码处理
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public interface ValidateCodeService
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 生成验证码
|
||||||
|
*/
|
||||||
|
public AjaxResult createCaptcha() throws IOException, CaptchaException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验验证码
|
||||||
|
*/
|
||||||
|
public void checkCaptcha(String key, String value) throws CaptchaException;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,122 @@
|
||||||
|
package com.ruoyi.web.admin.service.impl;
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.FastByteArrayOutputStream;
|
||||||
|
|
||||||
|
import com.google.code.kaptcha.Producer;
|
||||||
|
import com.ruoyi.common.core.constant.CacheConstants;
|
||||||
|
import com.ruoyi.common.core.constant.Constants;
|
||||||
|
import com.ruoyi.common.core.exception.CaptchaException;
|
||||||
|
import com.ruoyi.common.core.utils.StringUtils;
|
||||||
|
import com.ruoyi.common.core.utils.sign.Base64;
|
||||||
|
import com.ruoyi.common.core.utils.uuid.IdUtils;
|
||||||
|
import com.ruoyi.common.core.web.domain.AjaxResult;
|
||||||
|
import com.ruoyi.common.redis.service.RedisService;
|
||||||
|
import com.ruoyi.web.admin.config.properties.CaptchaProperties;
|
||||||
|
import com.ruoyi.web.admin.service.ValidateCodeService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码实现处理
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class ValidateCodeServiceImpl implements ValidateCodeService
|
||||||
|
{
|
||||||
|
@Resource(name = "captchaProducer")
|
||||||
|
private Producer captchaProducer;
|
||||||
|
|
||||||
|
@Resource(name = "captchaProducerMath")
|
||||||
|
private Producer captchaProducerMath;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RedisService redisService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private CaptchaProperties captchaProperties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成验证码
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public AjaxResult createCaptcha() throws IOException, CaptchaException
|
||||||
|
{
|
||||||
|
AjaxResult ajax = AjaxResult.success();
|
||||||
|
boolean captchaEnabled = captchaProperties.getEnabled();
|
||||||
|
ajax.put("captchaEnabled", captchaEnabled);
|
||||||
|
if (!captchaEnabled)
|
||||||
|
{
|
||||||
|
return ajax;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存验证码信息
|
||||||
|
String uuid = IdUtils.simpleUUID();
|
||||||
|
String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid;
|
||||||
|
|
||||||
|
String capStr = null, code = null;
|
||||||
|
BufferedImage image = null;
|
||||||
|
|
||||||
|
String captchaType = captchaProperties.getType();
|
||||||
|
// 生成验证码
|
||||||
|
if ("math".equals(captchaType))
|
||||||
|
{
|
||||||
|
String capText = captchaProducerMath.createText();
|
||||||
|
capStr = capText.substring(0, capText.lastIndexOf("@"));
|
||||||
|
code = capText.substring(capText.lastIndexOf("@") + 1);
|
||||||
|
image = captchaProducerMath.createImage(capStr);
|
||||||
|
}
|
||||||
|
else if ("char".equals(captchaType))
|
||||||
|
{
|
||||||
|
capStr = code = captchaProducer.createText();
|
||||||
|
image = captchaProducer.createImage(capStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
redisService.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);
|
||||||
|
// 转换流信息写出
|
||||||
|
FastByteArrayOutputStream os = new FastByteArrayOutputStream();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ImageIO.write(image, "jpg", os);
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
return AjaxResult.error(e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
ajax.put("uuid", uuid);
|
||||||
|
ajax.put("img", Base64.encode(os.toByteArray()));
|
||||||
|
return ajax;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验验证码
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void checkCaptcha(String code, String uuid) throws CaptchaException
|
||||||
|
{
|
||||||
|
if (StringUtils.isEmpty(code))
|
||||||
|
{
|
||||||
|
throw new CaptchaException("验证码不能为空");
|
||||||
|
}
|
||||||
|
if (StringUtils.isEmpty(uuid))
|
||||||
|
{
|
||||||
|
throw new CaptchaException("验证码已失效");
|
||||||
|
}
|
||||||
|
String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid;
|
||||||
|
String captcha = redisService.getCacheObject(verifyKey);
|
||||||
|
redisService.deleteObject(verifyKey);
|
||||||
|
|
||||||
|
if (!code.equalsIgnoreCase(captcha))
|
||||||
|
{
|
||||||
|
throw new CaptchaException("验证码错误");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,243 @@
|
||||||
|
# 项目相关配置
|
||||||
|
ruoyi:
|
||||||
|
# 名称
|
||||||
|
name: ruoyi-web-admin
|
||||||
|
# 版本
|
||||||
|
version: 3.8.6
|
||||||
|
# 版权年份
|
||||||
|
copyrightYear: 2023
|
||||||
|
# 实例演示开关
|
||||||
|
demoEnabled: true
|
||||||
|
# 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
|
||||||
|
profile: ruoyi/uploadPath
|
||||||
|
# 获取ip地址开关
|
||||||
|
addressEnabled: false
|
||||||
|
# 验证码类型 math 数字计算 char 字符验证
|
||||||
|
captchaType: math
|
||||||
|
|
||||||
|
# 开发环境配置
|
||||||
|
server:
|
||||||
|
# 服务器的HTTP端口,默认为8080
|
||||||
|
port: 8080
|
||||||
|
servlet:
|
||||||
|
# 应用的访问路径
|
||||||
|
context-path: /
|
||||||
|
tomcat:
|
||||||
|
# tomcat的URI编码
|
||||||
|
uri-encoding: UTF-8
|
||||||
|
# 连接数满后的排队数,默认为100
|
||||||
|
accept-count: 1000
|
||||||
|
threads:
|
||||||
|
# tomcat最大线程数,默认为200
|
||||||
|
max: 800
|
||||||
|
# Tomcat启动初始化的线程数,默认值10
|
||||||
|
min-spare: 100
|
||||||
|
|
||||||
|
# 日志配置
|
||||||
|
logging:
|
||||||
|
level:
|
||||||
|
com.ruoyi: debug
|
||||||
|
org.springframework: warn
|
||||||
|
|
||||||
|
# 用户配置
|
||||||
|
user:
|
||||||
|
password:
|
||||||
|
# 密码最大错误次数
|
||||||
|
maxRetryCount: 5
|
||||||
|
# 密码锁定时间(默认10分钟)
|
||||||
|
lockTime: 10
|
||||||
|
|
||||||
|
# Spring配置
|
||||||
|
spring:
|
||||||
|
# 资源信息
|
||||||
|
messages:
|
||||||
|
# 国际化资源文件路径
|
||||||
|
basename: i18n/messages
|
||||||
|
# 文件上传
|
||||||
|
servlet:
|
||||||
|
multipart:
|
||||||
|
# 单个文件大小
|
||||||
|
max-file-size: 10MB
|
||||||
|
# 设置总上传的文件大小
|
||||||
|
max-request-size: 20MB
|
||||||
|
# 服务模块
|
||||||
|
devtools:
|
||||||
|
restart:
|
||||||
|
# 热部署开关
|
||||||
|
enabled: true
|
||||||
|
datasource:
|
||||||
|
druid:
|
||||||
|
stat-view-servlet:
|
||||||
|
enabled: true
|
||||||
|
loginUsername: admin
|
||||||
|
loginPassword: 123456
|
||||||
|
dynamic:
|
||||||
|
druid:
|
||||||
|
initial-size: 5
|
||||||
|
min-idle: 5
|
||||||
|
maxActive: 20
|
||||||
|
maxWait: 60000
|
||||||
|
timeBetweenEvictionRunsMillis: 60000
|
||||||
|
minEvictableIdleTimeMillis: 300000
|
||||||
|
validationQuery: SELECT 1 FROM DUAL
|
||||||
|
testWhileIdle: true
|
||||||
|
testOnBorrow: false
|
||||||
|
testOnReturn: false
|
||||||
|
poolPreparedStatements: true
|
||||||
|
maxPoolPreparedStatementPerConnectionSize: 20
|
||||||
|
filters: stat,slf4j
|
||||||
|
connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000
|
||||||
|
datasource:
|
||||||
|
# 主库数据源
|
||||||
|
master:
|
||||||
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
|
url: jdbc:mysql://localhost:3306/ry-config?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
||||||
|
username: root
|
||||||
|
password: chenroot
|
||||||
|
# 备库数据源
|
||||||
|
slave:
|
||||||
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
|
url: jdbc:mysql://localhost:3306/ry-config?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
||||||
|
username: root
|
||||||
|
password: chenroot
|
||||||
|
# redis 配置
|
||||||
|
redis:
|
||||||
|
# 地址
|
||||||
|
host: localhost
|
||||||
|
# 端口,默认为6379
|
||||||
|
port: 6379
|
||||||
|
# 数据库索引
|
||||||
|
database: 0
|
||||||
|
# 密码
|
||||||
|
password:
|
||||||
|
# 连接超时时间
|
||||||
|
timeout: 10s
|
||||||
|
lettuce:
|
||||||
|
pool:
|
||||||
|
# 连接池中的最小空闲连接
|
||||||
|
min-idle: 0
|
||||||
|
# 连接池中的最大空闲连接
|
||||||
|
max-idle: 8
|
||||||
|
# 连接池的最大数据库连接数
|
||||||
|
max-active: 8
|
||||||
|
# #连接池最大阻塞等待时间(使用负值表示没有限制)
|
||||||
|
max-wait: -1ms
|
||||||
|
#禁用nacos,yml文件
|
||||||
|
cloud:
|
||||||
|
nacos:
|
||||||
|
config:
|
||||||
|
enabled: false
|
||||||
|
refresh-enabled: false
|
||||||
|
discovery:
|
||||||
|
enabled: false
|
||||||
|
instance-enabled: false
|
||||||
|
|
||||||
|
main:
|
||||||
|
allow-bean-definition-overriding: true
|
||||||
|
# token配置
|
||||||
|
token:
|
||||||
|
# 令牌自定义标识
|
||||||
|
header: Authorization
|
||||||
|
# 令牌密钥
|
||||||
|
secret: abcdefghijklmnopqrstuvwxyz
|
||||||
|
# 令牌有效期(默认30分钟)
|
||||||
|
expireTime: 30
|
||||||
|
|
||||||
|
# MyBatis配置
|
||||||
|
mybatis:
|
||||||
|
# 搜索指定包别名
|
||||||
|
typeAliasesPackage: com.ruoyi.**.domain
|
||||||
|
# 配置mapper的扫描,找到所有的mapper.xml映射文件
|
||||||
|
mapperLocations: classpath*:mapper/**/*Mapper.xml
|
||||||
|
# 加载全局的配置文件
|
||||||
|
#configLocation: classpath:mybatis/mybatis-config.xml
|
||||||
|
# PageHelper分页插件
|
||||||
|
pagehelper:
|
||||||
|
helperDialect: mysql
|
||||||
|
supportMethodsArguments: true
|
||||||
|
params: count=countSql
|
||||||
|
# 防止XSS攻击
|
||||||
|
xss:
|
||||||
|
# 过滤开关
|
||||||
|
enabled: true
|
||||||
|
# 排除链接(多个用逗号分隔)
|
||||||
|
excludes: /system/notice
|
||||||
|
# 匹配链接
|
||||||
|
urlPatterns: /system/*,/monitor/*,/tool/*
|
||||||
|
##################### 文件模块 ###########################################
|
||||||
|
# 本地文件上传
|
||||||
|
file:
|
||||||
|
domain: http://127.0.0.1:9300
|
||||||
|
path: ruoyi/uploadPath
|
||||||
|
prefix: /statics
|
||||||
|
# FastDFS配置
|
||||||
|
fdfs:
|
||||||
|
domain: http://8.129.231.12
|
||||||
|
soTimeout: 3000
|
||||||
|
connectTimeout: 2000
|
||||||
|
trackerList: 8.129.231.12:22122
|
||||||
|
# Minio配置
|
||||||
|
minio:
|
||||||
|
url: http://8.129.231.12:9000
|
||||||
|
accessKey: minioadmin
|
||||||
|
secretKey: minioadmin
|
||||||
|
bucketName: test
|
||||||
|
######################## 文件模块 ########################################
|
||||||
|
# feign 配置
|
||||||
|
feign:
|
||||||
|
sentinel:
|
||||||
|
enabled: true
|
||||||
|
okhttp:
|
||||||
|
enabled: true
|
||||||
|
httpclient:
|
||||||
|
enabled: false
|
||||||
|
client:
|
||||||
|
config:
|
||||||
|
default:
|
||||||
|
connectTimeout: 10000
|
||||||
|
readTimeout: 10000
|
||||||
|
compression:
|
||||||
|
request:
|
||||||
|
enabled: true
|
||||||
|
response:
|
||||||
|
enabled: true
|
||||||
|
# 设置feign本地调用地址
|
||||||
|
debug:
|
||||||
|
url:
|
||||||
|
system: http://localhost:${server.port}/system
|
||||||
|
file: http://localhost:${server.port}/file
|
||||||
|
# 暴露监控端点
|
||||||
|
management:
|
||||||
|
endpoints:
|
||||||
|
web:
|
||||||
|
exposure:
|
||||||
|
include: '*'
|
||||||
|
# 安全配置
|
||||||
|
security:
|
||||||
|
# 验证码
|
||||||
|
captcha:
|
||||||
|
enabled: true
|
||||||
|
type: char
|
||||||
|
# 防止XSS攻击
|
||||||
|
xss:
|
||||||
|
enabled: true
|
||||||
|
excludeUrls:
|
||||||
|
- /system/notice
|
||||||
|
# 不校验白名单
|
||||||
|
ignore:
|
||||||
|
whites:
|
||||||
|
- /auth/logout
|
||||||
|
- /auth/login
|
||||||
|
- /auth/register
|
||||||
|
- /*/v2/api-docs
|
||||||
|
- /csrf
|
||||||
|
- /code
|
||||||
|
# Swagger配置
|
||||||
|
swagger:
|
||||||
|
# 是否开启swagger
|
||||||
|
enabled: true
|
||||||
|
# 请求前缀
|
||||||
|
pathMapping: /dev-api
|
||||||
|
title: 系统模块接口文档
|
||||||
|
license: Powered By ruoyi
|
||||||
|
licenseUrl: https://ruoyi.vip
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
Spring Boot Version: ${spring-boot.version}
|
||||||
|
Spring Application Name: ${spring.application.name}
|
||||||
|
_ _
|
||||||
|
(_) | |
|
||||||
|
_ __ _ _ ___ _ _ _ ______ ___ _ _ ___ | |_ ___ _ __ ___
|
||||||
|
| '__|| | | | / _ \ | | | || ||______|/ __|| | | |/ __|| __| / _ \| '_ ` _ \
|
||||||
|
| | | |_| || (_) || |_| || | \__ \| |_| |\__ \| |_ | __/| | | | | |
|
||||||
|
|_| \__,_| \___/ \__, ||_| |___/ \__, ||___/ \__| \___||_| |_| |_|
|
||||||
|
__/ | __/ |
|
||||||
|
|___/ |___/
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
# Tomcat
|
||||||
|
server:
|
||||||
|
port: 8080
|
||||||
|
|
||||||
|
# Spring
|
||||||
|
spring:
|
||||||
|
application:
|
||||||
|
# 应用名称
|
||||||
|
name: ruoyi-web-admin
|
||||||
|
profiles:
|
||||||
|
# 环境配置
|
||||||
|
active: local
|
||||||
|
|
||||||
|
# Spring Web 公共配置
|
||||||
|
autoconfigure:
|
||||||
|
exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure
|
||||||
|
mvc:
|
||||||
|
pathmatch:
|
||||||
|
matching-strategy: ant_path_matcher
|
||||||
|
cloud:
|
||||||
|
gateway:
|
||||||
|
discovery:
|
||||||
|
locator:
|
||||||
|
lowerCaseServiceId: true
|
||||||
|
enabled: true
|
||||||
|
discovery:
|
||||||
|
client:
|
||||||
|
simple:
|
||||||
|
instances:
|
||||||
|
ruoyi-file:
|
||||||
|
- uri: http://localhost:8080
|
||||||
|
ruoyi-system:
|
||||||
|
- uri: http://localhost:9090
|
||||||
|
|
||||||
|
|
@ -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-web-admin" />
|
||||||
|
<!-- 日志输出格式 -->
|
||||||
|
<property name="log.pattern" value="%d{yyyy-MM-dd 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="DEBUG">
|
||||||
|
<appender-ref ref="file_info" />
|
||||||
|
<appender-ref ref="file_error" />
|
||||||
|
</root>
|
||||||
|
</configuration>
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<!DOCTYPE configuration
|
||||||
|
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
|
||||||
|
"http://mybatis.org/dtd/mybatis-3-config.dtd">
|
||||||
|
<configuration>
|
||||||
|
<!-- 全局参数 -->
|
||||||
|
<settings>
|
||||||
|
<!-- 使全局的映射器启用或禁用缓存 -->
|
||||||
|
<setting name="cacheEnabled" value="true" />
|
||||||
|
<!-- 允许JDBC 支持自动生成主键 -->
|
||||||
|
<setting name="useGeneratedKeys" value="true" />
|
||||||
|
<!-- 配置默认的执行器.SIMPLE就是普通执行器;REUSE执行器会重用预处理语句(prepared statements);BATCH执行器将重用语句并执行批量更新 -->
|
||||||
|
<setting name="defaultExecutorType" value="SIMPLE" />
|
||||||
|
<!-- 指定 MyBatis 所用日志的具体实现 -->
|
||||||
|
<setting name="logImpl" value="SLF4J" />
|
||||||
|
<!-- 使用驼峰命名法转换字段 -->
|
||||||
|
<!-- <setting name="mapUnderscoreToCamelCase" value="true"/> -->
|
||||||
|
</settings>
|
||||||
|
|
||||||
|
</configuration>
|
||||||
Loading…
Reference in New Issue