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悬挂服务 ...
随机推荐
- 手把手教你写DI_3_小白徒手支持 `Singleton` 和 `Scoped` 生命周期
手把手教你写DI_3_小白徒手支持 Singleton 和 Scoped 生命周期 在上一节:手把手教你写DI_2_小白徒手撸构造函数注入 浑身绷带的小白同学:我们继续开展我们的工作,大家都知道 Si ...
- 树的直径,LCA复习笔记
前言 复习笔记第6篇. 求直径的两种方法 树形DP: dfs(y); ans=max( ans,d[x]+d[y]+w[i] ); d[x]=max( d[x],d[y]+w[i] ); int di ...
- 转载:c# 获取CPU温度(非WMI,直接读取硬件)
c#获取cpu温度 很早一个项目做远控,所以需要用到获取cpu温度,但是就是不知从何下手,无意中发现了Open Hardware Monitor,令我的项目成功完成 亲测20台清装xp sp2的机器, ...
- Docker安装rocketmq踩坑指南
Docker 网络 Docker容器运行的时候有host.bridge.none三种网络可供配置. 默认是bridge,即桥接网络,以桥接模式连接到宿主机:host是宿主网络,即与宿主机共用网络:no ...
- JS复习之深浅拷贝
一.复习导论(数据类型相关) 想掌握JS的深浅拷贝,首先来回顾一下JS的数据类型,JS中数据类型分为基本数据类型和引用数据类型. 基本数据类型是指存放在栈中的简单数据段,数据大小确定,内存空间大小可以 ...
- Java经典小游戏——贪吃蛇简单实现(附源码)
一.使用知识 Jframe GUI 双向链表 线程 二.使用工具 IntelliJ IDEA jdk 1.8 三.开发过程 3.1素材准备 首先在开发之前应该准备一些素材,已备用,我主要找了一个图片以 ...
- ssh远程服务器不通
1.关闭防火墙 service iptables status service iptables stop 2.在/etc/hosts文件添加远程服务器信息(连接的两端都添加) 服务器1(racdb1 ...
- Spring Cloud Alibaba (一): SpringCloud与SpringBoot版本选型
前言 近年SpringCloud与SpringBoot更新迭代非常频繁,导致我们对两者的版本选型非常的困难.若是版本选择有问题, 会导致开发中调试兼容性占用非常多的得必要时间. SpringClo ...
- centos 7 让脚本开机运行
按照网上搜索的文章指导,有三种方式可以实现,个人觉得比较简单的是修改/etc/rc.d/rc.local,添加要开机执行的命令. 修改后,重启,发现要运行的服务没有启动. 搜索发现下面这篇博客解释了原 ...
- Thymeleaf的th
th:action 定义后台控制器路径,类似<form>标签的action属性. <form id="login-form" th:action="@{ ...