【Blog.Core开源】网关自定义认证鉴权与传参
書接上文,上回咱們說到了《【Blog.Core開源】網關統一集成下游服務文檔》,已經將多個下游服務統一集成到了網關里,并且也把接口文檔Swagger給集成了,那今天就說一下認證和鑒權相關的話題。
繼續說下故事背景
在平時開發的時候,特別是有網關的情況下,經常會遇到一個不可避免的話題,就是網關到底要不要幫下游處理某些業務邏輯的問題,比如說認證鑒權、審計日志、當前用戶信息獲取,白名單等等。
這里其實見仁見智,同時也要考慮各個項目的具體架構設計和需要,我個人的習慣還是網關要輕一些,什么叫輕一些呢,拿BlogCore舉例,認證走的是Ids4的統一認證平臺,從平臺那里得到令牌Token,然后經過網關走到BlogCore,解析,并走具體的自定義授權邏輯,因為這里涉及到動態菜單權限配置,所以很少會放到網關里處理,畢竟每個下游服務都可能會有自己的那部分邏輯。其實除了授權這塊,還有一些數據,比如當前用戶的私密信息,例如手機號之類的,這個phone肯定不能放到token里的,因為token雖然有過期時間,但是就算是失效,還是可以解密出來的,放到公網上的令牌基本都是只放一些非私密的個人信息,比如uid或者是roleId,實在有需要也可以在token里放部門的id的,這也無可厚非,但是phone和address是萬萬不能放到token里的。
那么問題來了,phone和address我們到底應該從哪里獲取?上邊的菜單權限大家已經達成共識,就是放到下游,讓下游服務自己來處理,那根據token中的uid來獲取phone信息,就需要考慮下了,很多人說放網關唄,每次請求查庫等操作,然后放到header里傳遞給下游,這也是一個方案,今天也會給大家講講怎么獲取,怎么傳。
當然我個人的意見還是網關僅僅是解析token里有的,傳遞給下游,至于查庫的那些,還是下游獲取吧,這是我的個人意見,并不是完全正確。為什么呢,大家想想,咱們在網關里寫攔截器或者中間件,每次接口請求,都根據header中的token來查庫,這樣不管下游需不需要,不管下游接口是不是匿名都去查庫一下,會造成資源浪費,比如我就想搜索下list,每次都查詢下當前人的user信息,似乎沒那么必要,特別是list頁面高并發的時候,是不是不太好,當然這樣的好處就是對下游方便且能做詳細的審計日志。
今天咱們就說下如何自定義攔截器傳遞自定義claim信息給下游。
01
PART
網關自定義認證處理器
在網關中注冊認證服務,并設計處理器,實現認證授權攔截,比如說token是否可以正常的解密等,用來判斷token的有效性等,也可以查詢數據庫,獲取私密信息:
services.AddAuthentication().AddScheme<AuthenticationSchemeOptions,?CustomAuthenticationHandler>(Permissions.GWName,?_?=>?{?});然后具體的處理器,大家根據需求自定義即可,注意把信息放到Claims里,不僅可以在當前網關的其他地方獲取,從而減少二次請求的情況。也可以傳遞給下游服務。
public class CustomAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions> {public CustomAuthenticationHandler(IOptionsMonitor<AuthenticationSchemeOptions> options,ILoggerFactory logger,UrlEncoder encoder,ISystemClock clock) : base(options, logger, encoder, clock){}protected override async Task<AuthenticateResult> HandleAuthenticateAsync(){// 可以查詢數據庫等操作// 獲取當前用戶不能放到token中的私密信息var userPhone = "15010000000";var claims = new List<Claim>(){new Claim("user-phone", userPhone),new Claim("gw-sign", "gw")};var principal = new ClaimsPrincipal(new ClaimsIdentity(claims, Scheme.Name));var ticket = new AuthenticationTicket(principal, Scheme.Name);await Task.CompletedTask;return AuthenticateResult.Success(ticket);} }?內容很簡單,就是一個普通的處理器,那接下來就是看如何把Claim給傳給下游服務了。
02
PART
對下游服務開啟認證處理器
Ocelot已經做好了配置,就像是自定義響應處理器一樣,認證的也可以直接配置:
也可以有更多的參數配置,具體可以參考官網:
https://ocelot.readthedocs.io/en/latest/features/configuration.html?highlight=AuthenticationOptions#configuration
03
PART
Ocelot將Claim傳遞下游
還是在Ocelot的官網上可以看到很多Demo,我只配置三項,1、分別是動態從Claim中獲取并用Request的Header傳值,2、直接在Request中傳遞固定Header值,3、獲取下游服務的Response的Header給上游網關。
其中第三點還是很有用的,比如我們以后的Skywalking中,如果某次鏈路請求報錯了,但是又想快速的定位,所以就需要用戶給我們提供當前操作的標識,有時候是uid,有時候是url,這兩個都不是很直觀。通過配置Ocelot,正好可以從下游服務的response的header中返給前端,用戶就能提供了,更加快速方便的定位問題。
// blog-svc {"UpstreamPathTemplate": "/svc/blog/{url}","UpstreamHttpMethod": [ "Get", "Post", "Put", "Delete" ],"LoadBalancerOptions": {"Type": "RoundRobin"},"DownstreamPathTemplate": "/svc/blog/{url}","DownstreamScheme": "http","DownstreamHostAndPorts": [{"Host": "localhost","Port": 9291}],//?添加到headers// 從claims中獲取"AddHeadersToRequest": {"user-phone": "Claims[user-phone] > value","gw-sign": "Claims[gw-sign] > value"},//?從上游網關的request的header中"UpstreamHeaderTransform": {"custom-key": "blog.gateway"},// 從下游服務的response的header中"DownstreamHeaderTransform":?{"down-app":?"{para-down-app}","trace-id":?"Trace-Id"},"AuthenticationOptions": {"AuthenticationProviderKey": "GW"} },在上邊注釋的三塊,就是常見的三種方案。
04
PART
下游服務查看具體效果
在BlogCore服務中,valueController中測試下是否傳遞了具體的參數:
其中獲取Claim方法,也獲取了下header中其他的參數:
public IEnumerable<Claim> GetClaimsIdentity(){var claims = _accessor.HttpContext.User.Claims.ToList();var headers = _accessor.HttpContext.Request.Headers;foreach (var header in headers){claims.Add(new Claim(header.Key, header.Value));}return claims;}這里有一個小注意事項:
如果下游服務是加權的,可以直接通過swagger添加token的方式,獲取claims信息,但是接口是匿名的,那swagger是不會傳遞token信息的,我們可以用postman測試,一樣的效果,畢竟前端Vue.js也是我們手動傳遞的。
關于swagger不加權就不傳遞token這個問題,以后我會優化下,寫個擴展中間件。
查看下具體的情況:
攜帶上token以后,發起請求,無論是自定義固定的參數還是Claims中的變量都傳給了下游服務,并且下游的Response的Header也有了值。
好啦,網關系列的分享就先到這里了,咱們下次再見,說說注冊中心集成功能。
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的【Blog.Core开源】网关自定义认证鉴权与传参的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 一起来庆祝 .NET 20 周年!
- 下一篇: 如何通过 HttpWebRequest