Spring-Cloud-Gateway 整合 Sa-Token 全局过滤器之路由匹配

news/2025/2/12 19:38:51

Spring-Cloud-Gateway 整合 Sa-Token 全局过滤器之路由匹配

Sa-Token 是一个轻量级 Java 权限认证框架,主要解决:登录认证、权限认证、单点登录、OAuth2.0、分布式Session会话、微服务网关鉴权 等一系列权限相关问题。

Sa-Token 旨在以简单、优雅的方式完成系统的权限认证部分。其中在 Springboot/Webflux 中直接对需要鉴权的接口添加注解即可完成鉴权配置,不过当我们在需要全局拦截的情景下,比如网关代理,就必须手动配置需要鉴权的路由了,类似 Spring Filter,但 Sa-Token 将简便得多。

Sa-Token 官方文档上的路由拦截鉴权部分,看似很详细,实则介绍的并不到位,笔者在看了一遍后并不能完全精准的配置路由,于是对 Sa-Token 的路由匹配进行了一系列的实践尝试,得到了一套比较完备的实践方案。

以下基于 SpringCloud Gateway 整合 Sa-Token 时注册的全局过滤器

注册过滤器

首先,需要建立一个全局过滤器,由于 SpringCloud Gateway 是响应式的,所以使用的是 SaReactorFilter。

