【asp.net core 系列】15 自定義Identity

来源:https://www.cnblogs.com/c7jie/archive/2020/07/09/13270513.html
-Advertisement-
Play Games

0. 前言 在之前的文章中簡單介紹了一下asp.net core中的Identity,這篇文章將繼續針對Identity進行進一步的展開。 1. 給Identity添加額外的信息 在《【asp.net core 系列】13 Identity 身份驗證入門》一文中,我們大概瞭解瞭如何使用Identit ...


0. 前言

在之前的文章中簡單介紹了一下asp.net core中的Identity,這篇文章將繼續針對Identity進行進一步的展開。

1. 給Identity添加額外的信息

在《【asp.net core 系列】13 Identity 身份驗證入門》一文中,我們大概瞭解瞭如何使用Identity,以及如何保存一些信息以便後續的驗證。這裡我們將深入討論一下如何給Identity添加更多的信息。

我們知道在給Identity添加數據的時候,需要添加一個Claim對象。我們先回顧一下Claim的信息,Claim的屬性大多只提供了公開的get訪問器,所以這個類的重點在於構造方法:

public class Claim
{
    // 基礎的
    public Claim(string type, string value);
    public Claim(string type, string value, string valueType);
    public Claim(string type, string value, string valueType, string issuer);
    public Claim(string type, string value, string valueType, string issuer, string originalIssuer);
    //
    public Claim(BinaryReader reader);
    public Claim(BinaryReader reader, ClaimsIdentity subject);
}

暫且看一下幾個使用字元類型的構造函數參數:

  1. type Claim的類型,支持自定義,但asp.net core 提供了一個基礎的類型定義:
public static class ClaimTypes
{
    // 隱藏其他屬性
    public const string Name = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name";
    public const string Role = "http://schemas.microsoft.com/ws/2008/06/identity/claims/role";
}

​ 這個類里定義了大多數情況下會用到的Claims的類型。

  1. value 存放Claim的值,通常情況下不對這個值進行約束

  2. valueType 表示 value的類型,取值範圍參考類:

    public static class ClaimValueTypes
    {
        public const string Base64Binary = "http://www.w3.org/2001/XMLSchema#base64Binary";
        public const string UpnName = "http://schemas.xmlsoap.org/claims/UPN";
        public const string UpnName = "http://schemas.xmlsoap.org/claims/UPN";
     	public const string UInteger32 = "http://www.w3.org/2001/XMLSchema#uinteger32";
        public const string Time = "http://www.w3.org/2001/XMLSchema#time";
        public const string String = "http://www.w3.org/2001/XMLSchema#string";
        public const string Sid = "http://www.w3.org/2001/XMLSchema#sid";
        public const string RsaKeyValue = "http://www.w3.org/2000/09/xmldsig#RSAKeyValue";
        public const string Rsa = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/rsa";
        public const string Rfc822Name = "urn:oasis:names:tc:xacml:1.0:data-type:rfc822Name";
        public const string KeyInfo = "http://www.w3.org/2000/09/xmldsig#KeyInfo";
        public const string Integer64 = "http://www.w3.org/2001/XMLSchema#integer64";
        public const string X500Name = "urn:oasis:names:tc:xacml:1.0:data-type:x500Name";
        public const string Integer32 = "http://www.w3.org/2001/XMLSchema#integer32";
        public const string HexBinary = "http://www.w3.org/2001/XMLSchema#hexBinary";
        public const string Fqbn = "http://www.w3.org/2001/XMLSchema#fqbn";
        public const string Email = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress";
        public const string DsaKeyValue = "http://www.w3.org/2000/09/xmldsig#DSAKeyValue";
        public const string Double = "http://www.w3.org/2001/XMLSchema#double";
        public const string DnsName = "http://schemas.xmlsoap.org/claims/dns";
        public const string DaytimeDuration = "http://www.w3.org/TR/2002/WD-xquery-operators-20020816#dayTimeDuration";
        public const string DateTime = "http://www.w3.org/2001/XMLSchema#dateTime";
        public const string Date = "http://www.w3.org/2001/XMLSchema#date";
        public const string Boolean = "http://www.w3.org/2001/XMLSchema#boolean";
        public const string Base64Octet = "http://www.w3.org/2001/XMLSchema#base64Octet";
        public const string Integer = "http://www.w3.org/2001/XMLSchema#integer";
        public const string YearMonthDuration = "http://www.w3.org/TR/2002/WD-xquery-operators-20020816#yearMonthDuration";
    }
    
  3. issuer 用來存放 Claim的發佈者,預設值是:ClaimsIdentity.DefaultIssuer 該值允許在構造函數是傳NULL,一旦傳NULL,則自動認為是ClaimsIdentity.DefaultIssuer

  4. originalIssuer Claim的原發佈人,如果不給值,則預設與issuer一致。

