前面的部分:
Identity Server 4 從入門到落地(一)—— 從IdentityServer4.Admin開始
Identity Server 4 從入門到落地(二)—— 理解授權碼模式
Identity Server 4 從入門到落地(三)—— 創建Web客戶端
Identity Server 4 從入門到落地(四)—— 創建Web Api
Identity Server 4 從入門到落地(五)—— 使用Ajax 訪問 Web Api
Identity Server 4 從入門到落地(六)—— 簡單的單頁面客戶端
Identity Server 4 從入門到落地(七)—— 控制台客戶端
Identity Server 4 從入門到落地(八)—— .Net Framework 客戶端
認證服務和管理的github地址: https://github.com/zhenl/IDS4Admin
客戶端及web api示例代碼的github地址:https://github.com/zhenl/IDS4ClientDemo
前面我們試驗了認證服務的各種客戶端,到現在似乎一切正常,下一步需要為進一步開發做准備。我們要改造現有的應用,將現有的本地驗證改為認證服務,同時確保現有功能不受影響。現在,我們要模擬現有的應用功能,看在使用認證服務的情況下是否還能正常工作。在現有的應用中,使用基於ClaimsPrincipal的User對象獲取用戶數據並判斷權限,比如在RazorPage中,使用User.Identity.Name獲取登錄用戶名,使用User.IsInRole來判斷用戶是否在某個角色中,我們希望這些代碼不需要改動。我們在最初的Web客戶端程序中增加一些代碼來模擬這些功能。
在Index頁面中增加一些代碼,顯示用戶的名稱並判斷用戶是否屬於某個角色:
<span>@User.Identity.Name</span>
@if(User.IsInRole("AdminRole"))
{
<span>AdminUser</span>
}else
{
<span>NoAdmin</span>
}
運行客戶端,結果發現,沒有如我們想象中那樣工作:用戶名稱沒有顯示出來,角色判斷也不正確。我們在上面的代碼中設一下斷點,看一下User內部的變量:
這里可以看到兩個屬性:NameClaimType和RoleClaimType,這兩個屬性說明Identity的Name和Role對應的Claim,我們Name對應的Claim Type是name,不是缺省設置中的“http://schemas/xmlsoap.org/ws/2005/05/identity/claims/name”,我們需要修改一下這個設置,在program.cs中增加下面代碼:
options.TokenValidationParameters.RoleClaimType = "role";
options.TokenValidationParameters.NameClaimType = "name";
再次運行,用戶名稱可以顯示了,但角色判斷仍然不正確。我們回頭看一下返回的claims,發現沒有角色,這就有兩種可能,一種是角色沒有發送過來,還有一種可能就是沒有解析。我們先排除第一種可能,登錄到認證服務管理進行檢查。首先檢查一下client的scope中是否包含了role,我們在profile中發現已經設置了role:
然后檢查一下用戶是否設置了角色:
這一項也正常,那么問題應該出在客戶端。在網上搜了一下,發現需要在代碼中增加映射,代碼如下:
options.ClaimActions.MapJsonKey("role", "role");
再次運行程序,這次工作正常了。
需要說明一下,對於其它需要增加到Claim中的自定義項,也需要使用MapJsonKey或者MapUniqueJsonKey進行映射,比如,我們增加一個用戶自定義的屬性nickname,可以作為claim增加到名稱為profile的scope中,如果在客戶端獲取這個屬性,需要增加映射如下:
options.ClaimActions.MapUniqueJsonKey("nickname", "nickname");
MapJsonKey和MapUniqueJsonKey兩者的區別是,MapUniqueJsonKey會把多個相同的Claim合並為數組。比如,如果把上面的options.ClaimActions.MapJsonKey("role", "role");改為options.ClaimActions.MapUniqueJsonKey("role", "role");返回的claim 如下:
這種情況下多個role被合並到一起,作為一個數組存在,這種情況下IsInRole將不起作用。
到此,為客戶端開發做的准備差不多了,下一步我們需要把代碼中寫死的配置項移動到配置文件中去。