ocelot 中间件的变化

Intro

之前我们使用 ocelot 的时候自定义了一些中间件来实现我们定制化的一些需求,最近博客园上有小伙伴问我怎么使用,他用的版本是 16.0 版本,16.0 和 17.0 版本的差异不是特别大,就以 17.0 版本为例看一下 ocelot 中间件的变化

Sample

还是拿之前的一个自定义认证授权的一个中间件为例,中间件做的事情主要是

  1. 基于 Resource(API Path) 以及 请求 Method 查询需要的权限

  2. 如果不需要用户登录就可以访问,就直接往下游服务转发

  3. 如果需要权限,判断当前登录用户的角色是否有对应的角色可以访问

  4. 如果可以访问就转发到下游服务,如果没有权限访问根据用户是否登录,已登录返回 403 Forbidden,未登录返回 401 Unauthorized

Before

之前的实现(基于 13.x 版本)详细可以参考:https://www.cnblogs.com/weihanli/p/custom-authentication-authorization-in-ocelot.html

大致代码如下:

public class UrlBasedAuthenticationMiddleware : Ocelot.Middleware.OcelotMiddleware
{private readonly IConfiguration _configuration;private readonly IMemoryCache _memoryCache;private readonly OcelotRequestDelegate _next;public UrlBasedAuthenticationMiddleware(OcelotRequestDelegate next, IConfiguration configuration, IMemoryCache memoryCache, IOcelotLoggerFactory loggerFactory) : base(loggerFactory.CreateLogger<UrlBasedAuthenticationMiddleware>()){_next = next;_configuration = configuration;_memoryCache = memoryCache;}public async Task Invoke(DownstreamContext context){var permissions = await _memoryCache.GetOrCreateAsync("ApiPermissions", async entry =>{using (var conn = new SqlConnection(_configuration.GetConnectionString("ApiPermissions"))){entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1);return (await conn.QueryAsync<ApiPermission>("SELECT * FROM dbo.ApiPermissions")).ToArray();}});var result = await context.HttpContext.AuthenticateAsync(context.DownstreamReRoute.AuthenticationOptions.AuthenticationProviderKey);context.HttpContext.User = result.Principal;var user = context.HttpContext.User;var request = context.HttpContext.Request;var permission = permissions.FirstOrDefault(p =>request.Path.Value.Equals(p.PathPattern, StringComparison.OrdinalIgnoreCase) && p.Method.ToUpper() == request.Method.ToUpper());if (permission == null)// 完全匹配不到,再根据正则匹配{permission =permissions.FirstOrDefault(p =>Regex.IsMatch(request.Path.Value, p.PathPattern, RegexOptions.IgnoreCase) && p.Method.ToUpper() == request.Method.ToUpper());}if (!user.Identity.IsAuthenticated){if (permission != null && string.IsNullOrWhiteSpace(permission.AllowedRoles)) //默认需要登录才能访问{//context.HttpContext.User = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, "Anonymous") }, context.DownstreamReRoute.AuthenticationOptions.AuthenticationProviderKey));}else{SetPipelineError(context, new UnauthenticatedError("unauthorized, need login"));return;}}else{if (!string.IsNullOrWhiteSpace(permission?.AllowedRoles) &&!permission.AllowedRoles.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Any(r => user.IsInRole(r))){SetPipelineError(context, new UnauthorisedError("forbidden, have no permission"));return;}}await _next.Invoke(context);}
}

New

来看一下在新版本(16.x/17.x)的 ocelot 中实现代码是怎样的

