Identity Server 4 從入門到落地(九)—— 客戶端User和Role的解析

尋找無名的特質發表於2021-12-10

前面的部分:
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將不起作用。

到此,為客戶端開發做的準備差不多了,下一步我們需要把程式碼中寫死的配置項移動到配置檔案中去。

相關文章