這是從構造函數以及相關文檔中獲取到的。

關於ClaimTypes里我只貼了兩個,原因是這兩個值在Claim中是兩個必不可少的值。根據屬性名就能看出來,一個是設置用戶的名稱,一個是設置用戶的角色。

那麼,繼續探索Claim里的屬性和方法:

public class Claim
{
    public string Type { get; }
    public ClaimsIdentity Subject { get; }
    public IDictionary<string, string> Properties { get; }
    public string OriginalIssuer { get; }
    public string Issuer { get; }
    public string ValueType { get; }
    public string Value { get; }
    public virtual Claim Clone();
    public virtual Claim Clone(ClaimsIdentity identity);
    public virtual void WriteTo(BinaryWriter writer);
}

幾個基本屬性都是從構造函數中獲取的,這裡就不做過多的介紹了。不過值得註意的一點是,Properties這個屬性的值獲取是需要使用

public Claim(BinaryReader reader, ClaimsIdentity? subject)

這個構造方法才可以有效的對其進行賦值,所以這個屬性並沒有太多值得關註的地方。

介紹完了Claim類之後,我們繼續看一下Identity相關的常用類:

public class ClaimsIdentity : IIdentity;

通過這個類的聲明,我們可以看出它實現了介面:

public interface IIdentity
{
    string? AuthenticationType { get; }
    bool IsAuthenticated { get; }
    string? Name { get; }
}

其中

  • AuthenticationType 表示驗證類型
  • IsAuthenticated 表示是否驗證通過
  • Name 存放的用戶名

這是Identity里最關鍵的三個屬性,貫穿著整個Identity體系。我們繼續看一下ClaimsIdentity的幾個關鍵點:

public class ClaimsIdentity : IIdentity
{
    public ClaimsIdentity(string authenticationType);
    public ClaimsIdentity(IIdentity identity);
    public ClaimsIdentity(IEnumerable<Claim> claims);
    public ClaimsIdentity(IEnumerable<Claim> claims, string authenticationType);
    public ClaimsIdentity(IIdentity identity, IEnumerable<Claim> claims);
    public virtual void AddClaim(Claim claim);
    public virtual void AddClaims(IEnumerable<Claim> claims);
}

對於ClaimsIdentity而言,其核心內容是Claim實例。我們通常需要構造Claim對象,在Claim對象中添加我們想添加的值,然後裝入ClaimIdentity中。這裡有一個值需要額外註意一下:AuthenticationType 表示驗證類型,值並沒有額外要求,不過對於使用Cookie作為信息保存的話,需要設置值為:

CookieAuthenticationDefaults.AuthenticationScheme

這時候,我們已經獲得了一個Identity對象,在asp.net core 中 Identity體系還有最後一個關鍵類:

public class ClaimsPrincipal : IPrincipal
{
    public ClaimsPrincipal();
    public ClaimsPrincipal(IIdentity identity);
    public ClaimsPrincipal(IPrincipal principal);
    public virtual void AddIdentities(IEnumerable<ClaimsIdentity> identities);
    public virtual void AddIdentity(ClaimsIdentity identity);
}