@Configuration
public class SaTokenConfigure {@ResourceGson gson;// 注册 Sa-Token全局过滤器@Beanpublic SaReactorFilter getSaReactorFilter() {return new SaReactorFilter();}}

拦截范围

这是这个过滤器生效的范围,对于网关应用来说通常就是全部范围

return new SaReactorFilter()// 拦截全部.addInclude("/**");

赦免范围

之前过滤器拦截了所有的路由,对于资源型、脚本形等的路由绝大部分时候不需要拦截,因此我们对它们进行特别放行

.addExclude("/**.ico")
.addExclude("/**.svg")
.addExclude("/**.html")
.addExclude("/**.css")
.addExclude("/**.js")

需要放行哪些文件看情况决定,像我做的项目绝大部分都是纯后端,后端只暴露交互接口,我就可以不设置排除项。

错误处理

后端都会有全局错误处理,网关自然也不能落下,很不幸的是常见的 @ControllerAdvice@RestControllerAdvice在这里不起作用,是因为 SaToken 全局过滤器里面已经自带了全局错误处理,可以通过setError进行简单配置

.setError(e -> {return gson.toJson(Result.bad(e.getMessage()));
});

返回的内容将被转为字符串返回给前端,我为了与后端保持一致的返回格式,定义了一个返回类,并将其转为了Json字符串。如果你不希望鉴权失败时返回给前端字符串而是Json对象,可能就需要重写它的全局错误处理。

路由匹配

最核心的部分,路由匹配,需要通过setAuth进入路由鉴权设置,通过match来匹配指定形式的路由

.setAuth(obj -> {SaRouter.match("/**").check(StpUtil::checkLogin);/** 或者这样SaRouter.match("/**", StpUtil::checkLogin);*/
})

以上行为对所有的路由都开启了登录检验,一个match相当于 if 中的一个条件,如果后面再链接更多的match,则必须要所有的match都匹配上,才算匹配成功。

全开能保证不会遗留,但在全开的情况下,设置排除项将会比较麻烦。因为,当一个请求达到网关时,进入Auth域之后,会从上往下依次比对,假设/user/login需要放行,你不能下面一行直接加一句SaRouter.notAMtch("/user/login", StpUtil::checkLogin);,因为在上面一行它已经没有通过校验被抛出异常了,你需要的是

.setAuth(obj -> {SaRouter.match("/**", "/user/login", StpUtil::checkLogin);
})

或者这样:

.setAuth(obj -> {SaRouter.match("/**").notMatch("/user/login).check(StpUtil::checkLogin);
})

这样有2个弊端:

  • 需要排除的接口往往很多,都挤在一个对象上太臃肿
  • 没法做细致的匹配规则,比如Rest接口通常一个路由有不同的请求方式,第1种就没办法配置

适当的匹配粒度

更好的做法是按一定的粒度划分,按模块,按路由甚至按指定方法的路由匹配一个规则,是的,你可以直接写一个个精确路由地址的 SaRouter 规则:

// order-service
SaRouter.match("/order/client/cancelOrder").check(r->StpUtil.checkLogin()).check(r->StpUtil.checkPermission("1"));
SaRouter.match("/order/updateOrderState").check(r->StpUtil.checkLogin()).check(r->StpUtil.checkPermission("0"));
SaRouter.match("/order/finishOrder").check(r->StpUtil.checkLogin()).check(r->StpUtil.checkPermission("0"));
SaRouter.match("/order/page/order").check(r->StpUtil.checkLogin());

Rest接口规则

区别于上面路由名意图的明确,Rest接口往往共有一个路由,通过不同的请求方法进行区分,此时就需要借助 match(SaHttpMethod) 来匹配指定的请求方式

SaRouter.match(SaHttpMethod.GET).match("/message/msg").check(r->StpUtil.checkLogin());

上面这段代码就是对Rest接口/message/msg的GET请求进行了登录检验,那如果还有其它的方式,需要检验,但检验的不太一样,那我们要像如下几乎雷同的写4个独立的匹配规则吗?

SaRouter.match(SaHttpMethod.GET).match("/message/msg").check(r->StpUtil.checkLogin());
SaRouter.match(SaHttpMethod.POST).match("/message/msg").check(r->StpUtil.checkRole(1));
SaRouter.match(SaHttpMethod.PUT).match("/message/msg").check(r->StpUtil.checkRole(0));
SaRouter.match(SaHttpMethod.DELETE).match("/message/msg").check(r->StpUtil.checkPermission(1));

当然不!SaRouter.free可以开辟一段独立的匹配域,利用它我们可以巧妙的将一定范围内的规则聚合在一起:

SaRouter.match("/sv2/api/").free(r->{SaRouter.match(SaHttpMethod.POST).check(StpUtil::checkLogin);SaRouter.match(SaHttpMethod.PUT).check(StpUtil::checkLogin);});

上面这段代码将/sv2/api/这个Rest接口不同的独立规则聚合到了一起,这个形式更体现模块化的特性,可以提高代码的可维护性,毕竟它有一定的组织,而不是完全的零散。


总结: 接口匹配应当模块化,粒度要合适,不应过于集中,也不宜过于零散


https://dhexx.cn/news/show-4628147.html

相关文章

Vue+springboot舞蹈基础课程视频学习分享平台的实现和开发

基于java语言设计并实现了舞蹈基础数据平台。该系统基于B/S即所谓浏览器/服务器模式,应用Springboot框架,选择MySQL作为后台数据库。系统主要包括首页、个人中心、用户管理、舞蹈类型管理、舞蹈视频管理、用户留言、管理员管理、系统管理等功能模块。 重…

平面图学习

空调管道的设计:某景区有6个景点,位置分布如下图。 分析者认为:(1) A1与A4, (2) A2与A5, (3) A3与A6间人流较少,其它景点之间人流量大,必须投资铺设空调管道,但要求空调管道间不能交…

2.项目中的文件

项目的路径是这样的 目录 1 pages 1.1 json 1.2 wxml 1.3 wxss 1.4 js 2 utils 3 .eslintrc.js 4 app.js 5 app.json 6 app.wxss 7 project.config.json 8 project.private.config.json 9 sitemap.json 1 pages pages 用来存放所有小程序的页面&am…

随机数发生器设计(三)

随机数发生器设计(三)- 熵估计和健康测试 熵估计健康测试 熵估计 考虑都熵源的多样性,建立一个通用的熵估计模型比较困难。本文采用nist.sp.800-90B推荐的Markov评估。详见 https://doi.org/10.6028/NIST.SP.800-90B。 执行Markov评估时&am…

Netty之EventLoop 解读

是什么 EventLoop (事件循环对象)本质是一个单线程执行器(同时维护了一个 Selector),里面有 run 方法处理 Channel 上源源不断的 io 事件。 它的继承关系比较复杂 一条线是继承自 j.u.c.ScheduledExecutorService 因…

C++并发线程 - 如何管控线程【启动/暂停/停止/恢复】

系列文章目录 C高性能优化编程系列 深入理解软件架构设计系列 高级C并发线程系列 深入理解设计模式系列 超越昨天的自己 Keeps going beyond yesterdays own 线程管控 系列文章目录1、线程最基本的使用 - 简单管控2、如何将参数传递给线程3、线程归属权居然是可以转移的4、通…

Testing Spring MVC Web Controller

Overview Problem How can we test Spring MVC Web Controllers?How can we create HTTP requests and send to the controller?How can we verify the response? status_code, view_name, model_attributes. Spring Testing support Mock object support for…

前端046_单点登录SSO_初始化项目(Vue-CLI)

初始化项目 1、安装 node.js 和 npm2、安装 Vue-CLI 脚手架3、导入单点登录项目模版1、安装 node.js 和 npm 下载 http://nodejs.cn/download/安装 查看 npm 版本[root@pgdb ~]# node -v v12.22.12配置 npm 淘宝镜像命令npm config set registry https://registry

了解Session的本质

有一点我们必须承认,大多数web应用程序都离不开session的使用。这篇文章将会结合php以及http协议来分析如何建立一个安全的会话管理机制。 AD: 有一点我们必须承认,大多数web应用程序都离不开session的使用。这篇文章将会结合php以及http协…

【JavaEE】Tomcat-Servelet第一个helloworld程序

Tomcat & Servelet第一个程序helloworld! 文章目录 JavaEE & Tomcat & 第一个Servelet程序1. HTTP服务器 - Tomcat1.1 Tomcat的目录结构:1.2 启动Tomcat1.3 Tomcat的优点 2. Servelet框架2.1 创建Maven项目2.2 引入依赖2.3 创建目录2.4 写代…