以前咱们在 Ocelot 网关的基础上自定义了一个认证受权的 Ocelot 中间件,根据请求的路径和 Method 进行匹配,找到对应的权限配置,并判断是否能够拥有访问资源的角色,若是没有则返回 401/403,若是有权限则转发到下游服务。html
原来的匹配方式是首先根据请求路径和方法彻底匹配,若是匹配不到则尝试使用正则匹配。git
咱们此次要作的就是将原来的正则匹配替换成 Ocelot 內部的路由匹配方式,这样咱们在配置的时候就再也不须要配置两套了,一边写 Ocelot 路由的配置,一边写权限的配置,这样能减小很多工做量github
咱们想使用 Ocelot 的路由匹配,首先应该了解 Ocelot 的执行过程,而后找到对应的路由匹配的地方,看路由匹配使用到了哪个服务,用这个服务在咱们本身的业务逻辑里匹配便可。api
先来看一下 Ocelot 的服务注册,Ocelot 的服务注册async
能够看到主要的服务注册代码应该在 OcelotBuilder
中,查看 OcelotBuilder
https://github.com/ThreeMammals/Ocelot/blob/13.5.2/src/Ocelot/DependencyInjection/OcelotBuilder.csui
能够看到,Ocelot 的服务注册都在这里, Ocelot 内部好多都是基于接口的,因此须要找对应的实现的话能够看它的服务注册是注册的哪个服务便可。url
简单分析一下,Ocelot 的路由匹配过程必定在寻找下游地址的时候,根据上游的请求信息(直接请求网关的请求)匹配,因此咱们首先找到 DownstreamRouteFinderMiddleware
https://github.com/ThreeMammals/Ocelot/blob/13.5.2/src/Ocelot/DownstreamRouteFinder/Middleware/DownstreamRouteFinderMiddleware.cs3d
由上面的代码,咱们能够看到,下游路由地址是经过 IDownstreamRouteFinder
来找下游路由的,转到对应的实现代码: https://github.com/ThreeMammals/Ocelot/blob/13.5.2/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteFinder.cscode
这里咱们能够看到是经过 IUrlPathToUrlTemplateMatcher
来进行路由匹配的,因此咱们须要用到这个服务,而后看这个 Match
方法的参数,前两个参数比较明确,第一个参数是上游请求的地址,第二个参数是请求的 queryString,第三个参数则是 Ocelot 内部构建出来的路由模板信息 UpstreamPathTemplate
,而后咱们就须要知道怎么构建一个 UpstreamPathTemplate
对象,继续探索htm
直接看 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
上面提到了咱们没有使用 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; } } }
这样,apiPermission 的 Path 配置基本可使用和 Ocelot 配置同样的路由,能够更方便的配置,避免 996 咯