使用 Ocelot 匹配路由的方法匹配路由

Intro

之前我们在 Ocelot 网关的基础上自定义了一个认证授权的 Ocelot 中间件,根据请求的路径和 Method 进行匹配,找到对应的权限配置,并判断是否可以拥有访问资源的角色,如果没有则返回 401/403,如果有权限则转发到下游服务。

原来的匹配方式是首先根据请求路径和方法完全匹配,如果匹配不到则尝试使用正则匹配。

我们这次要做的就是将原来的正则匹配替换成 Ocelot 內部的路由匹配方式,这样我们在配置的时候就不再需要配置两套了,一边写 Ocelot 路由的配置,一边写权限的配置,这样能减少不少工作量

深入 Ocelot 路由匹配

我们想使用 Ocelot 的路由匹配,首先应该了解 Ocelot 的执行过程,然后找到对应的路由匹配的地方,看路由匹配使用到了哪一个服务,用这个服务在我们自己的业务逻辑里匹配即可。

先来看一下 Ocelot 的服务注册,Ocelot 的服务注册

可以看到主要的服务注册代码应该在 OcelotBuilder 中,查看 OcelotBuilder https://github.com/ThreeMammals/Ocelot/blob/13.5.2/src/Ocelot/DependencyInjection/OcelotBuilder.cs

可以看到,Ocelot 的服务注册都在这里, Ocelot 内部好多都是基于接口的,所以需要找对应的实现的话可以看它的服务注册是注册的哪一个服务即可。

简单分析一下,Ocelot 的路由匹配过程一定在寻找下游地址的时候,根据上游的请求信息(直接请求网关的请求)匹配,所以我们首先找到 DownstreamRouteFinderMiddleware https://github.com/ThreeMammals/Ocelot/blob/13.5.2/src/Ocelot/DownstreamRouteFinder/Middleware/DownstreamRouteFinderMiddleware.cs

由上面的代码,我们可以看到,下游路由地址是通过 IDownstreamRouteFinder 来找下游路由的,转到对应的实现代码: https://github.com/ThreeMammals/Ocelot/blob/13.5.2/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteFinder.cs

这里我们可以看到是通过 IUrlPathToUrlTemplateMatcher 来进行路由匹配的,所以我们需要用到这个服务,然后看这个 Match 方法的参数,前两个参数比较明确,第一个参数是上游请求的地址,第二个参数是请求的 queryString,第三个参数则是 Ocelot 内部构建出来的路由模板信息 UpstreamPathTemplate,然后我们就需要知道怎么构建一个 UpstreamPathTemplate 对象,继续探索

直接看 UpstreamPathTemplate,表示一脸懵逼,不知道怎么构建, 全局搜素了一下,发现有一个 IUpstreamTemplatePatternCreator 里面定义了一个 Create 的方法

这个方法看上去简单了好多,查看 IReRoute 的定义 https://github.com/ThreeMammals/Ocelot/blob/13.5.2/src/Ocelot/Configuration/File/IReRoute.cs

我们只需要根据路径模板构建一个 IReRoute 对象即可,Ocelot 中有一个实现了 IReRoute 的类 FileReRoute,但是感觉有些复杂,就没有用,自定义了一个类型实现了 IReRoute

自此,我们就已经找到了要使用 Ocelot 路由匹配所需要的服务了:

  • IUrlPathToUrlTemplateMatcher
  • IUpstreamTemplatePatternCreator

使用 Ocelot 路由匹配

上面提到了我们没有使用 FileReRoute 对象,所以我们就需要自定义一个 IReRoute 对象:

private class FakeReRoute : IReRoute
{
public string UpstreamPathTemplate { get; set; }
public bool ReRouteIsCaseSensitive { get; set; }
public int Priority { get; set; }
}

使用 Ocelot 路由匹配示例:

public class UrlBasedAuthenticationMiddleware : Ocelot.Middleware.OcelotMiddleware
{
private readonly GatewayOptions _gatewayOptions;
private readonly IMemoryCache _memoryCache;
private readonly OcelotRequestDelegate _next; private readonly IUrlPathToUrlTemplateMatcher _urlTemplateMatcher;
private readonly IUpstreamTemplatePatternCreator _templatePatternCreator; public UrlBasedAuthenticationMiddleware(OcelotRequestDelegate next, IOptions<GatewayOptions> options, IMemoryCache memoryCache, IOcelotLoggerFactory loggerFactory,
IUrlPathToUrlTemplateMatcher urlTemplateMatcher,
IUpstreamTemplatePatternCreator templatePatternCreator)
: base(loggerFactory.CreateLogger<UrlBasedAuthenticationMiddleware>())
{
_next = next; _gatewayOptions = options.Value;
_memoryCache = memoryCache; _urlTemplateMatcher = urlTemplateMatcher;
_templatePatternCreator = templatePatternCreator;
} public async Task Invoke(DownstreamContext context)
{
var permissions = await _memoryCache.GetOrCreateAsync(_gatewayOptions.ApiPermissionsCacheKey, async entry =>
{
using (var conn = new SqlConnection(_gatewayOptions.PermissionsConnectionString))
{
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1);
return (await conn.QueryAsync<ApiPermission>(@"")).Select(_ => _.GetViewModel()).ToArray();
}
}); var request = context.HttpContext.Request; var permission = permissions.FirstOrDefault(p =>
request.Path.Value.Equals(p.PathPattern, StringComparison.OrdinalIgnoreCase) &&
p.Method == request.Method.ToUpper());
if (null == permission)
{
permission = permissions.FirstOrDefault(p =>
p.Method == request.Method.ToUpper() &&
_urlTemplateMatcher.Match(request.Path.Value, request.QueryString.Value,
_templatePatternCreator.Create(new FakeReRoute()
{
UpstreamPathTemplate = p.PathPattern
})).Data.Match
);
} // ...
await _next.Invoke(context);
} private class FakeReRoute : IReRoute
{
public string UpstreamPathTemplate { get; set; }
public bool ReRouteIsCaseSensitive { get; set; }
public int Priority { get; set; }
}
}

