從零開始一起學Blazor WebAssembly 開發(4)
登入模組基本完成了,登入主要用了以下幾個點:
1、後端採用的Abp Vnext 框架,這個框架自帶的IdentityServer4使用者角色許可權控制,這個框架登入研究了好一陣子,有幾個坑這裡說下:
1)、Login.Razor利用HttpClient把使用者名稱和Rsa加密後的密碼提交到後端,後端把密碼解密後再往IdentityServer4的伺服器獲取Token(用的oauth2.0 裡的密碼模式),這塊知識網上能搜到不少,重點說一下這裡有個坑,就是我之前不知道的,傳scope時,要傳 offline_access 這個 才能返回RefreshToken,因為IdentityServer4 預設提供的是JWT令牌,這個令牌相關的資料也能從網上搜到不少,這裡就不講了。這個令牌有個缺點,就是伺服器控制不了退出,解決辦法就是讓accesstoken有效時間變短,快到期時用refreshtoken 重新整理,這個refreshtoken 我建議是儲存在後端,不要往前端放,需要重新整理時,前端傳請求過來。如果必須要放在前端,IdentityServer4 提供了OneTimeOnly這種設定。就是隻能用一次就換的。
2)、IdentityServer4在每次請求之前框架有一個呼叫發現文件的服務(httpClient.GetDiscoveryDocumentAsync)。這個服務就是返回IdentityServer4所支援的服務,研究了下Abp vnext框架自帶的,發現每次都呼叫這塊太耗時間。就想把結果放在快取裡,結果試了很久,發現Abp自帶的快取框架處理不好這個,自己另外寫也不是不行。但是就感覺不太好。然後研究發現,其實IdentityServer4 也帶了一個發現文件快取的方法IDiscoveryCache,用這個就能實現了,具體使用方法這裡講一下,因為我發現網上這個資料真不多:
a 先注入
//發現文件快取context.Services.AddSingleton<IDiscoveryCache>(r => { var factory = r.GetRequiredService<IHttpClientFactory>(); //var policy = new DiscoveryPolicy() { },用到可以加下 var dc = new DiscoveryCache(configuration["IdentityClients:Default:Authority"], () => factory.CreateClient()); //dc.CacheDuration = TimeSpan.FromMinutes(2); 快取有效期, 預設是24小時,需要改的話自己改 return dc; });
b 使用
private readonly IDiscoveryCache _ddrcache; //上下兩處程式碼結合自己的專案的Service,自己分別寫。 var disco = await _ddrcache.GetAsync(); if (disco.IsError) { throw new Exception(disco.Error); } return disco;
3)、前端和後端不是在同一個域裡邊,原本以為webAssembly 有點近似客戶端,應該不會有這個問題。發現還真的有,這就需要在後端設定下跨域設定,否則提交不了。
2、登入後前端得到Token,要把Token資訊儲存到localstore裡,這裡用到了網上一個大神的解決方案,直接看程式碼
public class TokenUtil { private readonly IJSRuntime _jsRuntime; public TokenUtil(IJSRuntime jsRuntime) { _jsRuntime = jsRuntime; } public async Task SaveAccessToken(string accessToken) { await _jsRuntime.InvokeVoidAsync("wasmHelper.saveAccessToken", accessToken); } public async Task<String> GetAccessToken() { return await _jsRuntime.InvokeAsync<String>("wasmHelper.getAccessToken"); } public async Task RefreshToken(string tokenValue) { var httpClient = new HttpClient(); httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + tokenValue); await httpClient.GetFromJsonAsync<TokenInfo>(""); } }
var wasmHelper = {}; wasmHelper.ACCESS_TOKEN_KEY = "__access_token__"; wasmHelper.saveAccessToken = function (tokenStr) { localStorage.setItem(wasmHelper.ACCESS_TOKEN_KEY, tokenStr); }; wasmHelper.getAccessToken = function () { return localStorage.getItem(wasmHelper.ACCESS_TOKEN_KEY); };
登入模組完成了,剩下就是些細節了。沒有什麼技術難題。