這個類提供了幾個方法用來存儲Identity,這個類在IPrincipal基礎上以Identity為基礎數據。這一點可以通過構造函數和它提供的一些方法可以確認。

2. 讀取Identity的信息

在第一小節中,我簡單介紹了一下如何利用Claim和ClaimsIdentity以及ClaimsPrincipal這三個類來存儲用戶信息以及我們想要的數據。這裡我們看一下如何通過Principal讀取信息,以及簡單剖析一下背後的邏輯。

我們使用HttpContext的擴展方法:

public static Task SignInAsync(this HttpContext context, ClaimsPrincipal principal);

將我們設置的principal數據保存,所保存的地方取決於我們在Startup.cs中的設置。在該系列中,我們啟用了Cookie,所以這個信息會以Cookie的形式保存。

在控制器內部時,Controller類為我們提供了一個屬性:

public ClaimsPrincipal User { get; }

通過這個屬性可以反向獲取到我們保存的Principal實例。

接下來,讓我們反向解析出Principal裡面的數據:

public interface IPrincipal
{
    IIdentity? Identity { get; }
    bool IsInRole(string role);
}

IPrincipal提供了兩個基礎數據和方法,一個是獲取一個Identity對象,一個是判斷是否是某個角色。

2.1 Identity

在ClaimPrincipal中,Identity屬性的預設取值邏輯是:

if (identities == null)
{
    throw new ArgumentNullException(nameof(identities));
}

foreach (ClaimsIdentity identity in identities)
{
    if (identity != null)
    {
        return identity;
    }
}

return null;

也就是獲取Principal中第一個不為Null的Identity對象,這個取值邏輯可以通過下麵的屬性進行修改:

public static Func<IEnumerable<ClaimsIdentity>, ClaimsIdentity?> PrimaryIdentitySelector { get; set; }

2.2 IsInRole

在Principal中,通常會存放一至多個Identity對象,每個 Identity對象有一至多個Claim對象。當有Claim對象的Type 值與Identity對象的:

public string RoleClaimType { get; }

值一致時,就會被認為該Claim裡面存放著角色信息,這時候會通過傳入的role值與Claim的Value進行比較。

比較的方法是Identity的實例方法HasClaim:

public virtual bool HasClaim(string type, string value);

如果初始化Identity時,沒有手動設置roleType參數,那麼這個參數取值就是:

public const string DefaultRoleClaimType = ClaimTypes.Role;

通常情況下,不會單獨設置roleType。

2.3 IsAuthenticated 判斷是否登錄

這個屬性並不是ClaimPrincipal的,而是ClaimIdentity的。通常在asp.net core 中會使用這個屬性判斷訪問者是否完成了身份校驗。這個屬性的判斷邏輯也很簡單:

public virtual bool IsAuthenticated
{
    get { return !string.IsNullOrEmpty(AuthenticationType); }
}

也就是說,在Identity中指定了AuthenticationType就會認為完成了身份校驗。

通常的使用方式:

User.Identity.IsAuthenticated

通過以上調用鏈進行數據調用。

2.4 Name

與IsAuthenticatedy一樣,這個屬性也是ClaimIdentity的。與IsInRole的判斷依據類似,這個屬性會獲取Identity中存放的Claim集合中第一個RoleType為ClaimType.Name的Claim,然後取值。

所以,在實現登錄的時候,如果想要能夠通過:

User.Identity.Name

獲取一個用戶名信息或者其他名稱信息的話,則需要設置一個Type等於:

public const string DefaultNameClaimType = ClaimTypes.Name;

的Claim實例對象。

2.5 獲取Claim

在Principal體系中,最重要也是最基礎的數據就是Claim對象。對於ClaimPrincipal對象來說,裡面必然會存放多個Claim對象。那麼,我們就需要有操作Claim對象的方法:

public virtual IEnumerable<Claim> Claims { get; }

