ocelot 中间件的变化
ocelot 中間件的變化
Intro
之前我們使用 ocelot 的時(shí)候自定義了一些中間件來實(shí)現(xiàn)我們定制化的一些需求,最近博客園上有小伙伴問我怎么使用,他用的版本是 16.0 版本,16.0 和 17.0 版本的差異不是特別大,就以 17.0 版本為例看一下 ocelot 中間件的變化
Sample
還是拿之前的一個(gè)自定義認(rèn)證授權(quán)的一個(gè)中間件為例,中間件做的事情主要是
基于 Resource(API Path) 以及 請求 Method 查詢需要的權(quán)限
如果不需要用戶登錄就可以訪問,就直接往下游服務(wù)轉(zhuǎn)發(fā)
如果需要權(quán)限,判斷當(dāng)前登錄用戶的角色是否有對(duì)應(yīng)的角色可以訪問
如果可以訪問就轉(zhuǎn)發(fā)到下游服務(wù),如果沒有權(quán)限訪問根據(jù)用戶是否登錄,已登錄返回 403 Forbidden,未登錄返回 401 Unauthorized
Before
之前的實(shí)現(xiàn)(基于 13.x 版本)詳細(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)//?完全匹配不到,再根據(jù)正則匹配{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))?//默認(rèn)需要登錄才能訪問{//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 中實(shí)現(xiàn)代碼是怎樣的
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
主要的區(qū)別在于 ocelot 中間件的變化,在之前的版本,ocelot 是自己的中間件,簽名是 Task Invoke(DownstreamContext context) 是 ocelot 自己的 DownstreamContext,在之后 ,Ocelot 為了和 asp.net core 中間件保持一樣的簽名,以更好的復(fù)用 asp.net core 中的中間件,更新了自己的中間件, ocelot 自己的 context 等信息現(xiàn)在放在了 HttpContext.Items 中,并通過一系列的擴(kuò)展方法來獲取和更新對(duì)應(yīng)的信息
但是目前的實(shí)現(xiàn)并不能夠完全等同于 asp.net core 中間件,因?yàn)槿绻阆胍袛嗄骋粋€(gè)中間件的話現(xiàn)在大概是有問題的,因?yàn)楝F(xiàn)在 ocelot 中間件里的 HttpContext 并不是原始的 HttpContext ocelot 會(huì)在真正開始處理請求之前新建一個(gè) HttpContext 把基本的請求信息復(fù)制過去,主要實(shí)現(xiàn)代碼: https://github.com/ThreeMammals/Ocelot/blob/17.0.0/src/Ocelot/Multiplexer/MultiplexingMiddleware.cs
如果想要在自定義中間件中實(shí)現(xiàn)中斷,需要使用 ocelot 的中間件,通過 SetError 來去處理而不要直接使用 httpContext.Response 去中斷請求
API Diff
中間件 Invoke 方法簽名,從原來的 Task Invoke(DownstreamContext context) 更新成 Task Invoke(HttpContext context)
SetPipelineError 不再是 OcelotMiddleware 中的一個(gè)方法,通過 httpContext.Items.SetError 方法來代替
通過 httpContext.Items.DownstreamRoute() 來獲取當(dāng)前請求的 DownstreamRoute 信息
More
除了中間件的變化,配置也發(fā)生了變化,原來的 ReRoute 也變成了 Route,升級(jí)的時(shí)候需要注意一下配置的變化,否則可能就會(huì) 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
總結(jié)
以上是生活随笔為你收集整理的ocelot 中间件的变化的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 探索 .NET团队对API的设计流程
- 下一篇: Istio 知多少 | 下一代微服务的守