More

这样,apiPermission 的 Path 配置基本可以使用和 Ocelot 配置一样的路由,可以更方便的配置,避免 996 咯

Reference

使用 Ocelot 匹配路由的方法匹配路由的更多相关文章

  1. js 实现前端路由的方法

    js 实现前端路由的方法 前端路由原理 History API https://developer.mozilla.org/en-US/docs/Web/API/History_API https:/ ...

  2. python中RabbitMQ的使用(路由键模糊匹配)

    路由键模糊匹配 使用正则表达式进行匹配.其中“#”表示所有.全部的意思:“*”只匹配到一个词. 匹配规则: 路由键:routings = [ 'happy.work',  'happy.life' , ...

  3. xp多网卡静态路由设置方法

    xp多网卡静态路由设置方法 一.多个IP都在同一网段或VALN.这类情况没什么好说的,在各块网卡的本地连接属性里设置好IP地址.子网掩码和默认网关即可. 二.多个IP属于不同网段或VLAN.这时如果按 ...

  4. MVC前后台获取Action、Controller、ID名方法 以及 路由规则

    前后台获取Action.Controller.ID名方法 前台页面:ViewContext.RouteData.Values["Action"].ToString();//获取Ac ...

  5. linux下(Ubuntu、centos)添加永久静态路由的方法

    项目中经常遇到多网卡的服务器,但是一个服务器的默认网关只有一个,当需要在多个网络中访问的时候(特别是在公安.交警等政府项目中),就需要添加静态路由了. 添加静态路由的方法有很多种,下面介绍2种比较常见 ...

  6. JQUERY选择和操作DOM元素(利用正则表达式的方法匹配字符串中的一部分)

    JQUERY选择和操作DOM元素(利用正则表达式的方法匹配字符串中的一部分) 1.匹配属性的开头 $("[attributeName^='value']"); 2.匹配属性的结尾 ...

  7. 1.3 正则表达式和python语言-1.3.4使用 match()方法匹配字符串

    1.3.4使用 match()方法匹配字符串(第一次写博客,格式,述语有不当之处还请见谅)2018-05-08 Python 代码是以Jupyter Notebook编写的,主要写的是python3的 ...

  8. CentOS 6.4 添加永久静态路由所有方法汇总(原创)

    转摘,原文章地址:http://blog.sina.com.cn/s/blog_828e50020101ern5.html 查看路由的命令route -n CentOS添加永久静态路由 在使用双网卡, ...

  9. xpath的数据和节点类型以及XPath中节点匹配的基本方法

    XPath数据类型  XPath可分为四种数据类型:  节点集(node-set)  节点集是通过路径匹配返回的符合条件的一组节点的集合.其它类型的数据不能转换为节点集.  布尔值(boolean)  ...

随机推荐

  1. Spring中常用的注解及作用

    @Component(value) 配置类,当使用该注解时,SpringIOC会将这个类自动扫描成一个bean实例 不写的时候,默认是类名,且首字母小写 @ComponentScan 默认是代表进行扫 ...

  2. package.json文件内容介绍

    概述 每个项目的根目录下面,一般都有一个package.json文件,定义了这个项目所需要的各种模块,以及项目的配置信息(比如名称.版本.许可证等元数据).npm install命令根据这个配置文件, ...

  3. python:正则0

    Python3 正则表达式特殊符号及用法(详细列表) 正则表达式的强大之处在于特殊符号的应用,特殊符号定义了字符集合.子组匹配.模式重复次数.正是这些特殊符号使得一个正则表达式可以匹配字符串集合而不只 ...

  4. python:爬虫2——隐藏自己

    一.添加浏览器 方法一: head['User-Agent'] = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, li ...

  5. IDEA+SpringBoot+Mybatis+maven分布式项目框架的搭建

    参考文章:https://blog.csdn.net/qq_34410726/article/details/98214992 一.maven分布式工程的基本架构 demo  #父工程模块,主要用来定 ...

  6. pyhton3 之 time模块实例小结

    一.实例1:实现秒表: import time print('按下回车开始计时,按下 Ctrl + C 停止计时.') while True: try: input() # 如果是 python 2. ...

  7. 读完此文让你了解各个queue的原理

    队列是一种特殊的线性表,它只允许在表的前端进行删除操作,而在表的后端进行插入操作.通俗来讲,就是一个队列中,早来的排在前面,后来的就在队尾,而这个队列大多只有一个出口和入口的单项队列.Queue的重要 ...

  8. 关于HashMap容量的初始化,还有这么多学问。

    在<HashMap中傻傻分不清楚的那些概念>文章中,我们介绍了HashMap中和容量相关的几个概念,简单介绍了一下HashMap的扩容机制. 文中我们提到,默认情况下HashMap的容量是 ...

  9. 【Android - 自定义View】之自定义View浅析

    1.概述 Android自定义View / ViewGroup的步骤大致如下: 1) 自定义属性: 2) 选择和设置构造方法: 3) 重写onMeasure()方法: 4) 重写onDraw()方法: ...

  10. java 抽象类和接口整理

    java中定义一些不含方法体的方法,方法体的实现交给该类的子类根据自己的具体情况去实现,这样的方法就是abstract修饰的抽象方法,包含抽象方法的类叫抽象类,用abstract修饰 抽象方法: ab ...