通過這個方法可以獲得ClaimPrincipal里所有的Claim對象,這是一個迭代器對象。

public virtual IEnumerable<Claim> FindAll(Predicate<Claim> match);

通過一個選擇器篩選出符合條件的Claim集合。

public virtual IEnumerable<Claim> FindAll(string type);

查詢所有符合類型的Claim對象。

public virtual Claim FindFirst(string type);

查找第一個Type值與指定值相同的Claim對象。

public virtual bool HasClaim(Predicate<Claim> match);

查詢是否存在符合條件的Claim對象。

public virtual bool HasClaim(string type, string value);

查詢是否有Type和Value屬性均等於指定值的Claim對象。

這些方法都是ClaimPrincipal里的,相對應的ClaimIdentity里也提供了類似的方法這裡就不做介紹了。

3. 總結

這一章介紹瞭如何利用Claim進行用戶信息保存,以及常規的一些使用邏輯。下一章,我們將繼續探索如何利用我們自己設置的Identity以達到我們的目的。

更多內容煩請關註我的博客《高先生小屋》

file


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前言 本文的文字及圖片來源於網路,僅供學習、交流使用,不具有任何商業用途,版權歸原作者所有,如有問題請及時聯繫我們以作處理。 作者:Python進階者 現在在疫情階段,想找一份不錯的工作變得更為困難,很多人會選擇去網上看招聘信息。可是招聘信息有一些是錯綜複雜的。而且不能把全部的信息全部羅列出來,以外 ...
  • 一、單節點實例 單節點實例還是比較簡單的,平時做個測試,寫個小程式如果需要用到緩存的話,啟動一個 Redis 還是很輕鬆的,做為一個 key/value 資料庫也是可以勝任的 二、主從模式(master/slaver) redis 主從模式配置 主從模式: redis 的主從模式,使用非同步複製,sl ...
  • 一、前言 回顧:基於.NetCore3.1系列 —— 認證授權方案之授權揭秘 (上篇) 在上一篇中,主要講解了授權在配置方面的源碼,從添加授權配置開始,我們引入了需要的授權配置選項,而不同的授權要求構建不同的策略方式,從而實現一種自己滿意的授權需求配置要求。 在這一節中,繼續上一篇的內容往下深入瞭解 ...
  • 一、原因 用戶希望後臺維護每日預約送貨/提貨時間配置一鍵生成,不需要過多操作。 二、整理實現思路 1.靈活維護/配置送貨、提貨時間段及允許預約數量的模板,如圖所示: 2.預約日期處理: ① 查找提貨/送貨時間配置的預約日期中最新一條數據大於等於當天日期,則預約日期自增一天; ② 查找提貨/送貨時間配 ...
  • 在與同事討論async/await內部實現的時候,突然想到Task.Yeild()這個函數,為什麼呢,瞭解一點C#async/await內部機制的都知道,在await一個非同步任務(函數)的時候,它會先判斷該Task是否已經完成,如果已經完成,則繼續執行下去,不會返回到調用方,原因是儘量避免線程切換, ...
  • 在WinForm程式中,要移動沒有標題欄的視窗,基本的實現思路是監聽需要拖動視窗內的控制項的滑鼠事件,然後將滑鼠位置發送給視窗進行相應的位移就可以了。通過借用Windows API也可以很容易實現這一點,比如像下麵這樣。 public class Win32Api { public const int ...
  • 想深入瞭解OrchadCore源碼許久了,但是讀源碼的時候遇到很多問題而網上的參考資料太少了(幾乎都是OrchadCms不帶OrchardCore的),現在解決得差不多了,做下筆記方便自己查看,有錯誤之處也請大家幫忙指出,謝謝。 OrchardCore是一個基於asp.net core的cms(廢話 ...
  • 場景 ASP.NET中新建Web網站並部署到IIS上(詳細圖文教程): https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/107199747 在上面博客中已經將網站部署到了IIS上。 怎樣對前端的請求進行響應普通文本和JSON數據以及 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...