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

使用 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

原文地址:https://www.cnblogs.com/weihanli/p/12043955.html

时间: 2024-10-06 21:29:29

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

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

xp多网卡静态路由设置方法 一.多个IP都在同一网段或VALN.这类情况没什么好说的,在各块网卡的本地连接属性里设置好IP地址.子网掩码和默认网关即可. 二.多个IP属于不同网段或VLAN.这时如果按照通常的方法设置IP地址,填好各网段的子网掩码和默认网关,就会发现服务器所在的多个网段中,只有一个网段可以正常通信. 这个问题我也是最近才碰到的.经过Google和百度一番,得出原因:路由冲突. 我们知道,Windows系统下为网络连接填写默认网关等于为本机添加一条默认静态路由.打开“本地连接属性”

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

前后台获取Action.Controller.ID名方法 前台页面:ViewContext.RouteData.Values["Action"].ToString();//获取Action名称   ViewContext.RouteData.Values["Controller"].ToString();//获取控制器名称          ViewContext.RouteData.Values["ID"].ToString();//获取路由参

THINKPHP短链接设置方法(路由设置)

//路由设置(短链接设置)'URL_ROUTER_ON' => true,'URL_ROUTE_RULES' => array('log' => 'Login/index','admin' => 'Login/admin','reg' => 'Login/register', 'c/:id' => 'Index/index/user' '/^C_(\d+)$/' => 'Index/index/user?id=:1'), 长链接:http://localhost/

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

项目中经常遇到多网卡的服务器,但是一个服务器的默认网关只有一个,当需要在多个网络中访问的时候(特别是在公安.交警等政府项目中),就需要添加静态路由了. 添加静态路由的方法有很多种,下面介绍2种比较常见的: 第一种:   这种方式在重启之后就失效了. echo "route add -net 172.16.100.0/24 gw 192.168.6.249" >>/etc/rc.local 使得在系统重启之后,加入这条命令. 删除路由 route del -net 172.1

centos6.7 route命令使用以及 添加永久路由的方法

因为一个服务器做了2个bond 0(em1,em2) bond1(em3,em4) 但是默认路由只生成了bond1的默认网关.但是bond1是数据库的心跳线.不是来做数据库数据传输用得.所以导致数据库无法访问.在rc.local 手动添加默认路由,但是系统重启后不生效.所以在网上找了一些办法.最终解决. 一:使用route 命令添加 1.使用route 命令添加的路由,机器重启或者网卡重启后路由就失效了,方法: 添加到主机的路由  route add –host 192.168.1.11 dev

描述文件不匹配的解决方法

场景: 有时候当我们在做真机测试的时候,会出现这样的错误提示:, 这个错误就是说,可能你的项目之前用别的描述文件进行过测试,现在你用另外的描述文件给它做真机测试,但是文件记录的时上次测试的描述文件,所有当你使用新的描述文件测试的时候,提示你说找不到指定的描述文件. 解决方法: 1. 打开项目文件夹,找到工程文件( .xcodeproj 文件),选中 - > 右键 -> 显示包内容: 2. 在打开的包内容中,找到 project.pbxproj 文件,选中 - > 右键 -> 打开方

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

转摘,原文章地址:http://blog.sina.com.cn/s/blog_828e50020101ern5.html 查看路由的命令route -n CentOS添加永久静态路由 在使用双网卡,同时使用2个网关的时候就需要加一条静态路由了.当然还有很多时候会需要加路由. 操作系统版本centos-6.4 64bit 一:使用route 命令添加 1.使用route 命令添加的路由,机器重启或者网卡重启后路由就失效了,方法: // 添加到主机的路由 # route add –host 192

CentOS6 Linux系统添加永久静态路由的方法

一.使用route命令添加route 命令 设置和查看路由表都可以用 route 命令,设置内核路由表的命令格式是: # route [add|del] [-net|-host] target [netmask Nm] [gw Gw] [[dev] If] 其中: add : 添加一条路由规则 del : 删除一条路由规则 -net : 目的地址是一个网络 -host : 目的地址是一个主机 target : 目的网络或主机 netmask : 目的地址的网络掩码 gw : 路由数据包通过的网关

微博第三方登陆请求授权出现错误码:21322(重定向地址不匹配)的解决方法

https://hostcoz.com/151.html 主题自带了微博登陆接口,很简单的去新浪微博开放平台创建了网页应用,然后把APP ID和 AppSecret填好后,以为大功告成后,轻车熟路地点击使用微博登陆,映入我眼前的是: 用微博帐号登录出错了!对第三方应用进行授权时出现错误,请您联系第三方应用的开发者:XXX 或者稍后再试. 错误码:21322重定向地址不匹配 遇到错误就去看了下新浪开放平台的接口说明 错误码为 redirect_uri_mismatch ,是因为没有设置回调地址.