1、經過元旦兩天的全力整改,終於在這新的一年,完成了我的布道生涯的第一個大步走 —— 那就是客戶端(VUE)、服務端(ASP.NET Core API)、授權中心(IdentityServer4)的大融合,不僅有文檔也有代碼,更重要的是實戰。
2、這一大步里邊當然也有很多小步驟,知識點就不說了,過去的文章里都有。
3、具體的代碼和效果呢,我會在下邊給大家先簡單的說明一下,今天的目的主要是第一篇,概述下,里邊的小知識點或者注意事項,其實主要還是Ids4的內容,我還是會慢慢的在以后的文章或者視頻中,給大家講解的。
4、有人問了我的新年計划,主要是CI/CD這一塊,配合Docker和DDD,實現第二個閉環,具體還沒有想好,等我通知吧。
那下邊我就簡單的說說效果吧,大家也可以自行體驗一下:
(支持滑動更新等基本操作,后期增加單點登錄)
一、Blog.Admin 客戶端
Admin項目,是基於Vue+Ele開發的一套后端權限框架,是我自主研發的,里邊主要是用到了動態數據庫授權認證,使用JWT的復雜策略授權技術,精確到按鈕基本,目前部門數據權限還在設計當中,以后也會附加上,這是內部授權這一塊,但是認證想做一個統一認證中心,所以就想到了IdentityServer4了。
Vue項目和其他的SPA項目是一樣的,連接IdentityServer4認證中心,主要是通過oidc-client這個插件來處理的,
npm install oidc-client --save
用法其實很簡單,我簡單說下思路,具體的看我的代碼,或者看官網都可以:
class ApplicationUserManager extends UserManager { constructor () { super({ authority: 'http://ids.neters.club', client_id: 'blogadminjs', redirect_uri: 'http://vueadmin.neters.club/callback', response_type: 'id_token token', scope: 'openid profile roles blog.core.api', post_logout_redirect_uri: 'http://vueadmin.neters.club' }) }
這個是核心方法,目的是通過配置,實現用戶管理,比如登錄,跳轉,回調,獲取用戶信息,令牌token,刷新等等,當然以后可能會配合單點登錄做處理。
methods: { async refreshUserInfo() { const user = await applicationUserManager.getUser(); if (user) { this.user.name = user.profile.name; this.user.isAuthenticated = true; } else { this.user.name = ""; this.user.isAuthenticated = false; } } }
這個是使用上邊的UserManager來處理遠程獲取到的用戶數據。
具體的代碼我已經放到了Blog.Admin項目的ids4分支下了:
二、Blog.Core 資源服務端
這個項目想必都知道了,不多說什么,其實你的任意一個后端服務項目都可以,只要做個表結構,然后配置Identityserver4的認證即可。
//2.2【認證】、IdentityServer4 認證 (暫時忽略) services.AddAuthentication("Bearer") .AddIdentityServerAuthentication(options => { options.Authority = "http://ids.neters.club"; options.RequireHttpsMetadata = false; options.ApiName = "blog.core.api"; });
這個就是一個核心的代碼,將我們BlogCore的資源服務器認證方式,從JWT改成Ids4認證。
修改完認證方式以后,下邊就是簡單的對其中幾個小知識點進行微調了,比如某些Claim聲明字段,我用的和Ids4不太一樣,所以就簡單微調一下,主要的修改內容,我也新建了一個分支,可以自行查看下,修改的地方不多。
三、Blog.Idp 認證授權中心
這個才是整個項目的重頭戲,項目的重中之重,不過配置起來也不難,具體的操作和使用,我也在錄視頻和寫文章,其實只要了解這幾個知識點,就基本學會了Ids4了:
1、如何引用指定的nuget包;
2、JWT、OpenID、OAuth2、OIDC 四者的關系和內容;
3、常見的四種授權方式:簡化、混合、密碼、客戶端等要明白場景,會用;
4、學會聯調;
具體的就不多說了,不是今天的講解內容。今天主要點一下部署的時候的幾個知識點,其他的看我的代碼即可,這次是master分支。
// 使用證書,可以直接使用開發證書 builder.AddDeveloperSigningCredential(); // 或者自己配置 .AddDeveloperSigningCredential(true, ConstanceHelper.AppSettings.CredentialFileName) .AddSigningCredential(new X509Certificate2(Path.Combine(Environment.WebRootPath, Configuration["Certificates:Path"]), Configuration["Certificates:Password"]))
證書這個很重要,當然既然是安全,就要好好處理,可以配合的證書,繼續搞搞HTTPS安全協議,未來還是很多要處理的。
new Client { ClientId = "blogadminjs", ClientName = "Blog.Admin JavaScript Client", AllowedGrantTypes = GrantTypes.Implicit, AllowAccessTokensViaBrowser = true, RedirectUris = { "http://vueadmin.neters.club/callback" }, PostLogoutRedirectUris = { "http://vueadmin.neters.club" }, AllowedCorsOrigins = { "http://vueadmin.neters.club" }, AllowedScopes = { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, "roles", "blog.core.api" } }
這是另一個重點,必須要配置客戶端,不僅是數據生成到數據庫,更是在授權的時候,起到安全的作用。
最后一個小問題,確定是我自己的問題,就是部署的時候,我本來用的Nginx進行代理,但是配置文件里,卻依然是localhost:
這樣就導致了一個問題,雖然我的認證中心項目可以正常的跑,我是說服務器生產環境,但是用客戶端去登錄的時候,會自動跑到locaohost里,畢竟配置文件是這樣的。
網上找了很多資料,都是本地教程,部署的都很少,我也是很方,最后我找到了官方的一個在線demo項目,它是正常的,我仔細研究了下代碼,代碼和我的沒啥區別,最后靈機一動,我看了看他們的服務器,是IIS!我趕緊實驗了一番,還真的是正常了:
但是這個不科學呀,這個我要承認下,肯定是我學藝不精,Nginx肯定是可行的,但是我沒有找到為何去配置,這個時候應該查看官網的,當我繼續在尋找的時候,但是正好有一個小伙伴的小伙伴告訴了我,賊簡單,這個故事告訴我們,學習知識要從細節着手,不能知其然而不知其所以然。
var builder = services.AddIdentityServer(options =>
{
options.Events.RaiseErrorEvents = true;
options.Events.RaiseInformationEvents = true;
options.Events.RaiseFailureEvents = true;
options.Events.RaiseSuccessEvents = true;
options.IssuerUri = "http://ids.neters.club";// 就是這里
options.PublicOrigin = "http://ids.neters.club";
})
結果還是正確的,看來還是要多讀文檔,當然如果實在是繞不過來,也可以先求助。
就這樣,元旦一整天就過去了,項目也終於放上去了,當然還有很多要優化的,比如如何在Nginx部署IdentityServer的時候配置HTTPS協議,既然開始了,便只顧風雨兼程了。
四、遇到了問題
1、手機版兼容,登錄不跳轉
本以為上邊的已經完成了,電腦上,也各種測試沒問題,就在下班的路上,在手機上試試吧,竟然無效?!無法跳轉。真是尷尬
夜里開始研究官方的Demo代碼,終於發了這一塊,竟然需要處理Cookie策略,直接看代碼吧:
public static class SameSiteHandlingExtensions { public static IServiceCollection AddSameSiteCookiePolicy(this IServiceCollection services) { services.Configure<CookiePolicyOptions>(options => { options.MinimumSameSitePolicy = SameSiteMode.Unspecified; options.OnAppendCookie = cookieContext => CheckSameSite(cookieContext.Context, cookieContext.CookieOptions); options.OnDeleteCookie = cookieContext => CheckSameSite(cookieContext.Context, cookieContext.CookieOptions); }); return services; } private static void CheckSameSite(HttpContext httpContext, CookieOptions options) { if (options.SameSite == SameSiteMode.None) { var userAgent = httpContext.Request.Headers["User-Agent"].ToString(); if (DisallowsSameSiteNone(userAgent)) { // For .NET Core < 3.1 set SameSite = (SameSiteMode)(-1) options.SameSite = SameSiteMode.Unspecified; } } } private static bool DisallowsSameSiteNone(string userAgent) { // Cover all iOS based browsers here. This includes: // - Safari on iOS 12 for iPhone, iPod Touch, iPad // - WkWebview on iOS 12 for iPhone, iPod Touch, iPad // - Chrome on iOS 12 for iPhone, iPod Touch, iPad // All of which are broken by SameSite=None, because they use the iOS networking stack if (userAgent.Contains("CPU iPhone OS 12") || userAgent.Contains("iPad; CPU OS 12")) { return true; } // Cover Mac OS X based browsers that use the Mac OS networking stack. This includes: // - Safari on Mac OS X. // This does not include: // - Chrome on Mac OS X // Because they do not use the Mac OS networking stack. if (userAgent.Contains("Macintosh; Intel Mac OS X 10_14") && userAgent.Contains("Version/") && userAgent.Contains("Safari")) { return true; } // Cover Chrome 50-69, because some versions are broken by SameSite=None, // and none in this range require it. // Note: this covers some pre-Chromium Edge versions, // but pre-Chromium Edge does not require SameSite=None. if (userAgent.Contains("Chrome/5") || userAgent.Contains("Chrome/6")) { return true; } return false; } }
然后把這個服務注冊:
最后中間件配置下Cookie策略:
有一個小伙伴說,只配置Cookie中間件即可,你也可以試試再。
2、無效Token,訪問無權限接口,異常
谷歌到了官方的 Issue,大家討論了很多,隨便找了一個:https://github.com/IdentityServer/IdentityServer4/issues/1627
簡單來說,主要是因為傳遞了一個無效的Token來訪問我們的資源服務器,導致中間件管道內異常,但是不影響正常使用,配置下,對token做下校驗即可,比如這樣的:
具體的可以查看我的代碼。
五、配置HTTPS
在我的微信公眾號文章里:
https://mp.weixin.qq.com/s/GQXah1rq6qEMqcgAfWNkIw
六、下一步 2020
今年開始,一切都是新的,第一個閉環已經完成了,當然還有很多需要優化,比如單點登錄,DDD和Blog項目也遷移到Ids4上來,那下一個就開始了 —— CI/CD DevOps 我來了。
https://github.com/anjoy8/Blog.IdentityServer(授權)