若依开源框架解析

 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/

在线演示地址:登录若依系统

若依开源框架解析_第1张图片

(2)若依前后端分离版本

        RuoYi-Vue是一个 Java EE 企业级快速开发平台,基于经典技术组合(Spring Boot、Spring Security、MyBatis、Jwt、Vue),内置模块如:部门管理、角色用户、菜单及按钮授权、数据权限、系统参数、日志管理、代码生成等。在线定时任务配置;支持集群,支持多数据源,支持分布式事务。

Gitee开源地址:RuoYi-Vue: 基于SpringBoot,Spring Security,JWT,Vue & Element 的前后端分离权限管理系统

在线文档地址:介绍 | RuoYi

在线演示地址:若依管理系统

若依开源框架解析_第2张图片 

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

在线演示地址:与前后端分离在线演示地址一样

若依开源框架解析_第3张图片 

若依总结:
知识点1:
domain层:它的JavaBean类,必须对应数据库中的数据。抽象的对象

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);
}

package com.byd.rental.framework.interceptor.impl;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.servlet.http.HttpServletRequest;

import com.byd.rental.common.core.redis.RedisCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson2.JSON;
import com.byd.rental.common.annotation.RepeatSubmit;
import com.byd.rental.common.constant.CacheConstants;
import com.byd.rental.common.filter.RepeatedlyRequestWrapper;
import com.byd.rental.common.utils.StringUtils;
import com.byd.rental.common.utils.http.HttpHelper;
import com.byd.rental.framework.interceptor.RepeatSubmitInterceptor;

/**
 * 判断请求url和数据是否和上一次相同,
 * 如果和上次相同,则是重复提交表单。 有效时间为10秒内。
 * 
 * @author ruoyi
 */
@Component
public class SameUrlDataInterceptor extends RepeatSubmitInterceptor
{
    public final String REPEAT_PARAMS = "repeatParams";

    public final String REPEAT_TIME = "repeatTime";

    // 令牌自定义标识
    @Value("${token.header}")
    private String header;

    @Autowired
    private RedisCache redisCache;

    @SuppressWarnings("unchecked")
    @Override
    public boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation)
    {
        String nowParams = "";
        if (request instanceof RepeatedlyRequestWrapper)
        {
            RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request;
            nowParams = HttpHelper.getBodyString(repeatedlyRequest);
        }

        // body参数为空,获取Parameter的数据
        if (StringUtils.isEmpty(nowParams))
        {
            nowParams = JSON.toJSONString(request.getParameterMap());
        }
        Map nowDataMap = new HashMap();
        nowDataMap.put(REPEAT_PARAMS, nowParams);
        nowDataMap.put(REPEAT_TIME, System.currentTimeMillis());

        // 请求地址(作为存放cache的key值)
        String url = request.getRequestURI();

        // 唯一值(没有消息头则使用请求地址)
        String submitKey = StringUtils.trimToEmpty(request.getHeader(header));

        // 唯一标识(指定key + url + 消息头)
        String cacheRepeatKey = CacheConstants.REPEAT_SUBMIT_KEY + url + submitKey;

        Object sessionObj = redisCache.getCacheObject(cacheRepeatKey);
        if (sessionObj != null)
        {
            Map sessionMap = (Map) sessionObj;
            if (sessionMap.containsKey(url))
            {
                Map preDataMap = (Map) sessionMap.get(url);
                if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap, annotation.interval()))
                {
                    return true;
                }
            }
        }
        Map cacheMap = new HashMap();
        cacheMap.put(url, nowDataMap);
        redisCache.setCacheObject(cacheRepeatKey, cacheMap, annotation.interval(), TimeUnit.MILLISECONDS);
        return false;
    }

    /**
     * 判断参数是否相同
     */
    private boolean compareParams(Map nowMap, Map preMap)
    {
        String nowParams = (String) nowMap.get(REPEAT_PARAMS);
        String preParams = (String) preMap.get(REPEAT_PARAMS);
        return nowParams.equals(preParams);
    }

    /**
     * 判断两次间隔时间
     */
    private boolean compareTime(Map nowMap, Map preMap, int interval)
    {
        long time1 = (Long) nowMap.get(REPEAT_TIME);
        long time2 = (Long) preMap.get(REPEAT_TIME);
        if ((time1 - time2) < interval)
        {
            return true;
        }
        return false;
    }
}

知识点13:菜单部门接口使用递归返回树结构

03【若依框架解读】Tree树形结构的控制(菜单,部门)_如何在5年薪百万的博客-CSDN博客

知识点14:若依的用户权限模型分析(多对多的都使用中间表存储之间的关联关系)

用户-角色-菜单      多对多对多   

用户-部门              多对一

用户-岗位              多对多

用户-角色-部门     多对多对多

若依系统用户权限模型分析_若依 角色 部门_baobao555#的博客-CSDN博客

知识点15:若依中Sentinel的使用(sentinel中文的含义是哨兵,它是阿里开发的一个限流、降级和熔断的开源框架。作用等同于hystrix,但是sentinel更加强大,功能更加丰富

从ruoyi-gateway中学习sentinel的用法_aofengdaxia的博客-CSDN博客

知识点16:若依监控模块(ruoyi-visual-monitor)

Spring Boot Admin(SBA)是一个针对Spring Boot的Actuator接口进行UI美化封装的监控工具。他可以返回在列表中浏览所有被监控Spring Boot项目的基本信息比如:Spring容器管理的所有的bean、详细的Health信息、内存信息、JVM信息、垃圾回收信息、各种配置信息(比如数据源、缓存列表和命中率)等,Threads 线程管理,Environment 管理等。

服务监控 | RuoYi

若依cloud -【 47 ~ 57】-CSDN博客

若依项目解读(微服务版)——4.监控模块(Springboot Admin)_若依数据监控_初见qwer的博客-CSDN博客

知识点17:若依密码传输

若依cloud采用的前端明文传输密码,后端auth服务进行密码加密,然后调用system服务入库。校验时,通过匹配的方法进行匹配校验。

使用:spring security中的BCryptPasswordEncoder方法进行加密

若依开源框架解析_第4张图片

加密算法---BCryptPasswordEncoder的使用及原理-CSDN博客

另外:手机号的中间4位*表示,使用分词拼接方式。

如果存储的手机号安全要求高,需要在库里面加密存储,换要支持模糊查询,那么可以直接用sql的加密解密函数进行加密解密,进行入库和查询。

如果为了安全考虑,请求中不明文传输密码,则参考:

插件集成 | RuoYi

【精选】若依(RuoYi)SpringBoot框架密码加密传输(前后分离板)_若依密码加密方式_夜の雨的博客-CSDN博客

知识点18:若依插件集成

插件集成 | RuoYi

若依开源框架解析_第5张图片

你可能感兴趣的:(SpringCloud,java,vue.js,spring,boot)