ocelot 中间件的变化
ocelot 中间件的变化
Intro
之前我们使用 ocelot 的时候自定义了一些中间件来实现我们定制化的一些需求,最近博客园上有小伙伴问我怎么使用,他用的版本是 16.0 版本,16.0 和 17.0 版本的差异不是特别大,就以 17.0 版本为例看一下 ocelot 中间件的变化
Sample
还是拿之前的一个自定义认证授权的一个中间件为例,中间件做的事情主要是
- 基于 Resource(API Path) 以及 请求 Method 查询需要的权限
- 如果不需要用户登录就可以访问,就直接往下游服务转发
- 如果需要权限,判断当前登录用户的角色是否有对应的角色可以访问
- 如果可以访问就转发到下游服务,如果没有权限访问根据用户是否登录,已登录返回 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并不是原始的HttpContextocelot 会在真正开始处理请求之前新建一个HttpContext把基本的请求信息复制过去,主要实现代码: https://github.com/ThreeMammals/Ocelot/blob/17.0.0/src/Ocelot/Multiplexer/MultiplexingMiddleware.cs如果想要在自定义中间件中实现中断,需要使用 ocelot 的中间件,通过 SetError 来去处理而不要直接使用
httpContext.Response去中断请求
API Diff
- 中间件
Invoke方法签名,从原来的Task Invoke(DownstreamContext context)更新成Task Invoke(HttpContext context) SetPipelineError不再是OcelotMiddleware中的一个方法,通过httpContext.Items.SetError方法来代替- 通过
httpContext.Items.DownstreamRoute()来获取当前请求的DownstreamRoute信息
More
除了中间件的变化,配置也发生了变化,原来的 ReRoute 也变成了 Route,升级的时候需要注意一下配置的变化,否则可能就会 404 了,在 17.0 之后,authorisation 更新成了 authorization, authorise 也更新成了 authorize
更多更新可以参考 ocelot 的 PR changes 和文档
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 中间件的变化的更多相关文章
- 自定义 ocelot 中间件输出自定义错误信息
自定义 ocelot 中间件输出自定义错误信息 Intro ocelot 中默认的 Response 中间件在出错的时候只会设置 StatusCode 没有具体的信息,想要展示自己定义的错误信息的时候 ...
- Ocelot中文文档-中间件注入和重写
警告!请谨慎使用. 如果您在中间件管道中看到任何异常或奇怪的行为,并且正在使用以下任何一种行为.删除它们,然后重试! 当在Startup.cs中配置Ocelot的时候,可以添加或覆盖中间件.如下所示: ...
- eShopOnContainers 知多少[9]:Ocelot gateways
引言 客户端与微服务的通信问题永远是一个绕不开的问题,对于小型微服务应用,客户端与微服务可以使用直连的方式进行通信,但对于对于大型的微服务应用我们将不得不面对以下问题: 如何降低客户端到后台的请求数量 ...
- ASP.NET Core OceLot 微服务实践
1.OceLot中间件介绍 在传统的BS应用中,随着业务需求的快速发展变化,需求不断增长,迫切需要一种更加快速高效的软件交付方式.微服务可以弥补单体应用不足,是一种更加快速高效软件架构风格.单体应用被 ...
- .NetCore·集成Ocelot组件之完全解决方案
阅文时长 | 11.04分钟 字数统计 | 17672.8字符 主要内容 | 1.前言.环境说明.预备知识 2.Ocelot基本使用 3.Ocelot功能挖掘 4.Ocelot集成其他组件 5.避坑指 ...
- Ocelot中文文档-入门
Ocelot只能用于.NET Core,目前是为netcoreapp2.0构建的,这个文档可能会帮你了解Ocelot是否适合你. .NET Core 2.0 安装NuGet包 使用nuget安装Oce ...
- Ocelot中文文档-管理
Ocelot支持在运行时通过一个认证的Http API修改配置.有两种方式对其验证, 使用Ocelot的内置IdentityServer(仅用于向管理API验证请求)或将管理API验证挂接到您自己的I ...
- 写个重新加载 ocelot 配置的接口
写个重新加载 ocelot 配置的接口 Intro 我们想把 ocelot 的配置放在自己的存储中,放在 Redis 或者数据库中,当修改了 Ocelot 的配置之后希望即时生效,又不想在网关这边定时 ...
- Ocelot + Consul + Registrator 基于Docker 实现服务发现、服务自动注册
目录 1. Consul集群搭建 1.1 F&Q Consul官方推荐的host网络模式运行 2. Registrator服务注册工具 2.1 F&Q Registrator悬挂服务 ...
随机推荐
- Springboot集成swagger和knife
前言 knife4j是在swagger的基本上做做了一次封装,主要体现在ui表现,所有在使用前必须先搭建好swagger2,其实是swagger和knife都可以访问, 至于哪个好用全看个人! swa ...
- js 彻底搞懂事件循环机制 Event Loop
我们都知道javascript是单线程语言,就是因为单线程的特性,就不得不提js中的同步和异步 一.同步和异步 所谓单线程,无非就是同步队列和异步队列,js代码是自上向下执行的,在主线程中立即执行的就 ...
- When you received Ubuntu...
翻译软件 Goldendict 安装命令: sudo apt install goldendict 在 dit -> Dictinoaries -> Websites 中添加有道的链接: ...
- 题解-Railgun
题面 Railgun \(T\) 组测试数据,每次给定 \(n,k\),求(\(F(i)\) 为斐波那契数列第 \(i\) 项): \[\sum_{1\le x_i\le n(1\le i\le k) ...
- 配置 Spring Batch 批处理失败重试机制
1. 引言 默认情况下,Spring批处理作业在执行过程中出现任何错误都会失败.然而有些时候,为了提高应用程序的弹性,我们就需要处理这类间歇性的故障. 在这篇短文中,我们就来一起探讨 如何在Sprin ...
- Editor.md解决跨域上传的问题
Editor.md解决跨域上传的问题 编辑 editormd\plugins\image-dialog\image-dialog.js 替换以下代码片段 if (settings.crossDomai ...
- vue2实现路由懒加载
一.什么是懒加载 顾名思义,懒加载就是随用随加载,什么时候需要就什么时候加载. 二.为什么需要懒加载 在单页应用中,如果没有使用懒加载,webpack打包后的文件会很大,这时进入首页时的加载时间会很长 ...
- CAP、BASE、ACID
CAP定理 定义 CAP定理(CAP theorem),它指出对于一个分布式计算系统来说,不可能同时满足以下三点: 一致性(Consistency) (等同于所有节点访问同一份最新的数据副本:写操作之 ...
- 栈的应用:表达式括号匹配检测(C)
问题说明: 假设数学表达式中允许包含两种括号:圆括号"()"和方括号"[]",嵌套顺序任意. 正确的嵌套模式:( [ ] ( ) ).[ ( [ ] [ ] ) ...
- 2、MyCat读写分离
1.主从复制 搭建mycat的读写分离,首先我们现需要搭建mysql的主从复制 [1].Mysql主从复制原理 [2].MySQL主从复制配置 (1).主机配置 修改配置文件:vim /etc/my. ...