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 和文档

Reference

ocelot 中间件的变化的更多相关文章

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

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

  2. Ocelot中文文档-中间件注入和重写

    警告!请谨慎使用. 如果您在中间件管道中看到任何异常或奇怪的行为,并且正在使用以下任何一种行为.删除它们,然后重试! 当在Startup.cs中配置Ocelot的时候,可以添加或覆盖中间件.如下所示: ...

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

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

  4. ASP.NET Core OceLot 微服务实践

    1.OceLot中间件介绍 在传统的BS应用中,随着业务需求的快速发展变化,需求不断增长,迫切需要一种更加快速高效的软件交付方式.微服务可以弥补单体应用不足,是一种更加快速高效软件架构风格.单体应用被 ...

  5. .NetCore&#183;集成Ocelot组件之完全解决方案

    阅文时长 | 11.04分钟 字数统计 | 17672.8字符 主要内容 | 1.前言.环境说明.预备知识 2.Ocelot基本使用 3.Ocelot功能挖掘 4.Ocelot集成其他组件 5.避坑指 ...

  6. Ocelot中文文档-入门

    Ocelot只能用于.NET Core,目前是为netcoreapp2.0构建的,这个文档可能会帮你了解Ocelot是否适合你. .NET Core 2.0 安装NuGet包 使用nuget安装Oce ...

  7. Ocelot中文文档-管理

    Ocelot支持在运行时通过一个认证的Http API修改配置.有两种方式对其验证, 使用Ocelot的内置IdentityServer(仅用于向管理API验证请求)或将管理API验证挂接到您自己的I ...

  8. 写个重新加载 ocelot 配置的接口

    写个重新加载 ocelot 配置的接口 Intro 我们想把 ocelot 的配置放在自己的存储中,放在 Redis 或者数据库中,当修改了 Ocelot 的配置之后希望即时生效,又不想在网关这边定时 ...

  9. Ocelot + Consul + Registrator 基于Docker 实现服务发现、服务自动注册

    目录 1. Consul集群搭建 1.1 F&Q Consul官方推荐的host网络模式运行 2. Registrator服务注册工具 2.1 F&Q Registrator悬挂服务 ...

随机推荐

  1. PyQt(Python+Qt)学习随笔:窗口对象尺寸调整相关的函数resize、showMaximized、showNormal、showMinimized

    resize(width,height) resize可以直接调整窗口的尺寸,调整效果类似于鼠标直接拉伸或缩小窗口,但窗口大小的最大值.最小值受窗口的sizePolicy.sizeHint.minim ...

  2. Llbp2p是什么?

    这是个很好的问题.用一句话来概况就是libp2p是一个模块化的协议系统,它的规范和程序库可以用来开发p2p网络应用程序. 对等节点基础 对于我们对libp2p在上面的概要描述有很多内容需要进一步解释, ...

  3. Springboot中redisTemplate乱码或json转换问题

    问题1 用RedisTemplate存入map值的时候,用rdm可视化打开,看到的是转码之后的数据,如图: 存入的方法为: public boolean hmset(String key, Map&l ...

  4. HTML引入外部字体

    HTML5如何引入外部字体 背景 现在我需要 "Montserrat-ExtraLight ExtraLight"类型的字体,但是html的font-family中找不到这个类型的 ...

  5. Acwing 393. 雇佣收银员

    算法1: 差分约束 + 枚举 O(Tn2028) 由于牵扯到 \([i - 8 + 1, i]\) 这段区间的和的约束,所以用前缀和更好表达一些. 设 \(num[i]\)表示 \(i\) 时刻有多少 ...

  6. Angular:自定义属性指令

    ①在命令行窗口下用 CLI 命令ng g directive创建指令类文件 ②将directives/light.directive.ts文件改造一番 import { Directive, Elem ...

  7. mysql中FILE权限

    FILE权限指的是对服务器主机上文件的访问,数据库用户拥有FILE权限才可以执行select into outfile,load data infile操作. 参考文章:http://blog.itp ...

  8. CentOS6下的ElasticSearch运行步骤

    如何运行ElasticSearch: 1.首先安装jdk1.8版本或以上. 2.下载elasticsearch的压缩包.(我下载的是elasticsearch-6.3.2.tar.gz) 3.使用命令 ...

  9. [日常摸鱼]bzoj4802 欧拉函数-PollardRho大整数分解算法

    啊居然要特判,卡了好久QAQ (好像Windows下的rand和Linux下的不一样? QwQ一些东西参考了喵铃的这篇blog:http://www.cnblogs.com/meowww/p/6400 ...

  10. 题解洛谷P1538【迎春舞会之数字舞蹈】

    方法:暴力,判断,输出 本题为了更好理解建议各位可以复制样例来研究,甚至可以复制题解来测试思想,相信大家不会抄. 有什么不好的请大佬们在评论里指出,谢谢 #include <bits/stdc+ ...