public class ApiPermission
{public string AllowedRoles { get; set; }public string PathPattern { get; set; }public string Method { get; set; }
}public class UrlBasedAuthenticationMiddleware : Ocelot.Middleware.OcelotMiddleware
{private readonly IConfiguration _configuration;private readonly IMemoryCache _memoryCache;private readonly RequestDelegate _next;public UrlBasedAuthenticationMiddleware(RequestDelegate next, IConfiguration configuration, IMemoryCache memoryCache, IOcelotLoggerFactory loggerFactory) : base(loggerFactory.CreateLogger<UrlBasedAuthenticationMiddleware>()){_next = next;_configuration = configuration;_memoryCache = memoryCache;}public async Task Invoke(HttpContext httpContext){// var permissions = await _memoryCache.GetOrCreateAsync("ApiPermissions", async entry =>//{//    using (var conn = new SqlConnection(_configuration.GetConnectionString("ApiPermissions")))//    {//        entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1);//        return (await conn.QueryAsync<ApiPermission>("SELECT * FROM dbo.ApiPermissions")).ToArray();//    }//});var permissions = new[]{new ApiPermission(){PathPattern = "/api/test/values",Method = "GET",AllowedRoles = ""},new ApiPermission(){PathPattern = "/api/test/user",Method = "GET",AllowedRoles = "User"},new ApiPermission(){PathPattern = "/api/test/admin",Method = "GET",AllowedRoles = "Admin"},};var downstreamRoute = httpContext.Items.DownstreamRoute();var result = await httpContext.AuthenticateAsync(downstreamRoute.AuthenticationOptions.AuthenticationProviderKey);if (result.Principal != null){httpContext.User = result.Principal;}var user = httpContext.User;var request = httpContext.Request;var permission = permissions.FirstOrDefault(p =>request.Path.ToString().Equals(p.PathPattern, StringComparison.OrdinalIgnoreCase) && p.Method.ToUpper() == request.Method.ToUpper());if (permission == null){permission =permissions.FirstOrDefault(p =>Regex.IsMatch(request.Path.ToString(), p.PathPattern, RegexOptions.IgnoreCase) && p.Method.ToUpper() == request.Method.ToUpper());}if (user.Identity?.IsAuthenticated == true){if (!string.IsNullOrWhiteSpace(permission?.AllowedRoles) &&!permission.AllowedRoles.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Any(r => user.IsInRole(r))){httpContext.Items.SetError(new UnauthorizedError("forbidden, have no permission"));return;}}else{if (permission != null && string.IsNullOrWhiteSpace(permission.AllowedRoles)){}else{httpContext.Items.SetError(new UnauthenticatedError("unauthorized, need login"));return;}}await _next.Invoke(httpContext);}
}

Diff

主要的区别在于 ocelot 中间件的变化,在之前的版本,ocelot 是自己的中间件,签名是 Task Invoke(DownstreamContext context) 是 ocelot 自己的 DownstreamContext,在之后 ,Ocelot 为了和 asp.net core 中间件保持一样的签名,以更好的复用 asp.net core 中的中间件,更新了自己的中间件, ocelot 自己的 context 等信息现在放在了 HttpContext.Items 中,并通过一系列的扩展方法来获取和更新对应的信息

但是目前的实现并不能够完全等同于 asp.net core 中间件,因为如果你想要中断某一个中间件的话现在大概是有问题的,因为现在 ocelot 中间件里的 HttpContext 并不是原始的 HttpContext ocelot 会在真正开始处理请求之前新建一个 HttpContext 把基本的请求信息复制过去,主要实现代码: https://github.com/ThreeMammals/Ocelot/blob/17.0.0/src/Ocelot/Multiplexer/MultiplexingMiddleware.cs

如果想要在自定义中间件中实现中断,需要使用 ocelot 的中间件,通过 SetError 来去处理而不要直接使用 httpContext.Response 去中断请求

API Diff

  1. 中间件 Invoke 方法签名,从原来的 Task Invoke(DownstreamContext context) 更新成 Task Invoke(HttpContext context)

  2. SetPipelineError 不再是 OcelotMiddleware 中的一个方法,通过 httpContext.Items.SetError 方法来代替

  3. 通过 httpContext.Items.DownstreamRoute() 来获取当前请求的 DownstreamRoute 信息

More

除了中间件的变化,配置也发生了变化,原来的 ReRoute 也变成了 Route,升级的时候需要注意一下配置的变化,否则可能就会 404 了,在 17.0 之后,authorisation 更新成了 authorization, authorise 也更新成了 authorize

更多更新可以参考 ocelot 的 PR changes 和文档

文中提到的示例在 Github 上可以获取完整的代码 https://github.com/WeihanLi/AspNetCorePlayground/tree/master/OcelotDemo

Reference

  • https://github.com/ThreeMammals/Ocelot/compare/15.0.0...16.0.0

  • https://github.com/ThreeMammals/Ocelot/compare/16.0.0...17.0.0

  • https://github.com/WeihanLi/AspNetCorePlayground/tree/master/OcelotDemo

  • https://www.cnblogs.com/weihanli/p/custom-authentication-authorization-in-ocelot.html

ocelot 中间件的变化相关推荐

  1. 自定义 ocelot 中间件输出自定义错误信息

    自定义 ocelot 中间件输出自定义错误信息 Intro ocelot 中默认的 Response 中间件在出错的时候只会设置 StatusCode 没有具体的信息,想要展示自己定义的错误信息的时候 ...

  2. 在Ocelot中使用自定义的中间件(二)

    在上文中<在Ocelot中使用自定义的中间件(一)>,我介绍了如何在Ocelot中使用自定义的中间件来修改下游服务的response body.今天,我们再扩展一下设计,让我们自己设计的中 ...

  3. 在Ocelot中使用自定义的中间件(一)

    Ocelot是ASP.NET Core下的API网关的一种实现,在微服务架构领域发挥了非常重要的作用.本文不会从整个微服务架构的角度来介绍Ocelot,而是介绍一下最近在学习过程中遇到的一个问题,以及 ...

  4. .net core ocelot 获取路由的mothed_Net Core微服务入门全纪录(四)Ocelot网关(上)

    上一篇[.Net Core微服务入门全纪录(三)--Consul-服务注册与发现(下)]已经使用Consul完成了服务的注册与发现,实际中光有服务注册与发现往往是不够的,我们需要一个统一的入口来连接客 ...

  5. eShopOnContainers 知多少[9]:Ocelot gateways

    引言 客户端与微服务的通信问题永远是一个绕不开的问题,对于小型微服务应用,客户端与微服务可以使用直连的方式进行通信,但对于对于大型的微服务应用我们将不得不面对以下问题: 如何降低客户端到后台的请求数量 ...

  6. swagger 返回json字符串_Net Core微服务入门全纪录(完结)——Ocelot与Swagger

    前言 上一篇[.Net Core微服务入门全纪录(八)--Docker Compose与容器网络]完成了docker-compose.yml文件的编写,最后使用docker compose的一个up指 ...

  7. .Net Core微服务入门全纪录(完结)——Ocelot与Swagger

    点击上方蓝字"小黑在哪里"关注我吧 前言 上一篇[.Net Core微服务入门全纪录(八)--Docker Compose与容器网络]完成了docker-compose.yml文件 ...

  8. .NET Core微服务开发网篇-ocelot

    通过上篇我们知道,网关是外部访问的统一入口,本文采用Ocelot作为Api网关. 环境要求: vs2019 .NetCore3.1 Ocelot16.0.1 创建一个产品服务Api站点(AAStore ...

  9. Angular SPA基于Ocelot API网关与IdentityServer4的身份认证与授权

    在上一讲中,我们已经完成了一个完整的案例,在这个案例中,我们可以通过Angular单页面应用(SPA)进行登录,然后通过后端的Ocelot API网关整合IdentityServer4完成身份认证.在 ...

最新文章

  1. 【Qt】调用Python函数:无参数、单个参数、多个参数、数组参数
  2. ZendStudio中,缺少gbk gb2312编码的解决办法
  3. swift_041(Swift的@noescape和@escaping)
  4. 一些常见的Find 命令
  5. HDU4546(优先队列)
  6. 汽车维修行业呼吁大学生加入修车行列
  7. terminated 线程_Java【多线程系列】JUC线程池—2. 原理(二)、Callable和Future
  8. 10以内的分解与组成怎么教_狗狗酷炫的飞盘游戏怎么玩?分解步骤教你快速学会...
  9. ssh(Spring+Spring mvc+hibernate)——DeptController.java
  10. iphone手机型号获取
  11. React开发(226):默认方法返回一个新的参数两个括号
  12. IntelliJ IDEA最常用的一些快捷键,学会了室友还以为你在祖安对线
  13. 【React Native 实战】微信登录
  14. python调用函数的优点_python基础之函数
  15. Linux下的基本操作
  16. 生成随机不重复数列表(C#)
  17. Web前端课程的学习内容有哪些?
  18. 制作简单时钟logo
  19. 计算机开机反复启动,电脑开机一直无限重启怎么解决
  20. Android Studio设置关闭当前页面的快捷键ctrl+w

热门文章

  1. zabbix4.2 系列(二):agent配置
  2. CentOS 6.5 部署WordPress
  3. linux里查看最耗CPU的线程
  4. [asp.net mvc 奇淫巧技] 04 - 你真的会用Action的模型绑定吗?
  5. ArrayList 的实现原理
  6. .netCHARTING图表控件详细介绍及下载
  7. Android学习笔记(1)
  8. Microsoft AJAX Client Library规范的实例
  9. (转)WCF教程系列(1)-创建第一个WCF程序
  10. word中 有注释标签吗_如何在Word中注释图像