- Claim
- ClaimTypes
- ClaimValueTypes
// 表示声明
public class Claim
{
// 用于保存声明的二进制序列化数据
private readonly byte[]? _userSerializationData;
// 声明的当前颁发者
private readonly string _issuer;
// 声明的最初颁发者
private readonly string _originalIssuer;
// 用于存储额外信息的数据字典
private Dictionary<string, string>? _properties;
// 声明的身份主体
private readonly ClaimsIdentity? _subject;
// 声明类型
// 使用 URI 格式的字符串作为声明类型
private readonly string _type;
// 声明值
private readonly string _value;
// 声明值的实际类型
// 使用类型的字符串名称
private readonly string _valueType;
// 使用声明类型和声明值
public Claim(string type, string value)
: this(type, value, ClaimValueTypes.String, ClaimsIdentity.DefaultIssuer, ClaimsIdentity.DefaultIssuer, (ClaimsIdentity?)null)
{
}
// 使用声明类型、声明值和声明值的实际类型
public Claim(string type, string value, string? valueType)
: this(type, value, valueType, ClaimsIdentity.DefaultIssuer, ClaimsIdentity.DefaultIssuer, (ClaimsIdentity?)null)
{
}
// 使用声明类型、声明值、声明值的实际类型和声明的颁发者(当前颁发者和最初颁发者相同)
public Claim(string type, string value, string? valueType, string? issuer)
: this(type, value, valueType, issuer, issuer, (ClaimsIdentity?)null)
{
}
// 使用声明类型、声明值、声明值的实际类型、声明的当前颁发者和声明的最初颁发者
public Claim(string type, string value, string? valueType, string? issuer, string? originalIssuer)
: this(type, value, valueType, issuer, originalIssuer, (ClaimsIdentity?)null)
{
}
// 使用声明类型、声明值、声明值的实际类型、声明的当前颁发者、声明的最初颁发者和声明的身份主体
public Claim(string type, string value, string? valueType, string? issuer, string? originalIssuer, ClaimsIdentity? subject)
: this(type, value, valueType, issuer, originalIssuer, subject, null, null)
{
}
// 内部最终都会调用这个构造函数初始化声明
internal Claim(string type, string value, string? valueType, string? issuer, string? originalIssuer, ClaimsIdentity? subject, string? propertyKey, string? propertyValue)
{
ArgumentNullException.ThrowIfNull(type);
ArgumentNullException.ThrowIfNull(value);
_type = type;
_value = value;
// 默认的声明值的实际类型使用 string 类型
_valueType = string.IsNullOrEmpty(valueType) ? ClaimValueTypes.String : valueType;
// 默认的声明的当前颁发者使用 ClaimsIdentity.DefaultIssuer 表示的本地认证中心
_issuer = string.IsNullOrEmpty(issuer) ? ClaimsIdentity.DefaultIssuer : issuer;
// 默认的声明的最初颁发者与声明的当前颁发者相同
_originalIssuer = string.IsNullOrEmpty(originalIssuer) ? _issuer : originalIssuer;
_subject = subject;
if (propertyKey != null)
{
_properties = new Dictionary<string, string>();
_properties[propertyKey] = propertyValue!;
}
}
// 克隆时使用的构造函数
// 拷贝声明的所有信息并指定身份主体
protected Claim(Claim other)
: this(other, (other == null ? (ClaimsIdentity?)null : other._subject))
{
}
// 克隆时使用的构造函数
// 拷贝声明的所有信息并指定身份主体
protected Claim(Claim other, ClaimsIdentity? subject)
{
ArgumentNullException.ThrowIfNull(other);
_issuer = other._issuer;
_originalIssuer = other._originalIssuer;
_subject = subject;
_type = other._type;
_value = other._value;
_valueType = other._valueType;
if (other._properties != null)
{
_properties = new Dictionary<string, string>(other._properties);
}
if (other._userSerializationData != null)
{
_userSerializationData = other._userSerializationData.Clone() as byte[];
}
}
public string Issuer
{
get { return _issuer; }
}
public string OriginalIssuer
{
get { return _originalIssuer; }
}
public IDictionary<string, string> Properties => _properties ??= new Dictionary<string, string>();
public ClaimsIdentity? Subject
{
get { return _subject; }
}
public string Type
{
get { return _type; }
}
public string Value
{
get { return _value; }
}
public string ValueType
{
get { return _valueType; }
}
public virtual Claim Clone()
{
return Clone((ClaimsIdentity?)null);
}
// 克隆声明并指定身份主体
public virtual Claim Clone(ClaimsIdentity? identity)
{
return new Claim(this, identity);
}
}
// 提供声明类型常量
public static class ClaimTypes
{
internal const string ClaimTypeNamespace = "http://schemas.microsoft.com/ws/2008/06/identity/claims";
public const string AuthenticationInstant = ClaimTypeNamespace + "/authenticationinstant";
public const string AuthenticationMethod = ClaimTypeNamespace + "/authenticationmethod";
public const string CookiePath = ClaimTypeNamespace + "/cookiepath";
public const string DenyOnlyPrimarySid = ClaimTypeNamespace + "/denyonlyprimarysid";
public const string DenyOnlyPrimaryGroupSid = ClaimTypeNamespace + "/denyonlyprimarygroupsid";
public const string DenyOnlyWindowsDeviceGroup = ClaimTypeNamespace + "/denyonlywindowsdevicegroup";
public const string Dsa = ClaimTypeNamespace + "/dsa";
public const string Expiration = ClaimTypeNamespace + "/expiration";
public const string Expired = ClaimTypeNamespace + "/expired";
public const string GroupSid = ClaimTypeNamespace + "/groupsid";
public const string IsPersistent = ClaimTypeNamespace + "/ispersistent";
public const string PrimaryGroupSid = ClaimTypeNamespace + "/primarygroupsid";
public const string PrimarySid = ClaimTypeNamespace + "/primarysid";
// 用户角色
public const string Role = ClaimTypeNamespace + "/role";
public const string SerialNumber = ClaimTypeNamespace + "/serialnumber";
public const string UserData = ClaimTypeNamespace + "/userdata";
public const string Version = ClaimTypeNamespace + "/version";
public const string WindowsAccountName = ClaimTypeNamespace + "/windowsaccountname";
public const string WindowsDeviceClaim = ClaimTypeNamespace + "/windowsdeviceclaim";
public const string WindowsDeviceGroup = ClaimTypeNamespace + "/windowsdevicegroup";
public const string WindowsUserClaim = ClaimTypeNamespace + "/windowsuserclaim";
public const string WindowsFqbnVersion = ClaimTypeNamespace + "/windowsfqbnversion";
public const string WindowsSubAuthority = ClaimTypeNamespace + "/windowssubauthority";
internal const string ClaimType2005Namespace = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims";
public const string Anonymous = ClaimType2005Namespace + "/anonymous";
public const string Authentication = ClaimType2005Namespace + "/authentication";
public const string AuthorizationDecision = ClaimType2005Namespace + "/authorizationdecision";
public const string Country = ClaimType2005Namespace + "/country";
public const string DateOfBirth = ClaimType2005Namespace + "/dateofbirth";
public const string Dns = ClaimType2005Namespace + "/dns";
public const string DenyOnlySid = ClaimType2005Namespace + "/denyonlysid";
// 用户电子邮件
public const string Email = ClaimType2005Namespace + "/emailaddress";
// 用户性别
public const string Gender = ClaimType2005Namespace + "/gender";
// 用户名字
public const string GivenName = ClaimType2005Namespace + "/givenname";
public const string Hash = ClaimType2005Namespace + "/hash";
// 用户家庭电话
public const string HomePhone = ClaimType2005Namespace + "/homephone";
public const string Locality = ClaimType2005Namespace + "/locality";
// 用户手机号
public const string MobilePhone = ClaimType2005Namespace + "/mobilephone";
// 用户名
public const string Name = ClaimType2005Namespace + "/name";
public const string NameIdentifier = ClaimType2005Namespace + "/nameidentifier";
public const string OtherPhone = ClaimType2005Namespace + "/otherphone";
// 用户地址邮编
public const string PostalCode = ClaimType2005Namespace + "/postalcode";
public const string Rsa = ClaimType2005Namespace + "/rsa";
public const string Sid = ClaimType2005Namespace + "/sid";
public const string Spn = ClaimType2005Namespace + "/spn";
public const string StateOrProvince = ClaimType2005Namespace + "/stateorprovince";
// 用户地址
public const string StreetAddress = ClaimType2005Namespace + "/streetaddress";
// 用户姓氏
public const string Surname = ClaimType2005Namespace + "/surname";
public const string System = ClaimType2005Namespace + "/system";
public const string Thumbprint = ClaimType2005Namespace + "/thumbprint";
public const string Upn = ClaimType2005Namespace + "/upn";
public const string Uri = ClaimType2005Namespace + "/uri";
public const string Webpage = ClaimType2005Namespace + "/webpage";
public const string X500DistinguishedName = ClaimType2005Namespace + "/x500distinguishedname";
internal const string ClaimType2009Namespace = "http://schemas.xmlsoap.org/ws/2009/09/identity/claims";
public const string Actor = ClaimType2009Namespace + "/actor";
}
// 提供声明值的实际类型常量
public static class ClaimValueTypes
{
private const string XmlSchemaNamespace = "http://www.w3.org/2001/XMLSchema";
public const string Base64Binary = XmlSchemaNamespace + "#base64Binary";
public const string Base64Octet = XmlSchemaNamespace + "#base64Octet";
public const string Boolean = XmlSchemaNamespace + "#boolean";
public const string Date = XmlSchemaNamespace + "#date";
public const string DateTime = XmlSchemaNamespace + "#dateTime";
public const string Double = XmlSchemaNamespace + "#double";
public const string Fqbn = XmlSchemaNamespace + "#fqbn";
public const string HexBinary = XmlSchemaNamespace + "#hexBinary";
public const string Integer = XmlSchemaNamespace + "#integer";
public const string Integer32 = XmlSchemaNamespace + "#integer32";
public const string Integer64 = XmlSchemaNamespace + "#integer64";
public const string Sid = XmlSchemaNamespace + "#sid";
public const string String = XmlSchemaNamespace + "#string";
public const string Time = XmlSchemaNamespace + "#time";
public const string UInteger32 = XmlSchemaNamespace + "#uinteger32";
public const string UInteger64 = XmlSchemaNamespace + "#uinteger64";
private const string SoapSchemaNamespace = "http://schemas.xmlsoap.org/";
public const string DnsName = SoapSchemaNamespace + "claims/dns";
public const string Email = SoapSchemaNamespace + "ws/2005/05/identity/claims/emailaddress";
public const string Rsa = SoapSchemaNamespace + "ws/2005/05/identity/claims/rsa";
public const string UpnName = SoapSchemaNamespace + "claims/UPN";
private const string XmlSignatureConstantsNamespace = "http://www.w3.org/2000/09/xmldsig#";
public const string DsaKeyValue = XmlSignatureConstantsNamespace + "DSAKeyValue";
public const string KeyInfo = XmlSignatureConstantsNamespace + "KeyInfo";
public const string RsaKeyValue = XmlSignatureConstantsNamespace + "RSAKeyValue";
private const string XQueryOperatorsNameSpace = "http://www.w3.org/TR/2002/WD-xquery-operators-20020816";
public const string DaytimeDuration = XQueryOperatorsNameSpace + "#dayTimeDuration";
public const string YearMonthDuration = XQueryOperatorsNameSpace + "#yearMonthDuration";
private const string Xacml10Namespace = "urn:oasis:names:tc:xacml:1.0";
public const string Rfc822Name = Xacml10Namespace + ":data-type:rfc822Name";
public const string X500Name = Xacml10Namespace + ":data-type:x500Name";
}
// 表示身份的接口
public interface IIdentity
{
// 身份名称
// 一般用于表示用户的用户名
string? Name { get; }
// 采用的认证类型
string? AuthenticationType { get; }
// 是否通过认证
bool IsAuthenticated { get; }
}
// 表示携带声明的身份
// 本质是对一组声明的封装
public class ClaimsIdentity : IIdentity
{
// 用于保存身份的二进制序列化数据
private byte[]? _userSerializationData;
// 在使用身份委托时,用来保存被委托人的身份
private ClaimsIdentity? _actor;
// 在使用身份委托时,用来保存被委托人的安全令牌
// 委托过程中被委托人的用户凭证会被封装到一个叫作安全令牌的可序列化对象中
// 具体使用哪种安全令牌取决于采用的认证类型
private object? _bootstrapContext;
// 外部声明集合
private List<List<Claim>>? _externalClaims;
private string? _label;
// 内部声明集合
// 通过 AddClaim 和 AddClaims 方法添加的声明都会被添加到这个集合中
private readonly List<Claim> _instanceClaims = new List<Claim>();
// 采用的认证类型
private string? _authenticationType;
// 针对用户名的声明类型
// 默认使用 ClaimTypes.Name
private string _nameClaimType = DefaultNameClaimType;
// 针对用户角色的声明类型
// 默认使用 ClaimTypes.Role
private string _roleClaimType = DefaultRoleClaimType;
// 默认颁发者
// 可以理解为本地认证中心
public const string DefaultIssuer = @"LOCAL AUTHORITY";
// 默认用户名声明类型
public const string DefaultNameClaimType = ClaimTypes.Name;
// 默认用户角色声明类型
public const string DefaultRoleClaimType = ClaimTypes.Role;
public ClaimsIdentity()
: this((IIdentity?)null, (IEnumerable<Claim>?)null, (string?)null, (string?)null, (string?)null)
{
}
// 使用传入的身份
public ClaimsIdentity(IIdentity? identity)
: this(identity, (IEnumerable<Claim>?)null, (string?)null, (string?)null, (string?)null)
{
}
// 使用认证类型
public ClaimsIdentity(string? authenticationType)
: this((IIdentity?)null, (IEnumerable<Claim>?)null, authenticationType, (string?)null, (string?)null)
{
}
// 使用声明集合和认证类型
public ClaimsIdentity(IEnumerable<Claim>? claims, string? authenticationType)
: this((IIdentity?)null, claims, authenticationType, (string?)null, (string?)null)
{
}
//使用传入的身份、声明集合
public ClaimsIdentity(IIdentity? identity, IEnumerable<Claim>? claims)
: this(identity, claims, (string?)null, (string?)null, (string?)null)
{
}
// 使用认证类型,用户名声明类型和用户角色声明类型
public ClaimsIdentity(string? authenticationType, string? nameType, string? roleType)
: this((IIdentity?)null, (IEnumerable<Claim>?)null, authenticationType, nameType, roleType)
{
}
// 使用声明集合、认证类型、用户名声明类型和用户角色声明类型
public ClaimsIdentity(IEnumerable<Claim>? claims, string? authenticationType, string? nameType, string? roleType)
: this((IIdentity?)null, claims, authenticationType, nameType, roleType)
{
}
// 内部最终都会调用这个构造函数初始化身份
public ClaimsIdentity(IIdentity? identity, IEnumerable<Claim>? claims, string? authenticationType, string? nameType, string? roleType)
{
// 检查身份类型是否是 ClaimsIdentity 类型
ClaimsIdentity? claimsIdentity = identity as ClaimsIdentity;
_authenticationType = (identity != null && string.IsNullOrEmpty(authenticationType)) ? identity.AuthenticationType :authenticationType;
_nameClaimType = !string.IsNullOrEmpty(nameType) ? nameType : (claimsIdentity != null ? claimsIdentity._nameClaimType :DefaultNameClaimType);
_roleClaimType = !string.IsNullOrEmpty(roleType) ? roleType : (claimsIdentity != null ? claimsIdentity._roleClaimType :DefaultRoleClaimType);
// 如果传入的 IIdentity 是 ClaimIdentity 类型
if (claimsIdentity != null)
{
_label = claimsIdentity._label;
// 使用传入的 ClaimIdentity 中的安全令牌
_bootstrapContext = claimsIdentity._bootstrapContext;
if (claimsIdentity.Actor != null)
{
if (!IsCircular(claimsIdentity.Actor))
{
// 使用传入的 ClaimIdentity 中的被委托人的身份
_actor = claimsIdentity.Actor;
}
else
{
throw new InvalidOperationException(SR.InvalidOperationException_ActorGraphCircular);
}
}
// 将传入的 ClaimIdentity 中的声明集合添加到内部声明集合中
SafeAddClaims(claimsIdentity._instanceClaims);
}
else
{
// 不是 ClaimIdentity 类型并且 IIdentity.Name 不为空则添加一个用户名声明
if (identity != null && !string.IsNullOrEmpty(identity.Name))
{
SafeAddClaim(new Claim(_nameClaimType, identity.Name, ClaimValueTypes.String, DefaultIssuer, DefaultIssuer, this));
}
}
// 将声明集合添加到内部声明集合中
if (claims != null)
{
SafeAddClaims(claims);
}
}
// 使用传入的身份
// 克隆时使用的构造函数
protected ClaimsIdentity(ClaimsIdentity other)
{
ArgumentNullException.ThrowIfNull(other);
if (other._actor != null)
{
_actor = other._actor.Clone();
}
_authenticationType = other._authenticationType;
_bootstrapContext = other._bootstrapContext;
_label = other._label;
_nameClaimType = other._nameClaimType;
_roleClaimType = other._roleClaimType;
if (other._userSerializationData != null)
{
_userSerializationData = other._userSerializationData.Clone() as byte[];
}
SafeAddClaims(other._instanceClaims);
}
// 从声明集合中查找用户名声明并返回表示用户名的声明值
public virtual string? Name
{
get
{
// 利用用户名声明类型查找声明
Claim? claim = FindFirst(_nameClaimType);
if (claim != null)
{
return claim.Value;
}
return null;
}
}
// 返回认证类型
public virtual string? AuthenticationType
{
get { return _authenticationType; }
}
// ClaimsIdentity 判断是否通过认证取决于认证类型是否为空
public virtual bool IsAuthenticated
{
get { return !string.IsNullOrEmpty(_authenticationType); }
}
// 返回声明集合
public virtual IEnumerable<Claim> Claims
{
get
{
if (_externalClaims == null)
{
return _instanceClaims;
}
return CombinedClaimsIterator();
}
}
// 添加或得到被委托人的身份
public ClaimsIdentity? Actor
{
get { return _actor; }
set
{
if (value != null)
{
if (IsCircular(value))
{
throw new InvalidOperationException(SR.InvalidOperationException_ActorGraphCircular);
}
}
_actor = value;
}
}
// 返回用户名声明类型
public string NameClaimType
{
get { return _nameClaimType; }
}
// 返回用户角色声明类型
public string RoleClaimType
{
get { return _roleClaimType; }
}
// 克隆身份
public virtual ClaimsIdentity Clone()
{
return new ClaimsIdentity(this);
}
// 返回合并后的声明集合
private IEnumerable<Claim> CombinedClaimsIterator()
{
for (int i = 0; i < _instanceClaims.Count; i++)
{
yield return _instanceClaims[i];
}
for (int j = 0; j < _externalClaims!.Count; j++)
{
if (_externalClaims[j] != null)
{
foreach (Claim claim in _externalClaims[j])
{
yield return claim;
}
}
}
}
// 添加声明
// 注意:
// 如果声明的身份主体不是当前身份
// 则会克隆声明并指定当前身份作为克隆声明的身份主体
public virtual void AddClaim(Claim claim)
{
ArgumentNullException.ThrowIfNull(claim);
if (object.ReferenceEquals(claim.Subject, this))
{
_instanceClaims.Add(claim);
}
else
{
_instanceClaims.Add(claim.Clone(this));
}
}
// 添加声明集合
public virtual void AddClaims(IEnumerable<Claim?> claims)
{
ArgumentNullException.ThrowIfNull(claims);
foreach (Claim? claim in claims)
{
if (claim == null)
{
continue;
}
if (object.ReferenceEquals(claim.Subject, this))
{
_instanceClaims.Add(claim);
}
else
{
_instanceClaims.Add(claim.Clone(this));
}
}
}
// 尝试删除一个声明
// 只是删除相同引用的声明
public virtual bool TryRemoveClaim(Claim? claim)
{
if (claim == null)
{
return false;
}
bool removed = false;
for (int i = 0; i < _instanceClaims.Count; i++)
{
if (object.ReferenceEquals(_instanceClaims[i], claim))
{
_instanceClaims.RemoveAt(i);
removed = true;
break;
}
}
return removed;
}
// 确保删除一个声明,否则抛出 InvalidOperationException 异常
public virtual void RemoveClaim(Claim? claim)
{
if (!TryRemoveClaim(claim))
{
throw new InvalidOperationException(SR.Format(SR.InvalidOperation_ClaimCannotBeRemoved, claim));
}
}
// 使用断言查找所有匹配的声明
public virtual IEnumerable<Claim> FindAll(Predicate<Claim> match)
{
ArgumentNullException.ThrowIfNull(match);
return Core(match);
IEnumerable<Claim> Core(Predicate<Claim> match)
{
foreach (Claim claim in Claims)
{
if (match(claim))
{
yield return claim;
}
}
}
}
// 利用声明类型查找所有相同声明类型的声明(忽略声明类型大小写)
public virtual IEnumerable<Claim> FindAll(string type)
{
ArgumentNullException.ThrowIfNull(type);
return Core(type);
IEnumerable<Claim> Core(string type)
{
foreach (Claim claim in Claims)
{
if (claim != null)
{
if (string.Equals(claim.Type, type, StringComparison.OrdinalIgnoreCase))
{
yield return claim;
}
}
}
}
}
// 利用断言查找第一个匹配的声明
public virtual Claim? FindFirst(Predicate<Claim> match)
{
ArgumentNullException.ThrowIfNull(match);
foreach (Claim claim in Claims)
{
if (match(claim))
{
return claim;
}
}
return null;
}
// 利用声明类型查找第一个相同声明类型的声明(忽略声明类型大小写)
public virtual Claim? FindFirst(string type)
{
ArgumentNullException.ThrowIfNull(type);
foreach (Claim claim in Claims)
{
if (claim != null)
{
if (string.Equals(claim.Type, type, StringComparison.OrdinalIgnoreCase))
{
return claim;
}
}
}
return null;
}
// 利用断言检查是否存在匹配的声明
public virtual bool HasClaim(Predicate<Claim> match)
{
ArgumentNullException.ThrowIfNull(match);
foreach (Claim claim in Claims)
{
if (match(claim))
{
return true;
}
}
return false;
}
// 利用声明类型和声明值检查是否存在相同声明类型和声明值的声明(忽略声明类型大小写但声明值必须完全相同)
public virtual bool HasClaim(string type, string value)
{
ArgumentNullException.ThrowIfNull(type);
ArgumentNullException.ThrowIfNull(value);
foreach (Claim claim in Claims)
{
if (claim != null
&& string.Equals(claim.Type, type, StringComparison.OrdinalIgnoreCase)
&& string.Equals(claim.Value, value, StringComparison.Ordinal))
{
return true;
}
}
return false;
}
}
// 表示通用身份
public class GenericIdentity : ClaimsIdentity
{
// 用户名
private readonly string m_name;
// 认证类型
private readonly string m_type;
// 使用用户名
public GenericIdentity(string name)
{
ArgumentNullException.ThrowIfNull(name);
m_name = name;
m_type = "";
// 添加用户名声明
AddNameClaim();
}
// 使用用户名和认证类型
public GenericIdentity(string name, string type)
{
ArgumentNullException.ThrowIfNull(name);
ArgumentNullException.ThrowIfNull(type);
m_name = name;
m_type = type;
// 添加用户名声明
AddNameClaim();
}
// 使用传入的身份
// 主要是克隆时使用
protected GenericIdentity(GenericIdentity identity)
: base(identity)
{
m_name = identity.m_name;
m_type = identity.m_type;
}
// 克隆身份
public override ClaimsIdentity Clone()
{
return new GenericIdentity(this);
}
// 返回声明集合
// 没有意义,不需要重写
public override IEnumerable<Claim> Claims
{
get
{
return base.Claims;
}
}
// 返回用户名
public override string Name
{
get
{
return m_name;
}
}
// 返回认证类型
public override string AuthenticationType
{
get
{
return m_type;
}
}
// GenericIdentity 重写了 IsAuthenticated 属性
// 判断是否通过认证取决于用户名是否为空
public override bool IsAuthenticated
{
get
{
return !m_name.Equals("");
}
}
// 添加用户名声明
private void AddNameClaim()
{
if (m_name != null)
{
base.AddClaim(new Claim(base.NameClaimType, m_name, ClaimValueTypes.String, ClaimsIdentity.DefaultIssuer, ClaimsIdentity.DefaultIssuer, this));
}
}
}
// 表示用户(当事人)的接口
public interface IPrincipal
{
// 用户的主要身份
IIdentity? Identity { get; }
// 检查用户是否在指定角色中
bool IsInRole(string role);
}
// 表示持有 ClaimsIdentity 身份的用户
public class ClaimsPrincipal : IPrincipal
{
// 用户的身份集合
// 每个用户可以拥有多个身份
private readonly List<ClaimsIdentity> _identities = new List<ClaimsIdentity>();
// 用户的主要身份选择器
// 每个用户需要确定一个主要身份
private static Func<IEnumerable<ClaimsIdentity>, ClaimsIdentity?> s_identitySelector = SelectPrimaryIdentity;
public ClaimsPrincipal()
{
}
// 使用身份集合
public ClaimsPrincipal(IEnumerable<ClaimsIdentity> identities)
{
ArgumentNullException.ThrowIfNull(identities);
_identities.AddRange(identities);
}
// 使用身份
// 如果不是 ClaimsIdentity 类型
// 则需要利用原身份创建一个新的 ClaimsIdentity 身份
public ClaimsPrincipal(IIdentity identity)
{
ArgumentNullException.ThrowIfNull(identity);
if (identity is ClaimsIdentity ci)
{
_identities.Add(ci);
}
else
{
_identities.Add(new ClaimsIdentity(identity));
}
}
// 使用传入的用户
// 克隆时使用的构造函数
public ClaimsPrincipal(IPrincipal principal)
{
ArgumentNullException.ThrowIfNull(principal);
ClaimsPrincipal? cp = principal as ClaimsPrincipal;
if (null == cp)
{
// 如果不是 ClaimsPrincipal 类型
// 则使用 IPrincipal.Identity 表示的主要身份创建一个新的 ClaimsIdentity 身份
_identities.Add(new ClaimsIdentity(principal.Identity));
}
else
{
if (null != cp.Identities)
{
_identities.AddRange(cp.Identities);
}
}
}
// 返回所有 ClaimsIdentity 身份的 Claim 集合中的声明
// 所以 ClaimsPrincipal 同样拥有 FindAll、FindFirst、HasClaim 等方法
// 但没有 AddClaim、AddClaims、RemoveClaim、TryRemoveClaim 等方法
public virtual IEnumerable<Claim> Claims
{
get
{
foreach (ClaimsIdentity identity in Identities)
{
foreach (Claim claim in identity.Claims)
{
yield return claim;
}
}
}
}
// 返回身份集合
public virtual IEnumerable<ClaimsIdentity> Identities
{
get
{
return _identities;
}
}
// 返回主要身份
// 利用主要身份选择器选择主要身份
public virtual System.Security.Principal.IIdentity? Identity
{
get
{
if (s_identitySelector != null)
{
return s_identitySelector(_identities);
}
else
{
return SelectPrimaryIdentity(_identities);
}
}
}
// 检查用户是否在指定角色中
public virtual bool IsInRole(string role)
{
// 遍历 ClaimsIdentity 身份集合
for (int i = 0; i < _identities.Count; i++)
{
if (_identities[i] != null)
{
// 检查声明类型和声明值
if (_identities[i].HasClaim(_identities[i].RoleClaimType, role))
{
return true;
}
}
}
return false;
}
// 添加身份
public virtual void AddIdentity(ClaimsIdentity identity)
{
ArgumentNullException.ThrowIfNull(identity);
_identities.Add(identity);
}
// 添加身份集合
public virtual void AddIdentities(IEnumerable<ClaimsIdentity> identities)
{
ArgumentNullException.ThrowIfNull(identities);
_identities.AddRange(identities);
}
// 选择主要身份
// 返回 ClaimsIdentity 身份集合中第一个不为 null 的身份
private static ClaimsIdentity? SelectPrimaryIdentity(IEnumerable<ClaimsIdentity> identities)
{
ArgumentNullException.ThrowIfNull(identities);
foreach (ClaimsIdentity identity in identities)
{
if (identity != null)
{
return identity;
}
}
return null;
}
// 返回主要身份选择器
public static Func<IEnumerable<ClaimsIdentity>, ClaimsIdentity?> PrimaryIdentitySelector
{
get
{
return s_identitySelector;
}
set
{
s_identitySelector = value;
}
}
// 克隆用户
public virtual ClaimsPrincipal Clone()
{
return new ClaimsPrincipal(this);
}
}
// 表示通用用户
public class GenericPrincipal : ClaimsPrincipal
{
// 用户的主要身份
private readonly IIdentity m_identity;
// 角色集合
private readonly string[]? m_roles;
// 使用身份和角色集合
public GenericPrincipal(IIdentity identity, string[]? roles)
{
ArgumentNullException.ThrowIfNull(identity);
// 保存原始身份
m_identity = identity;
if (roles != null)
{
m_roles = (string[])roles.Clone();
}
else
{
m_roles = null;
}
AddIdentityWithRoles(m_identity, m_roles);
}
// 向 ClaimsIdentity 身份添加用户角色声明
// 无论 IIdentity 是否是 ClaimsIdentity 类型,最终都会创建一个新的 ClaimsIdentity 身份
// 如果 IIdentity 是 ClaimsIdentity 类型,则克隆一个 ClaimsIdentity 身份
// 如果 IIdentity 不是 ClaimsIdentity 类型,则利用原始身份创建一个 ClaimsIdentity 身份
// 注意:
// 创建的用户角色声明的身份主体是新创建的 ClaimsIdentity 身份,不是原始的身份
private void AddIdentityWithRoles(IIdentity identity, string[]? roles)
{
// 确保得到一个 ClaimsIdentity 身份
if (identity is ClaimsIdentity claimsIdentity)
{
claimsIdentity = claimsIdentity.Clone();
}
else
{
claimsIdentity = new ClaimsIdentity(identity);
}
// 每个角色创建一个用户角色声明
if (roles != null && roles.Length > 0)
{
List<Claim> roleClaims = new List<Claim>(roles.Length);
foreach (string role in roles)
{
if (!string.IsNullOrWhiteSpace(role))
{
roleClaims.Add(new Claim(claimsIdentity.RoleClaimType, role, ClaimValueTypes.String, ClaimsIdentity.DefaultIssuer, ClaimsIdentity.DefaultIssuer, claimsIdentity));
}
}
// 将用户角色声明添加到外部声明集合
claimsIdentity.ExternalClaims.Add(roleClaims);
}
// 将新创建的 ClaimsIdentity 身份添加到身份集合中
base.AddIdentity(claimsIdentity);
}
// 永远返回 m_identity 字段保存的主要身份
public override IIdentity Identity
{
get { return m_identity; }
}
// 优先检查 m_roles 字段保存的角色集合
public override bool IsInRole(string? role)
{
// 角色集合如果为 null,则返回 false
if (role == null || m_roles == null)
return false;
for (int i = 0; i < m_roles.Length; ++i)
{
if (string.Equals(m_roles[i], role, StringComparison.OrdinalIgnoreCase))
return true;
}
// 没有匹配成功再继续检查声明集合
return base.IsInRole(role);
}
}
// 认证票据
// 本质是对一个 ClaimsPrincipal 的封装
public class AuthenticationTicket
{
public AuthenticationTicket(ClaimsPrincipal principal, AuthenticationProperties? properties, string authenticationScheme)
{
ArgumentNullException.ThrowIfNull(principal);
AuthenticationScheme = authenticationScheme;
Principal = principal;
Properties = properties ?? new AuthenticationProperties();
}
public AuthenticationTicket(ClaimsPrincipal principal, string authenticationScheme)
: this(principal, properties: null, authenticationScheme: authenticationScheme)
{ }
// 认证方案
public string AuthenticationScheme { get; }
// 用户
public ClaimsPrincipal Principal { get; }
// 认证会话上下文
public AuthenticationProperties Properties { get; }
// 克隆认证票据
public AuthenticationTicket Clone()
{
var principal = new ClaimsPrincipal();
foreach (var identity in Principal.Identities)
{
// 克隆身份
principal.AddIdentity(identity.Clone());
}
return new AuthenticationTicket(principal, Properties.Clone(), AuthenticationScheme);
}
}
// 认证会话上下文
// 提供认证会话的各种属性
public class AuthenticationProperties
{
internal const string IssuedUtcKey = ".issued";
internal const string ExpiresUtcKey = ".expires";
internal const string IsPersistentKey = ".persistent";
internal const string RedirectUriKey = ".redirect";
internal const string RefreshKey = ".refresh";
// 颁发时间与过期时间保存为 RFC1123 格式的字符串
// 这种格式总是以协调世界时(UTC)表示时间,并以 GMT 结尾
internal const string UtcDateTimeFormat = "r";
public AuthenticationProperties()
: this(items: null, parameters: null)
{ }
[JsonConstructor]
public AuthenticationProperties(IDictionary<string, string?> items)
: this(items, parameters: null)
{ }
public AuthenticationProperties(IDictionary<string, string?>? items, IDictionary<string, object?>? parameters)
{
Items = items ?? new Dictionary<string, string?>(StringComparer.Ordinal);
Parameters = parameters ?? new Dictionary<string, object?>(StringComparer.Ordinal);
}
public AuthenticationProperties Clone()
=> new AuthenticationProperties(
new Dictionary<string, string?>(Items, StringComparer.Ordinal),
new Dictionary<string, object?>(Parameters, StringComparer.Ordinal));
// 认证会话上下文的共享字典
public IDictionary<string, string?> Items { get; }
[JsonIgnore]
public IDictionary<string, object?> Parameters { get; }
// 认证票据是否允许被客户端持久化保存
[JsonIgnore]
public bool IsPersistent
{
// 此处应该调用 GetBool 方法
get => GetString(IsPersistentKey) != null;
// 此处应该调用 SetBool 方法
set => SetString(IsPersistentKey, value ? string.Empty : null);
}
// 重定向地址
// 不同情况下设置这个地址实现不同的功能
// 比如登录成功后重定向到主页,注销后重定向到登录页等
[JsonIgnore]
public string? RedirectUri
{
get => GetString(RedirectUriKey);
set => SetString(RedirectUriKey, value);
}
// 认证票据的颁发时间
[JsonIgnore]
public DateTimeOffset? IssuedUtc
{
get => GetDateTimeOffset(IssuedUtcKey);
set => SetDateTimeOffset(IssuedUtcKey, value);
}
// 认证票据的过期时间
[JsonIgnore]
public DateTimeOffset? ExpiresUtc
{
get => GetDateTimeOffset(ExpiresUtcKey);
set => SetDateTimeOffset(ExpiresUtcKey, value);
}
// 认证票据是否允许自动刷新
[JsonIgnore]
public bool? AllowRefresh
{
get => GetBool(RefreshKey);
set => SetBool(RefreshKey, value);
}
// 从共享字典得到值
public string? GetString(string key)
{
return Items.TryGetValue(key, out var value) ? value : null;
}
// 向共享字典设置值
public void SetString(string key, string? value)
{
if (value != null)
{
Items[key] = value;
}
else
{
Items.Remove(key);
}
}
public T? GetParameter<T>(string key)
=> Parameters.TryGetValue(key, out var obj) && obj is T value ? value : default;
public void SetParameter<T>(string key, T value)
=> Parameters[key] = value;
protected bool? GetBool(string key)
{
if (Items.TryGetValue(key, out var value) && bool.TryParse(value, out var boolValue))
{
return boolValue;
}
return null;
}
protected void SetBool(string key, bool? value)
{
if (value.HasValue)
{
Items[key] = value.GetValueOrDefault().ToString();
}
else
{
Items.Remove(key);
}
}
protected DateTimeOffset? GetDateTimeOffset(string key)
{
if (Items.TryGetValue(key, out var value)
&& DateTimeOffset.TryParseExact(value, UtcDateTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out var dateTimeOffset))
{
return dateTimeOffset;
}
return null;
}
protected void SetDateTimeOffset(string key, DateTimeOffset? value)
{
if (value.HasValue)
{
Items[key] = value.GetValueOrDefault().ToString(UtcDateTimeFormat, CultureInfo.InvariantCulture);
}
else
{
Items.Remove(key);
}
}
}
// 安全数据格式化接口
// 主要功能是格式化数据(加密)和反格式化数据(解密)
public interface ISecureDataFormat<TData>
{
// 格式化数据(加密)
string Protect(TData data);
// 格式化数据(加密)
// 可以提供一个用途字符串
string Protect(TData data, string? purpose);
// 反格式化数据(解密)
TData? Unprotect(string? protectedText);
// 反格式化数据(解密)
// 可以提供一个用途字符串
TData? Unprotect(string? protectedText, string? purpose);
}
// ISecureDataFormat<> 的默认实现
// 内部实现需要借助序列化器和密码器来完成数据的格式化
public class SecureDataFormat<TData> : ISecureDataFormat<TData>
{
// 序列化器
private readonly IDataSerializer<TData> _serializer;
// 密码器
private readonly IDataProtector _protector;
public SecureDataFormat(IDataSerializer<TData> serializer, IDataProtector protector)
{
_serializer = serializer;
_protector = protector;
}
// 加密数据
public string Protect(TData data)
{
return Protect(data, purpose: null);
}
// 加密数据
// 步骤如下:
// 1. 利用序列化器对数据进行二进制序列化得到字节数组
// 2. 如果提供了用途字符串,则根据用途字符串创建一个新的密码器
// 3. 利用密码器对字节数组进行加密
// 4. 对加密后的字节数组进行 Base64 编码,并返回编码后的字符串
public string Protect(TData data, string? purpose)
{
var userData = _serializer.Serialize(data);
var protector = _protector;
if (!string.IsNullOrEmpty(purpose))
{
protector = protector.CreateProtector(purpose);
}
var protectedData = protector.Protect(userData);
return Base64UrlTextEncoder.Encode(protectedData);
}
// 解密数据
public TData? Unprotect(string? protectedText)
{
return Unprotect(protectedText, purpose: null);
}
// 解密数据
// 步骤如下:
// 1. 对字符串进行 Base64 解码得到密文的字节数组
// 2. 如果提供了用途字符串,则根据用途字符串创建一个新的密码器
// 3. 利用密码器对密文的字节数组进行解密
// 4. 对解密后的字节数组进行反序列化得到数据的原始类型对象
public TData? Unprotect(string? protectedText, string? purpose)
{
try
{
if (protectedText == null)
{
return default(TData);
}
var protectedData = Base64UrlTextEncoder.Decode(protectedText);
if (protectedData == null)
{
return default(TData);
}
var protector = _protector;
if (!string.IsNullOrEmpty(purpose))
{
protector = protector.CreateProtector(purpose);
}
var userData = protector.Unprotect(protectedData);
if (userData == null)
{
return default(TData);
}
return _serializer.Deserialize(userData);
}
catch
{
return default(TData);
}
}
}
// 认证票据数据格式化
// 需要提供一个密码器并使用 TicketSerializer.Default 属性返回的 TicketSerializer 作为序列化器
public class TicketDataFormat : SecureDataFormat<AuthenticationTicket>
{
public TicketDataFormat(IDataProtector protector)
: base(TicketSerializer.Default, protector)
{
}
}
// 认证结果
public class AuthenticateResult
{
private static readonly AuthenticateResult _noResult = new() { None = true };
protected AuthenticateResult() { }
// 只要认证票据不为 null 则表示认证成功
[MemberNotNullWhen(true, nameof(Ticket), nameof(Principal), nameof(Properties))]
public bool Succeeded => Ticket != null;
// 认证票据
// 创建成功认证结果的核心操作就是利用认证票据设置这个属性
public AuthenticationTicket? Ticket { get; protected set; }
// 认证身份
// 创建成功认证结果时返回 AuthenticationTicket.Principal 属性表示的 ClaimsPrincipal 用户
public ClaimsPrincipal? Principal => Ticket?.Principal;
// 认证会话上下文
// 创建成功认证结果时返回 AuthenticationTicket.Properties 属性表示的 AuthenticationProperties 认证会话上下文
public AuthenticationProperties? Properties { get; protected set; }
// 认证失败时的异常
// 创建失败认证结果的核心操作就是利用具体的异常设置这个属性
public Exception? Failure { get; protected set; }
// 表示没有认证结果
public bool None { get; protected set; }
// 克隆认证结果
public AuthenticateResult Clone()
{
if (None)
{
return NoResult();
}
if (Failure != null)
{
return Fail(Failure, Properties?.Clone());
}
if (Succeeded)
{
return Success(Ticket!.Clone());
}
throw new NotImplementedException();
}
// 静态工具方法
// 使用 AuthenticationTicket 创建表示成功的认证结果
public static AuthenticateResult Success(AuthenticationTicket ticket)
{
ArgumentNullException.ThrowIfNull(ticket);
return new AuthenticateResult() { Ticket = ticket, Properties = ticket.Properties };
}
// 静态工具方法
// 创建表示无结果的认证结果
public static AuthenticateResult NoResult()
{
return _noResult;
}
// 静态工具方法
// 使用具体的异常创建表示失败的认证结果
public static AuthenticateResult Fail(Exception failure)
{
return new AuthenticateResult() { Failure = failure };
}
// 静态工具方法
// 创建表示失败的认证结果
public static AuthenticateResult Fail(Exception failure, AuthenticationProperties? properties)
{
return new AuthenticateResult() { Failure = failure, Properties = properties };
}
// 静态工具方法
// 创建表示失败的认证结果
public static AuthenticateResult Fail(string failureMessage)
=> Fail(new AuthenticationFailureException(failureMessage));
// 静态工具方法
// 创建表示失败的认证结果
public static AuthenticateResult Fail(string failureMessage, AuthenticationProperties? properties)
=> Fail(new AuthenticationFailureException(failureMessage), properties);
}
// 认证处理器接口
// 每个 AuthenticationScheme 都对应一个 IAuthenticationHandler 实现类型
public interface IAuthenticationHandler
{
// 认证初始化
// 利用 AuthenticationScheme 和 HttpContext 初始化认证处理器
Task InitializeAsync(AuthenticationScheme scheme, HttpContext context);
// 认证处理方法
// 最终 AuthenticationMiddleware 中间件会利用这个方法对每个请求进行认证,并返回 AuthenticateResult 认证结果
Task<AuthenticateResult> AuthenticateAsync();
// 用于发送质询消息
// 如果客户端(被认证方)没有携带有效的认证凭据,则响应状态码为 401 Unauthorized 的质询消息
Task ChallengeAsync(AuthenticationProperties? properties);
// 用于发送质询消息
// 如果客户端(被认证方)通过认证但没有权限访问资源,则响应状态码为 403 Forbidden 的质询消息
Task ForbidAsync(AuthenticationProperties? properties);
}
- IAuthenticationSignOutHandler
// 认证注销处理器接口
public interface IAuthenticationSignOutHandler : IAuthenticationHandler
{
// 注销方法
Task SignOutAsync(AuthenticationProperties? properties);
}
- IAuthenticationSignInHandler
// 认证登录处理器接口
// 一般认证处理器的实现类型都会实现这个接口
// 以提供完整的登录、注销、认证及两个质询方法
public interface IAuthenticationSignInHandler : IAuthenticationSignOutHandler
{
// 登录方法
Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties? properties);
}
- IAuthenticationRequestHandler
// 认证请求处理器接口
public interface IAuthenticationRequestHandler : IAuthenticationHandler
{
// 认证请求处理方法
// 如果返回 true 则表示已经完成了请求处理
// 那么 AuthenticationMiddleware 中间件不需要继续执行后续管道中的请求处理器
Task<bool> HandleRequestAsync();
}
// 认证方案
// 本质是对一个具体认证处理器类型的封装
public class AuthenticationScheme
{
public AuthenticationScheme(string name, string? displayName, Type handlerType)
{
ArgumentNullException.ThrowIfNull(name);
ArgumentNullException.ThrowIfNull(handlerType);
// 提供的认证处理器类型必须实现 IAuthenticationHandler 接口
if (!typeof(IAuthenticationHandler).IsAssignableFrom(handlerType))
{
throw new ArgumentException("handlerType must implement IAuthenticationHandler.");
}
Name = name;
HandlerType = handlerType;
DisplayName = displayName;
}
// 认证方案名称
public string Name { get; }
// 认证方案显示名称
public string? DisplayName { get; }
// 认证处理器类型
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
public Type HandlerType { get; }
}
- AuthenticationSchemeBuilder
// 认证方案建造者
// 实际的 AuthenticationScheme 创建者
public class AuthenticationSchemeBuilder
{
// 认证方案名称
public AuthenticationSchemeBuilder(string name)
{
Name = name;
}
public string Name { get; }
public string? DisplayName { get; set; }
// 认证处理器类型
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
public Type? HandlerType { get; set; }
// 构建认证方案
public AuthenticationScheme Build()
{
if (HandlerType is null)
{
throw new InvalidOperationException($"{nameof(HandlerType)} must be configured to build an {nameof(AuthenticationScheme)}.");
}
return new AuthenticationScheme(Name, DisplayName, HandlerType);
}
}
// 认证选项
// 本质是对一组 AuthenticationSchemeBuilder 的封装
public class AuthenticationOptions
{
private readonly IList<AuthenticationSchemeBuilder> _schemes = new List<AuthenticationSchemeBuilder>();
// 认证方案建造者集合
public IEnumerable<AuthenticationSchemeBuilder> Schemes => _schemes;
// 用于缓存认证方案建造者
public IDictionary<string, AuthenticationSchemeBuilder> SchemeMap { get; } = new Dictionary<string, AuthenticationSchemeBuilder>(StringComparer.Ordinal);
// 添加认证方案建造者
// 使用认证方案名称和配置委托创建并配置认证方案建造者
public void AddScheme(string name, Action<AuthenticationSchemeBuilder> configureBuilder)
{
ArgumentNullException.ThrowIfNull(name);
ArgumentNullException.ThrowIfNull(configureBuilder);
if (SchemeMap.ContainsKey(name))
{
throw new InvalidOperationException("Scheme already exists: " + name);
}
var builder = new AuthenticationSchemeBuilder(name);
configureBuilder(builder);
_schemes.Add(builder);
SchemeMap[name] = builder;
}
// 添加认证方案建造者
// 使用泛型参数表示的认证处理器类型配置认证方案建造者
public void AddScheme<THandler>(string name, string? displayName) where THandler : IAuthenticationHandler
{
AddScheme(name, b =>
{
b.DisplayName = displayName;
b.HandlerType = typeof(THandler);
});
}
// 默认认证方案名称
public string? DefaultScheme { get; set; }
// 提供认证操作的默认认证方案名称
public string? DefaultAuthenticateScheme { get; set; }
// 提供登录操作的默认认证方案名称
public string? DefaultSignInScheme { get; set; }
// 提供注销操作的默认认证方案名称
public string? DefaultSignOutScheme { get; set; }
// 提供质询操作(401 UnAuthorized)的默认认证方案名称
public string? DefaultChallengeScheme { get; set; }
// 提供质询操作(403 Forbidden)的默认认证方案名称
public string? DefaultForbidScheme { get; set; }
// 是否需要认证登录
// 如果 ClaimsIdentity.IsAuthenticated 属性为 true 则表示已通过认证
public bool RequireAuthenticatedSignIn { get; set; } = true;
private bool? _disableAutoDefaultScheme;
internal bool DisableAutoDefaultScheme
{
get
{
if (!_disableAutoDefaultScheme.HasValue)
{
_disableAutoDefaultScheme = AppContext.TryGetSwitch("Microsoft.AspNetCore.Authentication.SuppressAutoDefaultScheme", out var enabled) && enabled;
}
return _disableAutoDefaultScheme.Value;
}
set => _disableAutoDefaultScheme = value;
}
}
- IAuthenticationShecmeProvider
// 认证方案提供器接口
public interface IAuthenticationSchemeProvider
{
// 得到所有 AuthenticationScheme
Task<IEnumerable<AuthenticationScheme>> GetAllSchemesAsync();
// 根据指定的认证方案名称得到对应的 AuthenticationScheme
Task<AuthenticationScheme?> GetSchemeAsync(string name);
// 以下方法得到提供登录、注销、认证以及两个质询操作的默认认证方案
// 得到默认的认证处理器是 IAuthenticationHandler 的认证方案
Task<AuthenticationScheme?> GetDefaultAuthenticateSchemeAsync();
// 得到默认的认证处理器是 IAuthenticationHandler 的认证方案
Task<AuthenticationScheme?> GetDefaultChallengeSchemeAsync();
// 得到默认的认证处理器是 IAuthenticationHandler 的认证方案
Task<AuthenticationScheme?> GetDefaultForbidSchemeAsync();
// 得到默认的认证处理器类型是 IAuthenticationSignOutHandler 的认证方案
Task<AuthenticationScheme?> GetDefaultSignOutSchemeAsync();
// 得到默认的认证处理器类型是 IAuthenticationSignInHandler 的认证方案
Task<AuthenticationScheme?> GetDefaultSignInSchemeAsync();
// 添加认证方案
// 直接添加 AuthenticationScheme,不通过 AuthenticationSchemeBuilder 创建
void AddScheme(AuthenticationScheme scheme);
// 尝试添加认证方案
// 实现类型在实现 AddScheme 方法时应该在未添加成功时抛出异常
bool TryAddScheme(AuthenticationScheme scheme)
{
try
{
AddScheme(scheme);
return true;
}
catch
{
return false;
}
}
// 根据认证方案名称移除认证方案
void RemoveScheme(string name);
// 得到所有提供认证处理器类型是 IAuthenticationRequestHandler 的认证方案
Task<IEnumerable<AuthenticationScheme>> GetRequestHandlerSchemesAsync();
}
- AuthenticationShecmeProvider
// IAuthenticationSchemeProvider 的默认实现
public class AuthenticationSchemeProvider : IAuthenticationSchemeProvider
{
public AuthenticationSchemeProvider(IOptions<AuthenticationOptions> options)
: this(options, new Dictionary<string, AuthenticationScheme>(StringComparer.Ordinal))
{
}
protected AuthenticationSchemeProvider(IOptions<AuthenticationOptions> options, IDictionary<string, AuthenticationScheme> schemes)
{
_options = options.Value;
_schemes = schemes ?? throw new ArgumentNullException(nameof(schemes));
_requestHandlers = new List<AuthenticationScheme>();
foreach (var builder in _options.Schemes)
{
// 利用 AuthenticationSchemeBuilder 构建 AuthenticationScheme
var scheme = builder.Build();
AddScheme(scheme);
}
}
private readonly AuthenticationOptions _options;
private readonly object _lock = new object();
// 用于缓存认证方案
private readonly IDictionary<string, AuthenticationScheme> _schemes;
private readonly List<AuthenticationScheme> _requestHandlers;
private static readonly Task<AuthenticationScheme?> _nullScheme = Task.FromResult<AuthenticationScheme?>(null);
private Task<AuthenticationScheme?> _autoDefaultScheme = _nullScheme;
private IEnumerable<AuthenticationScheme> _schemesCopy = Array.Empty<AuthenticationScheme>();
private IEnumerable<AuthenticationScheme> _requestHandlersCopy = Array.Empty<AuthenticationScheme>();
// 使用默认认证方案名称得到对应的方案
private Task<AuthenticationScheme?> GetDefaultSchemeAsync()
=> _options.DefaultScheme != null
? GetSchemeAsync(_options.DefaultScheme)
: _autoDefaultScheme;
// 使用针对认证操作的认证方案名称得到对应的认证方案
// 不存在则使用默认方案
public virtual Task<AuthenticationScheme?> GetDefaultAuthenticateSchemeAsync()
=> _options.DefaultAuthenticateScheme != null
? GetSchemeAsync(_options.DefaultAuthenticateScheme)
: GetDefaultSchemeAsync();
// 使用针对质询操作(401 UnAuthorized)的默认认证方案名称
// 不存在则使用默认方案
public virtual Task<AuthenticationScheme?> GetDefaultChallengeSchemeAsync()
=> _options.DefaultChallengeScheme != null
? GetSchemeAsync(_options.DefaultChallengeScheme)
: GetDefaultSchemeAsync();
// 使用针对质询操作(403 Forbidden)的认证方案名称得到对应的认证方案
// 不存在则使用默认方案
public virtual Task<AuthenticationScheme?> GetDefaultForbidSchemeAsync()
=> _options.DefaultForbidScheme != null
? GetSchemeAsync(_options.DefaultForbidScheme)
: GetDefaultChallengeSchemeAsync();
// 使用针对登录操作的认证方案名称得到对应的认证方案
// 不存在则使用默认方案
public virtual Task<AuthenticationScheme?> GetDefaultSignInSchemeAsync()
=> _options.DefaultSignInScheme != null
? GetSchemeAsync(_options.DefaultSignInScheme)
: GetDefaultSchemeAsync();
// 使用针对注销操作的认证方案名称得到对应的认证方案
// 不存在则使用默认方案
public virtual Task<AuthenticationScheme?> GetDefaultSignOutSchemeAsync()
=> _options.DefaultSignOutScheme != null
? GetSchemeAsync(_options.DefaultSignOutScheme)
: GetDefaultSignInSchemeAsync();
// 从缓存的认证方案集合中根据名称得到对应的方案
public virtual Task<AuthenticationScheme?> GetSchemeAsync(string name)
=> Task.FromResult(_schemes.TryGetValue(name, out var scheme) ? scheme : null);
// 得到所有实现 IAuthenticationRequestHandler 接口的认证方案集合
public virtual Task<IEnumerable<AuthenticationScheme>> GetRequestHandlerSchemesAsync()
=> Task.FromResult(_requestHandlersCopy);
// 直接添加 AuthenticationScheme,不通过 AuthenticationSchemeBuilder 创建
// 必须不存在同名的认证方案才能添加成功
public virtual bool TryAddScheme(AuthenticationScheme scheme)
{
if (_schemes.ContainsKey(scheme.Name))
{
return false;
}
lock (_lock)
{
if (_schemes.ContainsKey(scheme.Name))
{
return false;
}
// 如果 AuthenticationScheme.HandlerType 类型实现了 IAuthenticationRequestHandler 接口
// 则需要添加到 IAuthenticationRequestHandler 集合中
if (typeof(IAuthenticationRequestHandler).IsAssignableFrom(scheme.HandlerType))
{
_requestHandlers.Add(scheme);
_requestHandlersCopy = _requestHandlers.ToArray();
}
_schemes[scheme.Name] = scheme;
_schemesCopy = _schemes.Values.ToArray();
CheckAutoDefaultScheme();
return true;
}
}
// 直接添加 AuthenticationScheme,不通过 AuthenticationSchemeBuilder 创建
public virtual void AddScheme(AuthenticationScheme scheme)
{
if (_schemes.ContainsKey(scheme.Name))
{
throw new InvalidOperationException("Scheme already exists: " + scheme.Name);
}
lock (_lock)
{
if (!TryAddScheme(scheme))
{
throw new InvalidOperationException("Scheme already exists: " + scheme.Name);
}
}
}
// 根据认证方案名称移除认证方案
// 同时需要尝试从 IAuthenticationRequestHandler 集合中移除
public virtual void RemoveScheme(string name)
{
if (!_schemes.TryGetValue(name, out _))
{
return;
}
lock (_lock)
{
if (_schemes.TryGetValue(name, out var scheme))
{
if (_requestHandlers.Remove(scheme))
{
_requestHandlersCopy = _requestHandlers.ToArray();
}
_schemes.Remove(name);
_schemesCopy = _schemes.Values.ToArray();
CheckAutoDefaultScheme();
}
}
}
// 得到所有 AuthenticationScheme
public virtual Task<IEnumerable<AuthenticationScheme>> GetAllSchemesAsync()
=> Task.FromResult(_schemesCopy);
// 自动默认认证方案使用认证方案集合中的第一个认证方案
private void CheckAutoDefaultScheme()
{
if (!_options.DisableAutoDefaultScheme)
{
if (_schemes.Count == 1)
{
_autoDefaultScheme = Task.FromResult<AuthenticationScheme?>(_schemesCopy.First());
}
else
{
_autoDefaultScheme = _nullScheme;
}
}
}
}
- IAuthenticationHandlerProvider
// 认证处理器提供器接口
public interface IAuthenticationHandlerProvider
{
// 得到认证处理器
Task<IAuthenticationHandler?> GetHandlerAsync(HttpContext context, string authenticationScheme);
}
- AuthenticationHandlerProvider
// IAuthenticationHandlerProvider 的默认实现
public class AuthenticationHandlerProvider : IAuthenticationHandlerProvider
{
public AuthenticationHandlerProvider(IAuthenticationSchemeProvider schemes)
{
Schemes = schemes;
}
// 认证方案提供器
public IAuthenticationSchemeProvider Schemes { get; }
// 根据认证方案名称缓存对应的认证处理器
private readonly Dictionary<string, IAuthenticationHandler> _handlerMap = new Dictionary<string, IAuthenticationHandler>(StringComparer.Ordinal);
// 得到认证处理器
// 具体实现步骤如下:
// 1. 使用认证方案名称调用 IAuthenticationSchemeProvider.GetSchemeAsync 方法得到对应的 AuthenticationScheme
// 2. 通过 HttpContext 提供的针对每个请求的服务范围容器
// 使用 AuthenticationScheme.HandlerType 表示的认证处理器类型得到对应的 IAuthenticationHandler
public async Task<IAuthenticationHandler?> GetHandlerAsync(HttpContext context, string authenticationScheme)
{
// 尝试从缓存中得到认证处理器
if (_handlerMap.TryGetValue(authenticationScheme, out var value))
{
return value;
}
// 利用认证方案名称调用 IAuthenticationSchemeProvider.GetSchemeAsync 方法得到 AuthenticationScheme
var scheme = await Schemes.GetSchemeAsync(authenticationScheme);
if (scheme == null)
{
return null;
}
// 如果没有注册认证服务器类型则使用反射方式创建认证服务器
// 但是选取的最优构造函数的参数会由表示每个请求的服务范围容器提供
var handler = (context.RequestServices.GetService(scheme.HandlerType) ??
ActivatorUtilities.CreateInstance(context.RequestServices, scheme.HandlerType))
as IAuthenticationHandler;
if (handler != null)
{
// 初始化认证处理器
await handler.InitializeAsync(scheme, context);
// 缓存认证处理器
_handlerMap[authenticationScheme] = handler;
}
return handler;
}
}