1、框架介绍
RuoYi是一个基于Java技术开发的后台管理系统,目前官方同步在维护的有3个版本。
开源协议:MIT
解释:MIT是和BSD一样宽松的许可协议,作者只想保留版权,而无任何其他了限制.也就是说,你必须在你的发行版里包含原许可协议的声明,无论你是以二进制发布的还是以源代码发布的。
(1)若依不分离版本
RuoYi是基于经典技术组合(Spring Boot、Apache Shiro、MyBatis、Thymeleaf)主要目的让开发者注重专注业务,降低技术难度,从而节省人力成本,缩短项目周期,提高软件安全质量。
Gitee开源地址:RuoYi: 基于SpringBoot的权限管理系统 易读易懂、界面简洁美观。 核心技术采用Spring、MyBatis、Shiro没有任何其它重度依赖。直接运行即可用
在线文档地址:http://doc.ruoyi.vip/ruoyi/
在线演示地址:登录若依系统
(2)若依前后端分离版本
RuoYi-Vue是一个 Java EE 企业级快速开发平台,基于经典技术组合(Spring Boot、Spring Security、MyBatis、Jwt、Vue),内置模块如:部门管理、角色用户、菜单及按钮授权、数据权限、系统参数、日志管理、代码生成等。在线定时任务配置;支持集群,支持多数据源,支持分布式事务。
Gitee开源地址:RuoYi-Vue: 基于SpringBoot,Spring Security,JWT,Vue & Element 的前后端分离权限管理系统
在线文档地址:介绍 | RuoYi
在线演示地址:若依管理系统
com.ruoyi ├── common // 工具类 │ └── annotation // 自定义注解 │ └── config // 全局配置 │ └── constant // 通用常量 │ └── core // 核心控制 │ └── enums // 通用枚举 │ └── exception // 通用异常 │ └── json // JSON数据处理 │ └── utils // 通用类处理 │ └── xss // XSS过滤处理 ├── framework // 框架核心 │ └── aspectj // 注解实现 │ └── config // 系统配置 │ └── datasource // 数据权限 │ └── interceptor // 拦截器 │ └── manager // 异步处理 │ └── shiro // 权限控制 │ └── web // 前端控制 ├── ruoyi-generator // 代码生成(不用可移除) ├── ruoyi-quartz // 定时任务(不用可移除) ├── ruoyi-system // 系统代码 ├── ruoyi-admin // 后台服务 ├── ruoyi-xxxxxx // 其他模块
(3)若依微服务版本(在用)
RuoYi-Cloud 是一个 Java EE 分布式微服务架构平台,基于经典技术组合(Spring Boot、Spring Cloud & Alibaba、Vue、Element),内置模块如:部门管理、角色用户、菜单及按钮授权、数据权限、系统参数、日志管理、代码生成等。在线定时任务配置;支持集群,支持多数据源。
Gitee开源地址:RuoYi-Cloud: 基于Spring Boot、Spring Cloud & Alibaba的分布式微服务架构权限管理系统
在线文档地址:介绍 | RuoYi
在线演示地址:与前后端分离在线演示地址一样
若依总结:
pojo层:全称为(Plain Ordinary Java Object 普通Java对象) 存放普通的JavaBean类
Model层:它的JavaBean类,封装的是前端在数据库中需要使用的数据。
知识点2:
若依的前后端未分离版本和vue版本都是基于SpringBoot开发的轻量级Java快速开发框架,他们分模块开发,在我理解其实也是把其他的模块引入主模块使用,保证大家可以分模块开发。和引入jar包是一样的。
知识点3:
若依的@excel注解是为了实现导出excel表。
知识点4:
若依cloud中各个微服务的作用:
com.ruoyi
├── ruoyi-ui // 前端框架 [80]
├── ruoyi-gateway // 网关模块 [8080]
├── ruoyi-auth // 认证中心 [9200] // 独立的认认证授权中心,有启动类
├── ruoyi-api // 接口模块
│ └── ruoyi-api-system // 系统接口 系统抽取的统一的接口api,没有启动类
├── ruoyi-common // 抽离的通用模块工具,没有启动类
│ └── ruoyi-common-core // 核心模块
│ └── ruoyi-common-datascope // 权限范围
│ └── ruoyi-common-datasource // 多数据源
│ └── ruoyi-common-log // 日志记录
│ └── ruoyi-common-redis // 缓存服务
│ └── 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 // 公共依赖
知识点5:用到了匿名内部类写法
/**
* 文件服务降级处理
*
* @author ruoyi
*/
@Component
public class RemoteFileFallbackFactory implements FallbackFactory
{
private static final Logger log = LoggerFactory.getLogger(RemoteFileFallbackFactory.class);
@Override
public RemoteFileService create(Throwable throwable)
{
log.error("文件服务调用失败:{}", throwable.getMessage());
return new RemoteFileService()
{
@Override
public R upload(MultipartFile file)
{
return R.fail("上传文件失败:" + throwable.getMessage());
}
};
}
}
知识点6:微服务若依环境搭建(重点)
若依微服务版手把手教你本地搭建环境并运行前后端项目 - 霸道流氓 - 博客园
知识点7:nacos.config是nacos的初始化数据库,ry-cloud是业务库,ry-config是若依的nacos库。ry_20200924.sql和quartz.sql是ry-cloud的sql,ry_config_20200924.sql是ry-config的sql
知识点7:若依aop理解,冰山一角
深入分析若依数据权限@datascope (注解+AOP+动态sql拼接) 【循序渐进,附分析过程】_金发罗婕的博客-CSDN博客_若依数据权限
知识点8:若依异常理解,见自定义异常和全局异常处理类博客
知识点9:为什么接口返回类型有的用R 有的用AjaxResult?
序列化方式不同,AjaxResult
继承了HashMap HashMap虽然也实现了Serializable
但是有些秘密在里面 有兴趣可以了解下,也可以去了解下 不同序列化
方式的利弊~
知识点9:若依通过自定义注解@innerAuth+AOP来拒绝外部请求
若依框架解读(微服务版)——2.模块间的调用逻辑(ruoyi-api模块)(OpenFeign)_初见qwer的博客-CSDN博客_若依微服务框架原理
知识点10:(重点)若依权限注解:注解类RequiresLogin、RequiresPermissions、RequiresRoles,分别用于登录认证、权限认证和角色认证
切面类PreAuthorizeAspect,基于 Spring Aop 的注解鉴权
被代理类SysJobController,被代理类就是添加注解的方法所在的类,可以是任意一个类
若依权限校验源码分析_紫荆之后-的博客-CSDN博客_若依权限验证
知识点11:若依拦截器过滤器使用
若依登陆过程及过滤器拦截器的使用:
用户登陆接口:1、把用户信息通过uuid即token作为key,存储在缓存中,并设置过期时间,2、通过jwt存储token,userId,userName在map中,设置过期时间,通过jwt创建一个编码后的access_token和expireTime,并返回token在前端
过滤器:用户前端传递的access_token通过Jwt解析,尝试获取userkey(即token),userid,username
并判断是否过期,为空等。如果异常即刻报错,否则将解析的userkey,userid,username纳入请求头中,请求接着交给拦截器。
拦截器:拦截器从请求头中获取userkey,userid,username,通过userkey从缓存中获取用户信息,并刷新缓存中用户信息的过期时间。并将用户信息加入到本地的TransmittableThreadLocal
知识点12:幂等性 _ 防重复提交(若依前后端分离版中的代码)
幂等性原本是数学上的概念,用在接口上就可以理解为:同一个接口,多次发出同一个请求,必须保证操作只执行一次。 调用接口发生异常并且重复尝试时,总是会造成系统所无法承受的损失,所以必须阻止这种现象的发生。
比如下面这些情况,如果没有实现接口幂等性会有很严重的后果: 支付接口,重复支付会导致多次扣钱 ;订单接口,同一个订单可能会多次创建。
gitee:RuoYi-Vue: 基于SpringBoot,Spring Security,JWT,Vue & Element 的前后端分离权限管理系统,同时提供了 Vue3 的版本
使用拦截器实现,在接口方法上添加@RepeatSubmit
注解,注解参数说明:
参数 | 类型 | 默认值 | 描述 |
---|---|---|---|
interval | int | 5000 | 间隔时间(ms),小于此时间视为重复提交 |
message | String | 不允许重复提交,请稍后再试 | 提示消息 |
使用redis缓存接口的请求URL及参数信息,如果存在且一致并在额定时间范围内,不让重复提交。
package com.byd.rental.common.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义注解防止表单重复提交
*
* @author ruoyi
*
*/
@Inherited
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RepeatSubmit
{
/**
* 间隔时间(ms),小于此时间视为重复提交
*/
public int interval() default 5000;
/**
* 提示消息
*/
public String message() default "不允许重复提交,请稍候再试";
}
package com.byd.rental.framework.interceptor;
import java.lang.reflect.Method;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.byd.rental.common.core.domain.AjaxResult;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import com.alibaba.fastjson2.JSON;
import com.byd.rental.common.annotation.RepeatSubmit;
import com.byd.rental.common.utils.ServletUtils;
/**
* 防止重复提交拦截器
*
* @author ruoyi
*/
@Component
public abstract class RepeatSubmitInterceptor implements HandlerInterceptor
{
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
{
if (handler instanceof HandlerMethod)
{
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class);
if (annotation != null)
{
if (this.isRepeatSubmit(request, annotation))
{
AjaxResult ajaxResult = AjaxResult.error(annotation.message());
ServletUtils.renderString(response, JSON.toJSONString(ajaxResult));
return false;
}
}
return true;
}
else
{
return true;
}
}
/**
* 验证是否重复提交由子类实现具体的防重复提交的规则
*
* @param request
* @return
* @throws Exception
*/
public abstract boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation);
}
;