AspNetCore 认证 <1.0>


概览

认证(Authentication):用来给用户颁发一个凭证,其包含一个用户的基本信息,这个凭证可以由第三方机构(STS)颁发,也可以由自己颁发。

认证就涉及到两个模型:票据模型和认证模型。

票据模型抽象出用户凭证

认证模型通过中间件的方式给用户颁发凭证。

票据模型

模型概览

Claim:声明

ClaimsIdentity:身份,包含多个声明

ClaimPrincipal:用户,包含多个身份

AuthenticationTicket:票据,封装用户

TicketDataFormat:对票据进行处理,通过IDataProtector:对票据进行加密解密,通过TicketSerializer对票据进行序列化和反序列化。

Claim

陈述/声明

身份确认之后,认证方赋予的陈述,可以携带任何与认证用户相关的信息,可序列化,在网络中传递。

要点:

  • Type属性:陈述的类型
  • Value属性:陈述的值
  • ValueType属性:陈述值的类型
  • Issuer属性:颁发者
  • OriginalIssure属性:原始颁发者
  • Subject属性:陈述主题的ClaimsIdnetity对象
    /// 
    /// A Claim is a statement about an entity by an Issuer.
    /// A Claim consists of a Type, Value, a Subject and an Issuer.
    /// Additional properties, ValueType, Properties and OriginalIssuer help understand the claim when making decisions.
    /// 
    public class Claim
    {
        private enum SerializationMask
        {
            None = 0,
            NameClaimType = 1,
            RoleClaimType = 2,
            StringType = 4,
            Issuer = 8,
            OriginalIssuerEqualsIssuer = 16,
            OriginalIssuer = 32,
            HasProperties = 64,
            UserData = 128,
        }

        private readonly byte[]? _userSerializationData;

        private readonly string _issuer;
        private readonly string _originalIssuer;
        private Dictionary? _properties;

        private readonly ClaimsIdentity? _subject;
        private readonly string _type;
        private readonly string _value;
        private readonly string _valueType;

        /// 
        /// Initializes an instance of  using a .
        /// Normally the  is constructed using the bytes from  and initialized in the same way as the .
        /// 
        /// a  pointing to a .
        /// if 'reader' is null.
        public Claim(BinaryReader reader)
            : this(reader, null)
        {
        }

        /// 
        /// Initializes an instance of  using a .
        /// Normally the  is constructed using the bytes from  and initialized in the same way as the .
        /// 
        /// a  pointing to a .
        ///  the value for , which is the  that has these claims.
        /// if 'reader' is null.
        public Claim(BinaryReader reader, ClaimsIdentity? subject)
        {
            if (reader == null)
            {
                throw new ArgumentNullException(nameof(reader));
            }

            _subject = subject;

            SerializationMask mask = (SerializationMask)reader.ReadInt32();
            int numPropertiesRead = 1;
            int numPropertiesToRead = reader.ReadInt32();
            _value = reader.ReadString();

            if ((mask & SerializationMask.NameClaimType) == SerializationMask.NameClaimType)
            {
                _type = ClaimsIdentity.DefaultNameClaimType;
            }
            else if ((mask & SerializationMask.RoleClaimType) == SerializationMask.RoleClaimType)
            {
                _type = ClaimsIdentity.DefaultRoleClaimType;
            }
            else
            {
                _type = reader.ReadString();
                numPropertiesRead++;
            }

            if ((mask & SerializationMask.StringType) == SerializationMask.StringType)
            {
                _valueType = reader.ReadString();
                numPropertiesRead++;
            }
            else
            {
                _valueType = ClaimValueTypes.String;
            }

            if ((mask & SerializationMask.Issuer) == SerializationMask.Issuer)
            {
                _issuer = reader.ReadString();
                numPropertiesRead++;
            }
            else
            {
                _issuer = ClaimsIdentity.DefaultIssuer;
            }

            if ((mask & SerializationMask.OriginalIssuerEqualsIssuer) == SerializationMask.OriginalIssuerEqualsIssuer)
            {
                _originalIssuer = _issuer;
            }
            else if ((mask & SerializationMask.OriginalIssuer) == SerializationMask.OriginalIssuer)
            {
                _originalIssuer = reader.ReadString();
                numPropertiesRead++;
            }
            else
            {
                _originalIssuer = ClaimsIdentity.DefaultIssuer;
            }

            if ((mask & SerializationMask.HasProperties) == SerializationMask.HasProperties)
            {
                int numProperties = reader.ReadInt32();
                numPropertiesRead++;
                for (int i = 0; i < numProperties; i++)
                {
                    Properties.Add(reader.ReadString(), reader.ReadString());
                }
            }

            if ((mask & SerializationMask.UserData) == SerializationMask.UserData)
            {
                int cb = reader.ReadInt32();
                _userSerializationData = reader.ReadBytes(cb);
                numPropertiesRead++;
            }

            for (int i = numPropertiesRead; i < numPropertiesToRead; i++)
            {
                reader.ReadString();
            }
        }

        /// 
        /// Creates a  with the specified type and value.
        /// 
        /// The claim type.
        /// The claim value.
        ///  or  is null.
        /// 
        ///  is set to ,
        ///  is set to ,
        ///  is set to , and
        ///  is set to null.
        /// 
        /// 
        /// 
        /// 
        public Claim(string type, string value)
            : this(type, value, ClaimValueTypes.String, ClaimsIdentity.DefaultIssuer, ClaimsIdentity.DefaultIssuer, (ClaimsIdentity?)null)
        {
        }

        /// 
        /// Creates a  with the specified type, value, and value type.
        /// 
        /// The claim type.
        /// The claim value.
        /// The claim value type.
        ///  or  is null.
        /// 
        ///  is set to ,
        ///  is set to ,
        /// and  is set to null.
        /// 
        /// 
        /// 
        /// 
        public Claim(string type, string value, string? valueType)
            : this(type, value, valueType, ClaimsIdentity.DefaultIssuer, ClaimsIdentity.DefaultIssuer, (ClaimsIdentity?)null)
        {
        }

        /// 
        /// Creates a  with the specified type, value, value type, and issuer.
        /// 
        /// The claim type.
        /// The claim value.
        /// The claim value type. If this parameter is empty or null, then  is used.
        /// The claim issuer. If this parameter is empty or null, then  is used.
        ///  or  is null.
        /// 
        ///  is set to value of the  parameter,
        ///  is set to null.
        /// 
        /// 
        /// 
        /// 
        public Claim(string type, string value, string? valueType, string? issuer)
            : this(type, value, valueType, issuer, issuer, (ClaimsIdentity?)null)
        {
        }

        /// 
        /// Creates a  with the specified type, value, value type, issuer and original issuer.
        /// 
        /// The claim type.
        /// The claim value.
        /// The claim value type. If this parameter is null, then  is used.
        /// The claim issuer. If this parameter is empty or null, then  is used.
        /// The original issuer of this claim. If this parameter is empty or null, then originalIssuer == issuer.
        ///  or  is null.
        /// 
        ///  is set to null.
        /// 
        /// 
        /// 
        /// 
        public Claim(string type, string value, string? valueType, string? issuer, string? originalIssuer)
            : this(type, value, valueType, issuer, originalIssuer, (ClaimsIdentity?)null)
        {
        }

        /// 
        /// Creates a  with the specified type, value, value type, issuer, original issuer and subject.
        /// 
        /// The claim type.
        /// The claim value.
        /// The claim value type. If this parameter is null, then  is used.
        /// The claim issuer. If this parameter is empty or null, then  is used.
        /// The original issuer of this claim. If this parameter is empty or null, then originalIssuer == issuer.
        /// The subject that this claim describes.
        ///  or  is null.
        /// 
        /// 
        /// 
        public Claim(string type, string value, string? valueType, string? issuer, string? originalIssuer, ClaimsIdentity? subject)
            : this(type, value, valueType, issuer, originalIssuer, subject, null, null)
        {
        }

        /// 
        /// This internal constructor was added as a performance boost when adding claims that are found in the NTToken.
        /// We need to add a property value to distinguish DeviceClaims from UserClaims.
        /// 
        /// The claim type.
        /// The claim value.
        /// The claim value type. If this parameter is null, then  is used.
        /// The claim issuer. If this parameter is empty or null, then  is used.
        /// The original issuer of this claim. If this parameter is empty or null, then originalIssuer == issuer.
        /// The subject that this claim describes.
        /// This allows adding a property when adding a Claim.
        /// The value associated with the property.
        internal Claim(string type, string value, string? valueType, string? issuer, string? originalIssuer, ClaimsIdentity? subject, string? propertyKey, string? propertyValue)
        {
            if (type == null)
            {
                throw new ArgumentNullException(nameof(type));
            }

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

            _type = type;
            _value = value;
            _valueType = string.IsNullOrEmpty(valueType) ? ClaimValueTypes.String : valueType;
            _issuer = string.IsNullOrEmpty(issuer) ? ClaimsIdentity.DefaultIssuer : issuer;
            _originalIssuer = string.IsNullOrEmpty(originalIssuer) ? _issuer : originalIssuer;
            _subject = subject;

            if (propertyKey != null)
            {
                _properties = new Dictionary();
                _properties[propertyKey] = propertyValue!;
            }
        }

        /// 
        /// Copy constructor for 
        /// 
        /// the  to copy.
        /// will be set to 'null'.
        /// if 'other' is null.
        protected Claim(Claim other)
            : this(other, (other == null ? (ClaimsIdentity?)null : other._subject))
        {
        }

        /// 
        /// Copy constructor for 
        /// 
        /// the  to copy.
        /// the  to assign to .
        /// will be set to 'subject'.
        /// if 'other' is null.
        protected Claim(Claim other, ClaimsIdentity? subject)
        {
            if (other == null)
                throw new ArgumentNullException(nameof(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(other._properties);
            }

            if (other._userSerializationData != null)
            {
                _userSerializationData = other._userSerializationData.Clone() as byte[];
            }
        }

        /// 
        /// Contains any additional data provided by a derived type, typically set when calling .
        /// 
        protected virtual byte[]? CustomSerializationData
        {
            get
            {
                return _userSerializationData;
            }
        }

        /// 
        /// Gets the issuer of the .
        /// 
        public string Issuer
        {
            get { return _issuer; }
        }

        /// 
        /// Gets the original issuer of the .
        /// 
        /// 
        /// When the  differs from the , it means
        /// that the claim was issued by the  and was re-issued
        /// by the .
        /// 
        public string OriginalIssuer
        {
            get { return _originalIssuer; }
        }

        /// 
        /// Gets the collection of Properties associated with the .
        /// 
        public IDictionary Properties
        {
            get
            {
                if (_properties == null)
                {
                    _properties = new Dictionary();
                }
                return _properties;
            }
        }

        /// 
        /// Gets the subject of the .
        /// 
        public ClaimsIdentity? Subject
        {
            get { return _subject; }
        }

        /// 
        /// Gets the claim type of the .
        /// .
        /// 
        public string Type
        {
            get { return _type; }
        }

        /// 
        /// Gets the value of the .
        /// 
        public string Value
        {
            get { return _value; }
        }

        /// 
        /// Gets the value type of the .
        /// 
        /// 
        public string ValueType
        {
            get { return _valueType; }
        }

        /// 
        /// Creates a new instance  with values copied from this object.
        /// 
        public virtual Claim Clone()
        {
            return Clone((ClaimsIdentity?)null);
        }

        /// 
        /// Creates a new instance  with values copied from this object.
        /// 
        /// the value for , which is the  that has these claims.
        ///  will be set to 'identity'.
        public virtual Claim Clone(ClaimsIdentity? identity)
        {
            return new Claim(this, identity);
        }

        /// 
        /// Serializes using a 
        /// 
        /// the  to use for data storage.
        /// if 'writer' is null.
        public virtual void WriteTo(BinaryWriter writer)
        {
            WriteTo(writer, null);
        }

        /// 
        /// Serializes using a 
        /// 
        /// the  to use for data storage.
        /// additional data provided by derived type.
        /// if 'writer' is null.
        protected virtual void WriteTo(BinaryWriter writer, byte[]? userData)
        {
            if (writer == null)
            {
                throw new ArgumentNullException(nameof(writer));
            }

            int numberOfPropertiesWritten = 1;
            SerializationMask mask = SerializationMask.None;
            if (string.Equals(_type, ClaimsIdentity.DefaultNameClaimType))
            {
                mask |= SerializationMask.NameClaimType;
            }
            else if (string.Equals(_type, ClaimsIdentity.DefaultRoleClaimType))
            {
                mask |= SerializationMask.RoleClaimType;
            }
            else
            {
                numberOfPropertiesWritten++;
            }

            if (!string.Equals(_valueType, ClaimValueTypes.String, StringComparison.Ordinal))
            {
                numberOfPropertiesWritten++;
                mask |= SerializationMask.StringType;
            }

            if (!string.Equals(_issuer, ClaimsIdentity.DefaultIssuer, StringComparison.Ordinal))
            {
                numberOfPropertiesWritten++;
                mask |= SerializationMask.Issuer;
            }

            if (string.Equals(_originalIssuer, _issuer, StringComparison.Ordinal))
            {
                mask |= SerializationMask.OriginalIssuerEqualsIssuer;
            }
            else if (!string.Equals(_originalIssuer, ClaimsIdentity.DefaultIssuer))
            {
                numberOfPropertiesWritten++;
                mask |= SerializationMask.OriginalIssuer;
            }

            if (_properties != null && _properties.Count > 0)
            {
                numberOfPropertiesWritten++;
                mask |= SerializationMask.HasProperties;
            }

            if (userData != null && userData.Length > 0)
            {
                numberOfPropertiesWritten++;
                mask |= SerializationMask.UserData;
            }

            writer.Write((int)mask);
            writer.Write(numberOfPropertiesWritten);
            writer.Write(_value);

            if (((mask & SerializationMask.NameClaimType) != SerializationMask.NameClaimType) && ((mask & SerializationMask.RoleClaimType) != SerializationMask.RoleClaimType))
            {
                writer.Write(_type);
            }

            if ((mask & SerializationMask.StringType) == SerializationMask.StringType)
            {
                writer.Write(_valueType);
            }

            if ((mask & SerializationMask.Issuer) == SerializationMask.Issuer)
            {
                writer.Write(_issuer);
            }

            if ((mask & SerializationMask.OriginalIssuer) == SerializationMask.OriginalIssuer)
            {
                writer.Write(_originalIssuer);
            }

            if ((mask & SerializationMask.HasProperties) == SerializationMask.HasProperties)
            {
                writer.Write(_properties!.Count);
                foreach (var kvp in _properties)
                {
                    writer.Write(kvp.Key);
                    writer.Write(kvp.Value);
                }
            }

            if ((mask & SerializationMask.UserData) == SerializationMask.UserData)
            {
                writer.Write(userData!.Length);
                writer.Write(userData);
            }

            writer.Flush();
        }

        /// 
        /// Returns a string representation of the  object.
        /// 
        /// 
        /// The returned string contains the values of the  and  properties.
        /// 
        /// The string representation of the  object.
        public override string ToString()
        {
            return _type + ": " + _value;
        }
    }

ClaimsIdentity

表示用户的身份,一个身份可以携带多个陈述。

IIdentity

要点

  • Name属性:身份总是具有一个确定的名字
  • IsAuthenticated属性:身份是否经过认证,只有身份经过认证的用户才是信任的
  • AuthenticationType属性:认证类型
    public interface IIdentity
    {
        // Access to the name string
        string? Name { get; }

        // Access to Authentication 'type' info
        string? AuthenticationType { get; }

        // Determine if this represents the unauthenticated identity
        bool IsAuthenticated { get; }
    }

ClaimsIdentity

携带声明的身份对象。

要点

  • DefaultIssuer = @"LOCAL AUTHORITY";
  • DefaultNameClaimType = ClaimTypes.Name;
  • DefaultRoleClaimType = ClaimTypes.Role;
  • IsAuthenticated的逻辑:return !string.IsNullOrEmpty(_authenticationType);
  • Actor和BootstrapContext:表示身份委托

    /// 
    /// An Identity that is represented by a set of claims.
    /// 
    public class ClaimsIdentity : IIdentity
    {
        private enum SerializationMask
        {
            None = 0,
            AuthenticationType = 1,
            BootstrapConext = 2,
            NameClaimType = 4,
            RoleClaimType = 8,
            HasClaims = 16,
            HasLabel = 32,
            Actor = 64,
            UserData = 128,
        }

        private byte[]? _userSerializationData;
        private ClaimsIdentity? _actor;
        private string? _authenticationType;
        private object? _bootstrapContext;
        private List>? _externalClaims;
        private string? _label;
        private readonly List _instanceClaims = new List();
        private string _nameClaimType = DefaultNameClaimType;
        private string _roleClaimType = DefaultRoleClaimType;

        public const string DefaultIssuer = @"LOCAL AUTHORITY";
        public const string DefaultNameClaimType = ClaimTypes.Name;
        public const string DefaultRoleClaimType = ClaimTypes.Role;

        // NOTE about _externalClaims.
        // GenericPrincpal and RolePrincipal set role claims here so that .IsInRole will be consistent with a 'role' claim found by querying the identity or principal.
        // _externalClaims are external to the identity and assumed to be dynamic, they not serialized or copied through Clone().
        // Access through public method: ClaimProviders.

        /// 
        /// Initializes an instance of .
        /// 
        public ClaimsIdentity()
            : this((IIdentity?)null, (IEnumerable?)null, (string?)null, (string?)null, (string?)null)
        {
        }

        /// 
        /// Initializes an instance of .
        /// 
        ///  supplies the  and .
        ///  for details on how internal values are set.
        public ClaimsIdentity(IIdentity? identity)
            : this(identity, (IEnumerable?)null, (string?)null, (string?)null, (string?)null)
        {
        }

        /// 
        /// Initializes an instance of .
        /// 
        ///  associated with this instance.
        /// 
        ///  for details on how internal values are set.
        /// 
        public ClaimsIdentity(IEnumerable? claims)
            : this((IIdentity?)null, claims, (string?)null, (string?)null, (string?)null)
        {
        }

        /// 
        /// Initializes an instance of .
        /// 
        /// The authentication method used to establish this identity.
        public ClaimsIdentity(string? authenticationType)
            : this((IIdentity?)null, (IEnumerable?)null, authenticationType, (string?)null, (string?)null)
        {
        }

        /// 
        /// Initializes an instance of .
        /// 
        ///  associated with this instance.
        /// The authentication method used to establish this identity.
        ///  for details on how internal values are set.
        public ClaimsIdentity(IEnumerable? claims, string? authenticationType)
            : this((IIdentity?)null, claims, authenticationType, (string?)null, (string?)null)
        {
        }

        /// 
        /// Initializes an instance of .
        /// 
        ///  supplies the  and .
        ///  associated with this instance.
        ///  for details on how internal values are set.
        public ClaimsIdentity(IIdentity? identity, IEnumerable? claims)
            : this(identity, claims, (string?)null, (string?)null, (string?)null)
        {
        }

        /// 
        /// Initializes an instance of .
        /// 
        /// The type of authentication used.
        /// The  used when obtaining the value of .
        /// The  used when performing logic for .
        ///  for details on how internal values are set.
        public ClaimsIdentity(string? authenticationType, string? nameType, string? roleType)
            : this((IIdentity?)null, (IEnumerable?)null, authenticationType, nameType, roleType)
        {
        }

        /// 
        /// Initializes an instance of .
        /// 
        ///  associated with this instance.
        /// The type of authentication used.
        /// The  used when obtaining the value of .
        /// The  used when performing logic for .
        ///  for details on how internal values are set.
        public ClaimsIdentity(IEnumerable? claims, string? authenticationType, string? nameType, string? roleType)
            : this((IIdentity?)null, claims, authenticationType, nameType, roleType)
        {
        }

        /// 
        /// Initializes an instance of .
        /// 
        ///  supplies the  and .
        ///  associated with this instance.
        /// The type of authentication used.
        /// The  used when obtaining the value of .
        /// The  used when performing logic for .
        /// If 'identity' is a , then there are potentially multiple sources for AuthenticationType, NameClaimType, RoleClaimType.
        /// Priority is given to the parameters: authenticationType, nameClaimType, roleClaimType.
        /// All s are copied into this instance in a . Each Claim is examined and if Claim.Subject != this, then Claim.Clone(this) is called before the claim is added.
        /// Any 'External' claims are ignored.
        /// 
        /// if 'identity' is a  and  results in a circular reference back to 'this'.
        public ClaimsIdentity(IIdentity? identity, IEnumerable? claims, string? authenticationType, string? nameType, string? roleType)
        {
            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);

            if (claimsIdentity != null)
            {
                _label = claimsIdentity._label;
                _bootstrapContext = claimsIdentity._bootstrapContext;

                if (claimsIdentity.Actor != null)
                {
                    //
                    // Check if the Actor is circular before copying. That check is done while setting
                    // the Actor property and so not really needed here. But checking just for sanity sake
                    //
                    if (!IsCircular(claimsIdentity.Actor))
                    {
                        _actor = claimsIdentity.Actor;
                    }
                    else
                    {
                        throw new InvalidOperationException(SR.InvalidOperationException_ActorGraphCircular);
                    }
                }
                SafeAddClaims(claimsIdentity._instanceClaims);
            }
            else
            {
                if (identity != null && !string.IsNullOrEmpty(identity.Name))
                {
                    SafeAddClaim(new Claim(_nameClaimType, identity.Name, ClaimValueTypes.String, DefaultIssuer, DefaultIssuer, this));
                }
            }

            if (claims != null)
            {
                SafeAddClaims(claims);
            }
        }

        /// 
        /// Initializes an instance of  using a .
        /// Normally the  is constructed using the bytes from  and initialized in the same way as the .
        /// 
        /// a  pointing to a .
        /// if 'reader' is null.
        public ClaimsIdentity(BinaryReader reader)
        {
            if (reader == null)
                throw new ArgumentNullException(nameof(reader));

            Initialize(reader);
        }

        /// 
        /// Copy constructor.
        /// 
        ///  to copy.
        /// if 'other' is null.
        protected ClaimsIdentity(ClaimsIdentity other)
        {
            if (other == null)
            {
                throw new ArgumentNullException(nameof(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);
        }

        protected ClaimsIdentity(SerializationInfo info, StreamingContext context)
        {
            throw new PlatformNotSupportedException();
        }

        /// 
        /// Initializes an instance of  from a serialized stream created via
        /// .
        /// 
        /// 
        /// The  to read from.
        /// 
        /// Thrown is the  is null.
        protected ClaimsIdentity(SerializationInfo info)
        {
            throw new PlatformNotSupportedException();
        }

        /// 
        /// Gets the authentication type that can be used to determine how this  authenticated to an authority.
        /// 
        public virtual string? AuthenticationType
        {
            get { return _authenticationType; }
        }

        /// 
        /// Gets a value that indicates if the user has been authenticated.
        /// 
        public virtual bool IsAuthenticated
        {
            get { return !string.IsNullOrEmpty(_authenticationType); }
        }

        /// 
        /// Gets or sets a  that was granted delegation rights.
        /// 
        /// if 'value' results in a circular reference back to 'this'.
        public ClaimsIdentity? Actor
        {
            get { return _actor; }
            set
            {
                if (value != null)
                {
                    if (IsCircular(value))
                    {
                        throw new InvalidOperationException(SR.InvalidOperationException_ActorGraphCircular);
                    }
                }
                _actor = value;
            }
        }

        /// 
        /// Gets or sets a context that was used to create this .
        /// 
        public object? BootstrapContext
        {
            get { return _bootstrapContext; }
            set { _bootstrapContext = value; }
        }

        /// 
        /// Gets the claims as , associated with this .
        /// 
        /// May contain nulls.
        public virtual IEnumerable Claims
        {
            get
            {
                if (_externalClaims == null)
                {
                    return _instanceClaims;
                }

                return CombinedClaimsIterator();
            }
        }

        private IEnumerable 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;
                    }
                }
            }
        }

        /// 
        /// Contains any additional data provided by a derived type, typically set when calling .
        /// 
        protected virtual byte[]? CustomSerializationData
        {
            get
            {
                return _userSerializationData;
            }
        }

        /// 
        /// Allow the association of claims with this instance of .
        /// The claims will not be serialized or added in Clone(). They will be included in searches, finds and returned from the call to .
        /// 
        internal List> ExternalClaims
        {
            get
            {
                if (_externalClaims == null)
                {
                    _externalClaims = new List>();
                }
                return _externalClaims;
            }
        }

        /// 
        /// Gets or sets the label for this 
        /// 
        public string? Label
        {
            get { return _label; }
            set { _label = value; }
        }

        /// 
        /// Gets the Name of this .
        /// 
        /// Calls  where string == NameClaimType, if found, returns  otherwise null.
        public virtual string? Name
        {
            // just an accessor for getting the name claim
            get
            {
                Claim? claim = FindFirst(_nameClaimType);
                if (claim != null)
                {
                    return claim.Value;
                }

                return null;
            }
        }

        /// 
        /// Gets the value that identifies 'Name' claims. This is used when returning the property .
        /// 
        public string NameClaimType
        {
            get { return _nameClaimType; }
        }

        /// 
        /// Gets the value that identifies 'Role' claims. This is used when calling .
        /// 
        public string RoleClaimType
        {
            get { return _roleClaimType; }
        }

        /// 
        /// Creates a new instance of  with values copied from this object.
        /// 
        public virtual ClaimsIdentity Clone()
        {
            return new ClaimsIdentity(this);
        }

        /// 
        /// Adds a single  to an internal list.
        /// 
        /// the add.
        /// If  != this, then Claim.Clone(this) is called before the claim is added.
        /// if 'claim' is null.
        public virtual void AddClaim(Claim claim)
        {
            if (claim == null)
            {
                throw new ArgumentNullException(nameof(claim));
            }

            if (object.ReferenceEquals(claim.Subject, this))
            {
                _instanceClaims.Add(claim);
            }
            else
            {
                _instanceClaims.Add(claim.Clone(this));
            }
        }

        /// 
        /// Adds a  to the internal list.
        /// 
        /// Enumeration of claims to add.
        /// Each claim is examined and if  != this, then Claim.Clone(this) is called before the claim is added.
        /// if 'claims' is null.
        public virtual void AddClaims(IEnumerable claims)
        {
            if (claims == null)
            {
                throw new ArgumentNullException(nameof(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));
                }
            }
        }

        /// 
        /// Attempts to remove a  the internal list.
        /// 
        /// the  to match.
        ///  It is possible that a  returned from  cannot be removed. This would be the case for 'External' claims that are provided by reference.
        /// object.ReferenceEquals is used to 'match'.
        /// 
        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;
        }

        /// 
        /// Removes a  from the internal list.
        /// 
        /// the  to match.
        ///  It is possible that a  returned from  cannot be removed. This would be the case for 'External' claims that are provided by reference.
        /// object.ReferenceEquals is used to 'match'.
        /// 
        /// if 'claim' cannot be removed.
        public virtual void RemoveClaim(Claim? claim)
        {
            if (!TryRemoveClaim(claim))
            {
                throw new InvalidOperationException(SR.Format(SR.InvalidOperation_ClaimCannotBeRemoved, claim));
            }
        }

        /// 
        /// Adds claims to internal list. Calling Claim.Clone if Claim.Subject != this.
        /// 
        /// a  to add to 
        /// private only call from constructor, adds to internal list.
        private void SafeAddClaims(IEnumerable 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));
                }
            }
        }

        /// 
        /// Adds claim to internal list. Calling Claim.Clone if Claim.Subject != this.
        /// 
        /// private only call from constructor, adds to internal list.
        private void SafeAddClaim(Claim? claim)
        {
            if (claim == null)
                return;

            if (object.ReferenceEquals(claim.Subject, this))
            {
                _instanceClaims.Add(claim);
            }
            else
            {
                _instanceClaims.Add(claim.Clone(this));
            }
        }

        /// 
        /// Retrieves a  where each claim is matched by .
        /// 
        /// The function that performs the matching logic.
        /// A  of matched claims.
        /// if 'match' is null.
        public virtual IEnumerable FindAll(Predicate match)
        {
            if (match == null)
            {
                throw new ArgumentNullException(nameof(match));
            }

            foreach (Claim claim in Claims)
            {
                if (match(claim))
                {
                    yield return claim;
                }
            }
        }

        /// 
        /// Retrieves a  where each Claim.Type equals .
        /// 
        /// The type of the claim to match.
        /// A  of matched claims.
        /// Comparison is: StringComparison.OrdinalIgnoreCase.
        /// if 'type' is null.
        public virtual IEnumerable FindAll(string type)
        {
            if (type == null)
            {
                throw new ArgumentNullException(nameof(type));
            }

            foreach (Claim claim in Claims)
            {
                if (claim != null)
                {
                    if (string.Equals(claim.Type, type, StringComparison.OrdinalIgnoreCase))
                    {
                        yield return claim;
                    }
                }
            }
        }

        /// 
        /// Retrieves the first  that is matched by .
        /// 
        /// The function that performs the matching logic.
        /// A , null if nothing matches.
        /// if 'match' is null.
        public virtual Claim? FindFirst(Predicate match)
        {
            if (match == null)
            {
                throw new ArgumentNullException(nameof(match));
            }

            foreach (Claim claim in Claims)
            {
                if (match(claim))
                {
                    return claim;
                }
            }

            return null;
        }

        /// 
        /// Retrieves the first  where Claim.Type equals .
        /// 
        /// The type of the claim to match.
        /// A , null if nothing matches.
        /// Comparison is: StringComparison.OrdinalIgnoreCase.
        /// if 'type' is null.
        public virtual Claim? FindFirst(string type)
        {
            if (type == null)
            {
                throw new ArgumentNullException(nameof(type));
            }

            foreach (Claim claim in Claims)
            {
                if (claim != null)
                {
                    if (string.Equals(claim.Type, type, StringComparison.OrdinalIgnoreCase))
                    {
                        return claim;
                    }
                }
            }

            return null;
        }

        /// 
        /// Determines if a claim is contained within this ClaimsIdentity.
        /// 
        /// The function that performs the matching logic.
        /// true if a claim is found, false otherwise.
        /// if 'match' is null.
        public virtual bool HasClaim(Predicate match)
        {
            if (match == null)
            {
                throw new ArgumentNullException(nameof(match));
            }

            foreach (Claim claim in Claims)
            {
                if (match(claim))
                {
                    return true;
                }
            }

            return false;
        }

        /// 
        /// Determines if a claim with type AND value is contained within this ClaimsIdentity.
        /// 
        /// the type of the claim to match.
        /// the value of the claim to match.
        /// true if a claim is matched, false otherwise.
        /// Comparison is: StringComparison.OrdinalIgnoreCase for Claim.Type, StringComparison.Ordinal for Claim.Value.
        /// if 'type' is null.
        /// if 'value' is null.
        public virtual bool HasClaim(string type, string value)
        {
            if (type == null)
            {
                throw new ArgumentNullException(nameof(type));
            }

            if (value == null)
            {
                throw new ArgumentNullException(nameof(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;
        }

        /// 
        /// Initializes from a . Normally the reader is initialized with the results from 
        /// Normally the  is initialized in the same way as the  passed to .
        /// 
        /// a  pointing to a .
        /// if 'reader' is null.
        private void Initialize(BinaryReader reader)
        {
            if (reader == null)
            {
                throw new ArgumentNullException(nameof(reader));
            }

            SerializationMask mask = (SerializationMask)reader.ReadInt32();
            int numPropertiesRead = 0;
            int numPropertiesToRead = reader.ReadInt32();

            if ((mask & SerializationMask.AuthenticationType) == SerializationMask.AuthenticationType)
            {
                _authenticationType = reader.ReadString();
                numPropertiesRead++;
            }

            if ((mask & SerializationMask.BootstrapConext) == SerializationMask.BootstrapConext)
            {
                _bootstrapContext = reader.ReadString();
                numPropertiesRead++;
            }

            if ((mask & SerializationMask.NameClaimType) == SerializationMask.NameClaimType)
            {
                _nameClaimType = reader.ReadString();
                numPropertiesRead++;
            }
            else
            {
                _nameClaimType = ClaimsIdentity.DefaultNameClaimType;
            }

            if ((mask & SerializationMask.RoleClaimType) == SerializationMask.RoleClaimType)
            {
                _roleClaimType = reader.ReadString();
                numPropertiesRead++;
            }
            else
            {
                _roleClaimType = ClaimsIdentity.DefaultRoleClaimType;
            }

            if ((mask & SerializationMask.HasLabel) == SerializationMask.HasLabel)
            {
                _label = reader.ReadString();
                numPropertiesRead++;
            }

            if ((mask & SerializationMask.HasClaims) == SerializationMask.HasClaims)
            {
                int numberOfClaims = reader.ReadInt32();
                for (int index = 0; index < numberOfClaims; index++)
                {
                    _instanceClaims.Add(CreateClaim(reader));
                }
                numPropertiesRead++;
            }

            if ((mask & SerializationMask.Actor) == SerializationMask.Actor)
            {
                _actor = new ClaimsIdentity(reader);
                numPropertiesRead++;
            }

            if ((mask & SerializationMask.UserData) == SerializationMask.UserData)
            {
                int cb = reader.ReadInt32();
                _userSerializationData = reader.ReadBytes(cb);
                numPropertiesRead++;
            }

            for (int i = numPropertiesRead; i < numPropertiesToRead; i++)
            {
                reader.ReadString();
            }
        }

        /// 
        /// Provides an extensibility point for derived types to create a custom .
        /// 
        /// the that points at the claim.
        /// a new .
        protected virtual Claim CreateClaim(BinaryReader reader)
        {
            if (reader == null)
            {
                throw new ArgumentNullException(nameof(reader));
            }

            return new Claim(reader, this);
        }

        /// 
        /// Serializes using a 
        /// 
        /// the  to use for data storage.
        /// if 'writer' is null.
        public virtual void WriteTo(BinaryWriter writer)
        {
            WriteTo(writer, null);
        }

        /// 
        /// Serializes using a 
        /// 
        /// the  to use for data storage.
        /// additional data provided by derived type.
        /// if 'writer' is null.
        protected virtual void WriteTo(BinaryWriter writer, byte[]? userData)
        {
            if (writer == null)
            {
                throw new ArgumentNullException(nameof(writer));
            }

            int numberOfPropertiesWritten = 0;
            var mask = SerializationMask.None;
            if (_authenticationType != null)
            {
                mask |= SerializationMask.AuthenticationType;
                numberOfPropertiesWritten++;
            }

            if (_bootstrapContext != null)
            {
                if (_bootstrapContext is string rawData)
                {
                    mask |= SerializationMask.BootstrapConext;
                    numberOfPropertiesWritten++;
                }
            }

            if (!string.Equals(_nameClaimType, ClaimsIdentity.DefaultNameClaimType, StringComparison.Ordinal))
            {
                mask |= SerializationMask.NameClaimType;
                numberOfPropertiesWritten++;
            }

            if (!string.Equals(_roleClaimType, ClaimsIdentity.DefaultRoleClaimType, StringComparison.Ordinal))
            {
                mask |= SerializationMask.RoleClaimType;
                numberOfPropertiesWritten++;
            }

            if (!string.IsNullOrWhiteSpace(_label))
            {
                mask |= SerializationMask.HasLabel;
                numberOfPropertiesWritten++;
            }

            if (_instanceClaims.Count > 0)
            {
                mask |= SerializationMask.HasClaims;
                numberOfPropertiesWritten++;
            }

            if (_actor != null)
            {
                mask |= SerializationMask.Actor;
                numberOfPropertiesWritten++;
            }

            if (userData != null && userData.Length > 0)
            {
                numberOfPropertiesWritten++;
                mask |= SerializationMask.UserData;
            }

            writer.Write((int)mask);
            writer.Write(numberOfPropertiesWritten);
            if ((mask & SerializationMask.AuthenticationType) == SerializationMask.AuthenticationType)
            {
                writer.Write(_authenticationType!);
            }

            if ((mask & SerializationMask.BootstrapConext) == SerializationMask.BootstrapConext)
            {
                writer.Write((string)_bootstrapContext!);
            }

            if ((mask & SerializationMask.NameClaimType) == SerializationMask.NameClaimType)
            {
                writer.Write(_nameClaimType);
            }

            if ((mask & SerializationMask.RoleClaimType) == SerializationMask.RoleClaimType)
            {
                writer.Write(_roleClaimType);
            }

            if ((mask & SerializationMask.HasLabel) == SerializationMask.HasLabel)
            {
                writer.Write(_label!);
            }

            if ((mask & SerializationMask.HasClaims) == SerializationMask.HasClaims)
            {
                writer.Write(_instanceClaims.Count);
                foreach (var claim in _instanceClaims)
                {
                    claim.WriteTo(writer);
                }
            }

            if ((mask & SerializationMask.Actor) == SerializationMask.Actor)
            {
                _actor!.WriteTo(writer);
            }

            if ((mask & SerializationMask.UserData) == SerializationMask.UserData)
            {
                writer.Write(userData!.Length);
                writer.Write(userData);
            }

            writer.Flush();
        }

        /// 
        /// Checks if a circular reference exists to 'this'
        /// 
        /// 
        /// 
        private bool IsCircular(ClaimsIdentity subject)
        {
            if (ReferenceEquals(this, subject))
            {
                return true;
            }

            ClaimsIdentity currSubject = subject;

            while (currSubject.Actor != null)
            {
                if (ReferenceEquals(this, currSubject.Actor))
                {
                    return true;
                }

                currSubject = currSubject.Actor;
            }

            return false;
        }

        /// 
        /// Populates the specified  with the serialization data for the ClaimsIdentity
        /// 
        /// The serialization information stream to write to. Satisfies ISerializable contract.
        /// Context for serialization. Can be null.
        /// Thrown if the info parameter is null.
        protected virtual void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            throw new PlatformNotSupportedException();
        }
    }

GenericIdentity

要点:

  • IsAuthenticated的内部逻辑:!m_name.Equals("");
    public class GenericIdentity : ClaimsIdentity
    {
        private readonly string m_name;
        private readonly string m_type;

        public GenericIdentity(string name)
        {
            if (name == null)
                throw new ArgumentNullException(nameof(name));

            m_name = name;
            m_type = "";

            AddNameClaim();
        }

        public GenericIdentity(string name, string type)
        {
            if (name == null)
                throw new ArgumentNullException(nameof(name));
            if (type == null)
                throw new ArgumentNullException(nameof(type));

            m_name = name;
            m_type = type;

            AddNameClaim();
        }

        protected GenericIdentity(GenericIdentity identity)
            : base(identity)
        {
            m_name = identity.m_name;
            m_type = identity.m_type;
        }

        /// 
        /// Returns a new instance of  with values copied from this object.
        /// 
        public override ClaimsIdentity Clone()
        {
            return new GenericIdentity(this);
        }

        public override IEnumerable Claims
        {
            get
            {
                return base.Claims;
            }
        }

        public override string Name
        {
            get
            {
                return m_name;
            }
        }

        public override string AuthenticationType
        {
            get
            {
                return m_type;
            }
        }

        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));
            }
        }
    }

ClaimsPrincipal

用来抽象用户

IPrincipal

要点

  • 认证用户必须由一个身份
  • IsInRole:确定当前用户是否被添加到指定角色中。如果是基于用户角色的授权方式,可以直接调用这个方法决定当前用户是否具有访问目标资源的权限。
    public interface IPrincipal
    {
        // Retrieve the identity object
        IIdentity? Identity { get; }

        // Perform a check for a specific role
        bool IsInRole(string role);
    }

ClaimsPrincipal

要点

  • IsInRole内部逻辑:this._identities[index].HasClaim(this._identities[index].RoleClaimType, role

  • 多个身份会有一个首要身份,使用Func, ClaimsIdentity?> SelectPrimaryIdentity来进行查找

  •     /// 
        /// Gets the identity of the current principal.
        /// 
        public virtual System.Security.Principal.IIdentity? Identity
        {
            get
            {
                if (s_identitySelector != null)
                {
                    return s_identitySelector(_identities);
                }
                else
                {
                    return SelectPrimaryIdentity(_identities);
                }
            }
        }
        /// 
        /// This method iterates through the collection of ClaimsIdentities and chooses an identity as the primary.
        /// 
        private static ClaimsIdentity? SelectPrimaryIdentity(IEnumerable identities)
        {
            if (identities == null)
            {
                throw new ArgumentNullException(nameof(identities));
            }
        
            foreach (ClaimsIdentity identity in identities)
            {
                if (identity != null)
                {
                    return identity;
                }
            }
        
            return null;
        }
    

    /// 
    /// Concrete IPrincipal supporting multiple claims-based identities
    /// 
    public class ClaimsPrincipal : IPrincipal
    {
        private enum SerializationMask
        {
            None = 0,
            HasIdentities = 1,
            UserData = 2
        }

        private readonly List _identities = new List();
        private readonly byte[]? _userSerializationData;

        private static Func, ClaimsIdentity?> s_identitySelector = SelectPrimaryIdentity;
        private static Func s_principalSelector = ClaimsPrincipalSelector;

        private static ClaimsPrincipal? SelectClaimsPrincipal()
        {
            // Diverging behavior from .NET Framework: In Framework, the default PrincipalPolicy is
            // UnauthenticatedPrincipal. In .NET Core, the default is NoPrincipal. .NET Framework
            // would throw an ArgumentNullException when constructing the ClaimsPrincipal with a
            // null principal from the thread if it were set to use NoPrincipal. In .NET Core, since
            // NoPrincipal is the default, we return null instead of throw.

            IPrincipal? threadPrincipal = Thread.CurrentPrincipal;

            return threadPrincipal switch {
                ClaimsPrincipal claimsPrincipal => claimsPrincipal,
                not null => new ClaimsPrincipal(threadPrincipal),
                null => null
            };
        }

        protected ClaimsPrincipal(SerializationInfo info, StreamingContext context)
        {
            throw new PlatformNotSupportedException();
        }

        /// 
        /// This method iterates through the collection of ClaimsIdentities and chooses an identity as the primary.
        /// 
        private static ClaimsIdentity? SelectPrimaryIdentity(IEnumerable identities)
        {
            if (identities == null)
            {
                throw new ArgumentNullException(nameof(identities));
            }

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

            return null;
        }

        public static Func, ClaimsIdentity?> PrimaryIdentitySelector
        {
            get
            {
                return s_identitySelector;
            }
            set
            {
                s_identitySelector = value;
            }
        }

        public static Func ClaimsPrincipalSelector
        {
            get
            {
                return s_principalSelector;
            }
            set
            {
                s_principalSelector = value;
            }
        }

        /// 
        /// Initializes an instance of .
        /// 
        public ClaimsPrincipal()
        {
        }

        /// 
        /// Initializes an instance of .
        /// 
        ///   the subjects in the principal.
        /// if 'identities' is null.
        public ClaimsPrincipal(IEnumerable identities)
        {
            if (identities == null)
            {
                throw new ArgumentNullException(nameof(identities));
            }

            _identities.AddRange(identities);
        }

        /// 
        /// Initializes an instance of 
        /// 
        ///   representing the subject in the principal. 
        /// if 'identity' is null.
        public ClaimsPrincipal(IIdentity identity)
        {
            if (identity == null)
            {
                throw new ArgumentNullException(nameof(identity));
            }

            if (identity is ClaimsIdentity ci)
            {
                _identities.Add(ci);
            }
            else
            {
                _identities.Add(new ClaimsIdentity(identity));
            }
        }

        /// 
        /// Initializes an instance of 
        /// 
        ///  used to form this instance.
        /// if 'principal' is null.
        public ClaimsPrincipal(IPrincipal principal)
        {
            if (null == principal)
            {
                throw new ArgumentNullException(nameof(principal));
            }

            //
            // If IPrincipal is a ClaimsPrincipal add all of the identities
            // If IPrincipal is not a ClaimsPrincipal, create a new identity from IPrincipal.Identity
            //
            ClaimsPrincipal? cp = principal as ClaimsPrincipal;
            if (null == cp)
            {
                _identities.Add(new ClaimsIdentity(principal.Identity));
            }
            else
            {
                if (null != cp.Identities)
                {
                    _identities.AddRange(cp.Identities);
                }
            }
        }

        /// 
        /// Initializes an instance of  using a .
        /// Normally the  is constructed using the bytes from  and initialized in the same way as the .
        /// 
        /// a  pointing to a .
        /// if 'reader' is null.
        public ClaimsPrincipal(BinaryReader reader)
        {
            if (reader == null)
            {
                throw new ArgumentNullException(nameof(reader));
            }

            SerializationMask mask = (SerializationMask)reader.ReadInt32();
            int numPropertiesToRead = reader.ReadInt32();
            int numPropertiesRead = 0;
            if ((mask & SerializationMask.HasIdentities) == SerializationMask.HasIdentities)
            {
                numPropertiesRead++;
                int numberOfIdentities = reader.ReadInt32();
                for (int index = 0; index < numberOfIdentities; ++index)
                {
                    // directly add to _identities as that is what we serialized from
                    _identities.Add(CreateClaimsIdentity(reader));
                }
            }

            if ((mask & SerializationMask.UserData) == SerializationMask.UserData)
            {
                int cb = reader.ReadInt32();
                _userSerializationData = reader.ReadBytes(cb);
                numPropertiesRead++;
            }

            for (int i = numPropertiesRead; i < numPropertiesToRead; i++)
            {
                reader.ReadString();
            }
        }

        /// 
        /// Adds a single  to an internal list.
        /// 
        /// the add.
        /// if 'identity' is null.
        public virtual void AddIdentity(ClaimsIdentity identity)
        {
            if (identity == null)
            {
                throw new ArgumentNullException(nameof(identity));
            }

            _identities.Add(identity);
        }

        /// 
        /// Adds a  to the internal list.
        /// 
        /// Enumeration of ClaimsIdentities to add.
        /// if 'identities' is null.
        public virtual void AddIdentities(IEnumerable identities)
        {
            if (identities == null)
            {
                throw new ArgumentNullException(nameof(identities));
            }

            _identities.AddRange(identities);
        }

        /// 
        /// Gets the claims as , associated with this  by enumerating all .
        /// 
        public virtual IEnumerable Claims
        {
            get
            {
                foreach (ClaimsIdentity identity in Identities)
                {
                    foreach (Claim claim in identity.Claims)
                    {
                        yield return claim;
                    }
                }
            }
        }

        /// 
        /// Contains any additional data provided by derived type, typically set when calling .
        /// 
        protected virtual byte[]? CustomSerializationData
        {
            get
            {
                return _userSerializationData;
            }
        }

        /// 
        /// Creates a new instance of  with values copied from this object.
        /// 
        public virtual ClaimsPrincipal Clone()
        {
            return new ClaimsPrincipal(this);
        }

        /// 
        /// Provides an extensibility point for derived types to create a custom .
        /// 
        /// the that points at the claim.
        /// if 'reader' is null.
        /// a new .
        protected virtual ClaimsIdentity CreateClaimsIdentity(BinaryReader reader)
        {
            if (reader == null)
            {
                throw new ArgumentNullException(nameof(reader));
            }

            return new ClaimsIdentity(reader);
        }

        /// 
        /// Returns the Current Principal by calling a delegate.  Users may specify the delegate.
        /// 
        public static ClaimsPrincipal? Current
        {
            // just accesses the current selected principal selector, doesn't set
            get
            {
                return s_principalSelector is not null ? s_principalSelector() : SelectClaimsPrincipal();
            }
        }

        /// 
        /// Retrieves a  where each claim is matched by .
        /// 
        /// The predicate that performs the matching logic.
        /// A  of matched claims.
        /// Each  is called. .
        /// if 'match' is null.
        public virtual IEnumerable FindAll(Predicate match)
        {
            if (match == null)
            {
                throw new ArgumentNullException(nameof(match));
            }

            foreach (ClaimsIdentity identity in Identities)
            {
                if (identity != null)
                {
                    foreach (Claim claim in identity.FindAll(match))
                    {
                        yield return claim;
                    }
                }
            }
        }

        /// 
        /// Retrieves a  where each Claim.Type equals .
        /// 
        /// The type of the claim to match.
        /// A  of matched claims.
        /// Each  is called. .
        /// if 'type' is null.
        public virtual IEnumerable FindAll(string type)
        {
            if (type == null)
            {
                throw new ArgumentNullException(nameof(type));
            }

            foreach (ClaimsIdentity identity in Identities)
            {
                if (identity != null)
                {
                    foreach (Claim claim in identity.FindAll(type))
                    {
                        yield return claim;
                    }
                }
            }
        }

        /// 
        /// Retrieves the first  that is matched by .
        /// 
        /// The predicate that performs the matching logic.
        /// A , null if nothing matches.
        /// Each  is called. .
        /// if 'match' is null.
        public virtual Claim? FindFirst(Predicate match)
        {
            if (match == null)
            {
                throw new ArgumentNullException(nameof(match));
            }

            Claim? claim = null;

            foreach (ClaimsIdentity identity in Identities)
            {
                if (identity != null)
                {
                    claim = identity.FindFirst(match);
                    if (claim != null)
                    {
                        return claim;
                    }
                }
            }

            return claim;
        }

        /// 
        /// Retrieves the first  where the Claim.Type equals .
        /// 
        /// The type of the claim to match.
        /// A , null if nothing matches.
        /// Each  is called. .
        /// if 'type' is null.
        public virtual Claim? FindFirst(string type)
        {
            if (type == null)
            {
                throw new ArgumentNullException(nameof(type));
            }

            Claim? claim = null;

            for (int i = 0; i < _identities.Count; i++)
            {
                if (_identities[i] != null)
                {
                    claim = _identities[i].FindFirst(type);
                    if (claim != null)
                    {
                        return claim;
                    }
                }
            }

            return claim;
        }

        /// 
        /// Determines if a claim is contained within all the ClaimsIdentities in this ClaimPrincipal.
        /// 
        /// The predicate that performs the matching logic.
        /// true if a claim is found, false otherwise.
        /// Each  is called. .
        /// if 'match' is null.
        public virtual bool HasClaim(Predicate match)
        {
            if (match == null)
            {
                throw new ArgumentNullException(nameof(match));
            }

            for (int i = 0; i < _identities.Count; i++)
            {
                if (_identities[i] != null)
                {
                    if (_identities[i].HasClaim(match))
                    {
                        return true;
                    }
                }
            }

            return false;
        }

        /// 
        /// Determines if a claim of claimType AND claimValue exists in any of the identities.
        /// 
        ///  the type of the claim to match.
        ///  the value of the claim to match.
        /// true if a claim is matched, false otherwise.
        /// Each  is called. .
        /// if 'type' is null.
        /// if 'value' is null.
        public virtual bool HasClaim(string type, string value)
        {
            if (type == null)
            {
                throw new ArgumentNullException(nameof(type));
            }

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

            for (int i = 0; i < _identities.Count; i++)
            {
                if (_identities[i] != null)
                {
                    if (_identities[i].HasClaim(type, value))
                    {
                        return true;
                    }
                }
            }

            return false;
        }

        /// 
        /// Collection of 
        /// 
        public virtual IEnumerable Identities
        {
            get
            {
                return _identities;
            }
        }

        /// 
        /// Gets the identity of the current principal.
        /// 
        public virtual System.Security.Principal.IIdentity? Identity
        {
            get
            {
                if (s_identitySelector != null)
                {
                    return s_identitySelector(_identities);
                }
                else
                {
                    return SelectPrimaryIdentity(_identities);
                }
            }
        }

        /// 
        /// IsInRole answers the question: does an identity this principal possesses
        /// contain a claim of type RoleClaimType where the value is '==' to the role.
        /// 
        /// The role to check for.
        /// 'True' if a claim is found. Otherwise 'False'.
        /// Each Identity has its own definition of the ClaimType that represents a role.
        public virtual bool IsInRole(string role)
        {
            for (int i = 0; i < _identities.Count; i++)
            {
                if (_identities[i] != null)
                {
                    if (_identities[i].HasClaim(_identities[i].RoleClaimType, role))
                    {
                        return true;
                    }
                }
            }

            return false;
        }

        /// 
        /// Serializes using a 
        /// 
        /// if 'writer' is null.
        public virtual void WriteTo(BinaryWriter writer)
        {
            WriteTo(writer, null);
        }

        /// 
        /// Serializes using a 
        /// 
        /// the  to use for data storage.
        /// additional data provided by derived type.
        /// if 'writer' is null.
        protected virtual void WriteTo(BinaryWriter writer, byte[]? userData)
        {
            if (writer == null)
            {
                throw new ArgumentNullException(nameof(writer));
            }

            int numberOfPropertiesWritten = 0;
            var mask = SerializationMask.None;
            if (_identities.Count > 0)
            {
                mask |= SerializationMask.HasIdentities;
                numberOfPropertiesWritten++;
            }

            if (userData != null && userData.Length > 0)
            {
                numberOfPropertiesWritten++;
                mask |= SerializationMask.UserData;
            }

            writer.Write((int)mask);
            writer.Write(numberOfPropertiesWritten);
            if ((mask & SerializationMask.HasIdentities) == SerializationMask.HasIdentities)
            {
                writer.Write(_identities.Count);
                foreach (var identity in _identities)
                {
                    identity.WriteTo(writer);
                }
            }

            if ((mask & SerializationMask.UserData) == SerializationMask.UserData)
            {
                writer.Write(userData!.Length);
                writer.Write(userData);
            }

            writer.Flush();
        }

        [OnSerializing]
        private void OnSerializingMethod(StreamingContext context)
        {
            if (this is ISerializable)
            {
                return;
            }

            if (_identities.Count > 0)
            {
                throw new PlatformNotSupportedException(SR.PlatformNotSupported_Serialization); // BinaryFormatter and WindowsIdentity would be needed
            }
        }

        protected virtual void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            throw new PlatformNotSupportedException();
        }
    }

GenericPrincipal

public class GenericPrincipal : ClaimsPrincipal
{
    private readonly IIdentity m_identity;
    private readonly string[]? m_roles;

    public GenericPrincipal(IIdentity identity, string[]? roles)
    {
        if (identity == null)
            throw new ArgumentNullException(nameof(identity));

        m_identity = identity;
        if (roles != null)
        {
            m_roles = (string[])roles.Clone();
        }
        else
        {
            m_roles = null;
        }

        AddIdentityWithRoles(m_identity, m_roles);
    }

    /// 
    /// helper method to add roles
    /// 
    private void AddIdentityWithRoles(IIdentity identity, string[]? roles)
    {
        if (identity is ClaimsIdentity claimsIdentity)
        {
            claimsIdentity = claimsIdentity.Clone();
        }
        else
        {
            claimsIdentity = new ClaimsIdentity(identity);
        }

        // Add 'roles' as external claims so they are not serialized
        if (roles != null && roles.Length > 0)
        {
            List roleClaims = new List(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);
        }

        base.AddIdentity(claimsIdentity);
    }

    public override IIdentity Identity
    {
        get { return m_identity; }
    }

    public override bool IsInRole(string? role)
    {
        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;
        }

        // it may be the case a ClaimsIdentity was passed in as the IIdentity which may have contained claims, they need to be checked.
        return base.IsInRole(role);
    }

    // This is called by AppDomain.GetThreadPrincipal() via reflection.
    private static IPrincipal GetDefaultInstance() => new GenericPrincipal(new GenericIdentity(string.Empty), new string[] { string.Empty });
}

AuthenticationTicket

AuthenticationTicket是对ClaimsPrincipal的封装

AuthenticationTicket

    /// 
    /// Contains user identity information as well as additional authentication state.
    /// 
    public class AuthenticationTicket
    {
        /// 
        /// Initializes a new instance of the  class
        /// 
        /// the  that represents the authenticated user.
        /// additional properties that can be consumed by the user or runtime.
        /// the authentication scheme that was responsible for this ticket.
        public AuthenticationTicket(ClaimsPrincipal principal, AuthenticationProperties? properties, string authenticationScheme)
        {
            if (principal == null)
            {
                throw new ArgumentNullException(nameof(principal));
            }

            AuthenticationScheme = authenticationScheme;
            Principal = principal;
            Properties = properties ?? new AuthenticationProperties();
        }

        /// 
        /// Initializes a new instance of the  class
        /// 
        /// the  that represents the authenticated user.
        /// the authentication scheme that was responsible for this ticket.
        public AuthenticationTicket(ClaimsPrincipal principal, string authenticationScheme) 
            : this(principal, properties: null, authenticationScheme: authenticationScheme)
        { }

        /// 
        /// Gets the authentication scheme that was responsible for this ticket.
        /// 
        public string AuthenticationScheme { get; }

        /// 
        /// Gets the claims-principal with authenticated user identities.
        /// 
        public ClaimsPrincipal Principal { get; }

        /// 
        /// Additional state values for the authentication session.
        /// 
        public AuthenticationProperties Properties { get; }

        /// 
        /// Returns a copy of the ticket.
        /// 
        /// 
        /// The method clones the  by calling  on each of the .
        /// 
        /// A copy of the ticket
        public AuthenticationTicket Clone()
        {
            var principal = new ClaimsPrincipal();
            foreach (var identity in Principal.Identities)
            {
                principal.AddIdentity(identity.Clone());
            }
            return new AuthenticationTicket(principal, Properties.Clone(), AuthenticationScheme);
        }
    }

AuthenticationProperties

包含很多与当提前认证上下文(Authentication Context)或者认证会话(Authentication Session)相关的信息,大部分属性都是对票据的描述。

    /// 
    /// Dictionary used to store state values about the authentication session.
    /// 
    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";
        internal const string UtcDateTimeFormat = "r";

        /// 
        /// Initializes a new instance of the  class.
        /// 
        public AuthenticationProperties()
            : this(items: null, parameters: null)
        { }

        /// 
        /// Initializes a new instance of the  class.
        /// 
        /// State values dictionary to use.
        public AuthenticationProperties(IDictionary items)
            : this(items, parameters: null)
        { }

        /// 
        /// Initializes a new instance of the  class.
        /// 
        /// State values dictionary to use.
        /// Parameters dictionary to use.
        public AuthenticationProperties(IDictionary? items, IDictionary? parameters)
        {
            Items = items ?? new Dictionary(StringComparer.Ordinal);
            Parameters = parameters ?? new Dictionary(StringComparer.Ordinal);
        }

        /// 
        /// Return a copy.
        /// 
        /// A copy.
        public AuthenticationProperties Clone()
            => new AuthenticationProperties(
                new Dictionary(Items, StringComparer.Ordinal),
                new Dictionary(Parameters, StringComparer.Ordinal));

        /// 
        /// State values about the authentication session.
        /// 
        public IDictionary Items { get; }

        /// 
        /// Collection of parameters that are passed to the authentication handler. These are not intended for
        /// serialization or persistence, only for flowing data between call sites.
        /// 
        public IDictionary Parameters { get; }

        /// 
        /// Gets or sets whether the authentication session is persisted across multiple requests.
        /// 
        public bool IsPersistent
        {
            get => GetString(IsPersistentKey) != null;
            set => SetString(IsPersistentKey, value ? string.Empty : null);
        }

        /// 
        /// Gets or sets the full path or absolute URI to be used as an http redirect response value.
        /// 
        public string? RedirectUri
        {
            get => GetString(RedirectUriKey);
            set => SetString(RedirectUriKey, value);
        }

        /// 
        /// Gets or sets the time at which the authentication ticket was issued.
        /// 
        public DateTimeOffset? IssuedUtc
        {
            get => GetDateTimeOffset(IssuedUtcKey);
            set => SetDateTimeOffset(IssuedUtcKey, value);
        }

        /// 
        /// Gets or sets the time at which the authentication ticket expires.
        /// 
        public DateTimeOffset? ExpiresUtc
        {
            get => GetDateTimeOffset(ExpiresUtcKey);
            set => SetDateTimeOffset(ExpiresUtcKey, value);
        }

        /// 
        /// Gets or sets if refreshing the authentication session should be allowed.
        /// 
        public bool? AllowRefresh
        {
            get => GetBool(RefreshKey);
            set => SetBool(RefreshKey, value);
        }

        /// 
        /// Get a string value from the  collection.
        /// 
        /// Property key.
        /// Retrieved value or null if the property is not set.
        public string? GetString(string key)
        {
            return Items.TryGetValue(key, out var value) ? value : null;
        }

        /// 
        /// Set or remove a string value from the  collection.
        /// 
        /// Property key.
        /// Value to set or  to remove the property.
        public void SetString(string key, string? value)
        {
            if (value != null)
            {
                Items[key] = value;
            }
            else
            {
                Items.Remove(key);
            }
        }

        /// 
        /// Get a parameter from the  collection.
        /// 
        /// Parameter type.
        /// Parameter key.
        /// Retrieved value or the default value if the property is not set.
        public T? GetParameter(string key)
            => Parameters.TryGetValue(key, out var obj) && obj is T value ? value : default;

        /// 
        /// Set a parameter value in the  collection.
        /// 
        /// Parameter type.
        /// Parameter key.
        /// Value to set.
        public void SetParameter(string key, T value)
            => Parameters[key] = value;

        /// 
        /// Get a nullable  from the  collection.
        /// 
        /// Property key.
        /// Retrieved value or  if the property is not set.
        protected bool? GetBool(string key)
        {
            if (Items.TryGetValue(key, out var value) && bool.TryParse(value, out var boolValue))
            {
                return boolValue;
            }
            return null;
        }

        /// 
        /// Set or remove a  value in the  collection.
        /// 
        /// Property key.
        /// Value to set or  to remove the property.
        protected void SetBool(string key, bool? value)
        {
            if (value.HasValue)
            {
                Items[key] = value.GetValueOrDefault().ToString();
            }
            else
            {
                Items.Remove(key);
            }
        }

        /// 
        /// Get a nullable  value from the  collection.
        /// 
        /// Property key.
        /// Retrieved value or  if the property is not set.
        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;
        }

        /// 
        /// Sets or removes a  value in the  collection.
        /// 
        /// Property key.
        /// Value to set or  to remove the property.
        protected void SetDateTimeOffset(string key, DateTimeOffset? value)
        {
            if (value.HasValue)
            {
                Items[key] = value.GetValueOrDefault().ToString(UtcDateTimeFormat, CultureInfo.InvariantCulture);
            }
            else
            {
                Items.Remove(key);
            }
        }
    }

TicketDataFormat

对票据进行格式化处理

ISecureDataFormat

    /// 
    /// A contract for securing data.
    /// 
    /// The type of the data to protect.
    public interface ISecureDataFormat
    {
        /// 
        /// Protects the specified .
        /// 
        /// The value to protect
        /// The data protected value.
        string Protect(TData data);

        /// 
        /// Protects the specified  for the specified .
        /// 
        /// The value to protect
        /// The purpose.
        /// A data protected value.
        string Protect(TData data, string? purpose);

        /// 
        /// Unprotects the specified .
        /// 
        /// The data protected value.
        /// An instance of .
        [return: MaybeNull]
        TData Unprotect(string protectedText);

        /// 
        /// Unprotects the specified  using the specified .
        /// 
        /// The data protected value.
        /// The purpose.
        /// An instance of .
        [return: MaybeNull]
        TData Unprotect(string protectedText, string? purpose);
    }

SecureDataFormat

    /// 
    /// An implementation for .
    /// 
    /// 
    public class SecureDataFormat : ISecureDataFormat
    {
        private readonly IDataSerializer _serializer;
        private readonly IDataProtector _protector;

        /// 
        /// Initializes a new instance of .
        /// 
        /// The .
        /// The .
        public SecureDataFormat(IDataSerializer serializer, IDataProtector protector)
        {
            _serializer = serializer;
            _protector = protector;
        }

        /// 
        public string Protect(TData data)
        {
            return Protect(data, purpose: null);
        }

        /// 
        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);
        }

        /// 
        [return: MaybeNull]
        public TData Unprotect(string protectedText)
        {
            return Unprotect(protectedText, purpose: null);
        }

        /// 
        [return: MaybeNull]
        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
            {
                // TODO trace exception, but do not leak other information
                return default(TData);
            }
        }
    }

TicketDataFormat

认证票据是一种私密性数据,需要进行加密和格式化,加密通过IDataProtector接口,格式化通过TiceketSerializer接口

要点TicketSerializer.Default

    /// 
    /// A  instance to secure
    /// .
    /// 
    public class TicketDataFormat : SecureDataFormat
    {
        /// 
        /// Initializes a new instance of .
        /// 
        /// The .
        public TicketDataFormat(IDataProtector protector)
            : base(TicketSerializer.Default, protector)
        {
        }
    }

IDataProtectionProvider

  public interface IDataProtectionProvider
  {
    IDataProtector CreateProtector(string purpose);
  }

IDataProtector

  public interface IDataProtector : IDataProtectionProvider
  {
    byte[] Protect(byte[] plaintext);

    byte[] Unprotect(byte[] protectedData);
  }

IDataSerializer

    /// 
    /// Contract for serialzing authentication data.
    /// 
    /// The type of the model being serialized.
    public interface IDataSerializer
    {
        /// 
        /// Serializes the specified .
        /// 
        /// The value to serialize.
        /// The serialized data.
        byte[] Serialize(TModel model);

        /// 
        /// Deserializes the specified  as an instance of type .
        /// 
        /// The bytes being deserialized.
        /// The model.
        [return: MaybeNull]
        TModel Deserialize(byte[] data);
    }

TiceketSerializer

    // This MUST be kept in sync with Microsoft.Owin.Security.Interop.AspNetTicketSerializer
    /// 
    /// Serializes and deserializes  instances.
    /// 
    public class TicketSerializer : IDataSerializer
    {
        private const string DefaultStringPlaceholder = "\0";
        private const int FormatVersion = 5;

        /// 
        /// Gets the default implementation for .
        /// 
        public static TicketSerializer Default { get; } = new TicketSerializer();

        /// 
        public virtual byte[] Serialize(AuthenticationTicket ticket)
        {
            using (var memory = new MemoryStream())
            {
                using (var writer = new BinaryWriter(memory))
                {
                    Write(writer, ticket);
                }
                return memory.ToArray();
            }
        }

        /// 
        public virtual AuthenticationTicket? Deserialize(byte[] data)
        {
            using (var memory = new MemoryStream(data))
            {
                using (var reader = new BinaryReader(memory))
                {
                    return Read(reader);
                }
            }
        }

        /// 
        /// Writes the  using the specified .
        /// 
        /// The .
        /// The .
        public virtual void Write(BinaryWriter writer, AuthenticationTicket ticket)
        {
            if (writer == null)
            {
                throw new ArgumentNullException(nameof(writer));
            }

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

            writer.Write(FormatVersion);
            writer.Write(ticket.AuthenticationScheme);

            // Write the number of identities contained in the principal.
            var principal = ticket.Principal;
            writer.Write(principal.Identities.Count());

            foreach (var identity in principal.Identities)
            {
                WriteIdentity(writer, identity);
            }

            PropertiesSerializer.Default.Write(writer, ticket.Properties);
        }

        /// 
        /// Writes the specified .
        /// 
        /// The .
        /// The .
        protected virtual void WriteIdentity(BinaryWriter writer, ClaimsIdentity identity)
        {
            if (writer == null)
            {
                throw new ArgumentNullException(nameof(writer));
            }

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

            var authenticationType = identity.AuthenticationType ?? string.Empty;

            writer.Write(authenticationType);
            WriteWithDefault(writer, identity.NameClaimType, ClaimsIdentity.DefaultNameClaimType);
            WriteWithDefault(writer, identity.RoleClaimType, ClaimsIdentity.DefaultRoleClaimType);

            // Write the number of claims contained in the identity.
            writer.Write(identity.Claims.Count());

            foreach (var claim in identity.Claims)
            {
                WriteClaim(writer, claim);
            }

            var bootstrap = identity.BootstrapContext as string;
            if (!string.IsNullOrEmpty(bootstrap))
            {
                writer.Write(true);
                writer.Write(bootstrap);
            }
            else
            {
                writer.Write(false);
            }

            if (identity.Actor != null)
            {
                writer.Write(true);
                WriteIdentity(writer, identity.Actor);
            }
            else
            {
                writer.Write(false);
            }
        }

        /// 
        protected virtual void WriteClaim(BinaryWriter writer, Claim claim)
        {
            if (writer == null)
            {
                throw new ArgumentNullException(nameof(writer));
            }

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

            WriteWithDefault(writer, claim.Type, claim.Subject?.NameClaimType ?? ClaimsIdentity.DefaultNameClaimType);
            writer.Write(claim.Value);
            WriteWithDefault(writer, claim.ValueType, ClaimValueTypes.String);
            WriteWithDefault(writer, claim.Issuer, ClaimsIdentity.DefaultIssuer);
            WriteWithDefault(writer, claim.OriginalIssuer, claim.Issuer);

            // Write the number of properties contained in the claim.
            writer.Write(claim.Properties.Count);

            foreach (var property in claim.Properties)
            {
                writer.Write(property.Key ?? string.Empty);
                writer.Write(property.Value ?? string.Empty);
            }
        }

        /// 
        /// Reads an .
        /// 
        /// The .
        /// The  if the format is supported, otherwise .
        public virtual AuthenticationTicket? Read(BinaryReader reader)
        {
            if (reader == null)
            {
                throw new ArgumentNullException(nameof(reader));
            }

            if (reader.ReadInt32() != FormatVersion)
            {
                return null;
            }

            var scheme = reader.ReadString();

            // Read the number of identities stored
            // in the serialized payload.
            var count = reader.ReadInt32();
            if (count < 0)
            {
                return null;
            }

            var identities = new ClaimsIdentity[count];
            for (var index = 0; index != count; ++index)
            {
                identities[index] = ReadIdentity(reader);
            }

            var properties = PropertiesSerializer.Default.Read(reader);

            return new AuthenticationTicket(new ClaimsPrincipal(identities), properties, scheme);
        }

        /// 
        /// Reads a  from a .
        /// 
        /// The .
        /// The read .
        protected virtual ClaimsIdentity ReadIdentity(BinaryReader reader)
        {
            if (reader == null)
            {
                throw new ArgumentNullException(nameof(reader));
            }

            var authenticationType = reader.ReadString();
            var nameClaimType = ReadWithDefault(reader, ClaimsIdentity.DefaultNameClaimType);
            var roleClaimType = ReadWithDefault(reader, ClaimsIdentity.DefaultRoleClaimType);

            // Read the number of claims contained
            // in the serialized identity.
            var count = reader.ReadInt32();

            var identity = new ClaimsIdentity(authenticationType, nameClaimType, roleClaimType);

            for (int index = 0; index != count; ++index)
            {
                var claim = ReadClaim(reader, identity);

                identity.AddClaim(claim);
            }

            // Determine whether the identity
            // has a bootstrap context attached.
            if (reader.ReadBoolean())
            {
                identity.BootstrapContext = reader.ReadString();
            }

            // Determine whether the identity
            // has an actor identity attached.
            if (reader.ReadBoolean())
            {
                identity.Actor = ReadIdentity(reader);
            }

            return identity;
        }

        /// 
        /// Reads a  and adds it to the specified .
        /// 
        /// The .
        /// The  to add the claim to.
        /// The read .
        protected virtual Claim ReadClaim(BinaryReader reader, ClaimsIdentity identity)
        {
            if (reader == null)
            {
                throw new ArgumentNullException(nameof(reader));
            }

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

            var type = ReadWithDefault(reader, identity.NameClaimType);
            var value = reader.ReadString();
            var valueType = ReadWithDefault(reader, ClaimValueTypes.String);
            var issuer = ReadWithDefault(reader, ClaimsIdentity.DefaultIssuer);
            var originalIssuer = ReadWithDefault(reader, issuer);

            var claim = new Claim(type, value, valueType, issuer, originalIssuer, identity);

            // Read the number of properties stored in the claim.
            var count = reader.ReadInt32();

            for (var index = 0; index != count; ++index)
            {
                var key = reader.ReadString();
                var propertyValue = reader.ReadString();

                claim.Properties.Add(key, propertyValue);
            }

            return claim;
        }

        private static void WriteWithDefault(BinaryWriter writer, string value, string defaultValue)
        {
            if (string.Equals(value, defaultValue, StringComparison.Ordinal))
            {
                writer.Write(DefaultStringPlaceholder);
            }
            else
            {
                writer.Write(value);
            }
        }

        private static string ReadWithDefault(BinaryReader reader, string defaultValue)
        {
            var value = reader.ReadString();
            if (string.Equals(value, DefaultStringPlaceholder, StringComparison.Ordinal))
            {
                return defaultValue;
            }
            return value;
        }
    }

认证模型

模型概览

认证模型主要是依靠AuthorizationMiddleware中间件来实现的,

  • Scope创建AuthenticationHandlerProvider实例,

  • 通过AuthenticationSchemeProvider的GetRequestHandlerSchemesAsync方法获取AuthenticationScheme对象,根据AuthenticationScheme对象,从AuthenticationHandlerProvider中先获取IAuthenticationRequestHandler,

  • 调用AuthenticationSchemeProvider的GetDefaultAuthenticateSchemeAsync方法,再获取AuthenticationScheme对象,从通过context的AuthenticateAsync进行处理,实际是调用了AuthenticationService的AuthenticateAsync

  • 如果result的Principal对象存在就将其赋给context.User属性。认证完成。

认证模式:

  • 质询无效(401 Unauthorized):Task ChallengeAsync(AuthenticationProperties? properties);
  • 质询禁止(403 Forbidden):Task ForbidAsync(AuthenticationProperties? properties);
  • 认证(AuthenticateAsync):Task AuthenticateAsync();
  • 登录(SignIn):Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties properties);
  • 登出(SignOut): Task SignOutAsync(AuthenticationProperties properties);

AuthenticationSchemeProvider

AuthenticationScheme

认证方案名称,认证处理器的类型,

    /// 
    /// AuthenticationSchemes assign a name to a specific 
    /// handlerType.
    /// 
    public class AuthenticationScheme
    {
        /// 
        /// Initializes a new instance of .
        /// 
        /// The name for the authentication scheme.
        /// The display name for the authentication scheme.
        /// The  type that handles this scheme.
        public AuthenticationScheme(string name, string? displayName, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type handlerType)
        {
            if (name == null)
            {
                throw new ArgumentNullException(nameof(name));
            }
            if (handlerType == null)
            {
                throw new ArgumentNullException(nameof(handlerType));
            }
            if (!typeof(IAuthenticationHandler).IsAssignableFrom(handlerType))
            {
                throw new ArgumentException("handlerType must implement IAuthenticationHandler.");
            }

            Name = name;
            HandlerType = handlerType;
            DisplayName = displayName;
        }

        /// 
        /// The name of the authentication scheme.
        /// 
        public string Name { get; }

        /// 
        /// The display name for the scheme. Null is valid and used for non user facing schemes.
        /// 
        public string? DisplayName { get; }

        /// 
        /// The  type that handles this scheme.
        /// 
        [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
        public Type HandlerType { get; }
    }

AuthenticateOptions

存放着AuthenticationSchemeBuilder

    public class AuthenticationOptions
    {
        private readonly IList _schemes = new List();

        /// 
        /// Returns the schemes in the order they were added (important for request handling priority)
        /// 
        public IEnumerable Schemes => _schemes;

        /// 
        /// Maps schemes by name.
        /// 
        public IDictionary SchemeMap { get; } = new Dictionary(StringComparer.Ordinal);

        /// 
        /// Adds an .
        /// 
        /// The name of the scheme being added.
        /// Configures the scheme.
        public void AddScheme(string name, Action configureBuilder)
        {
            if (name == null)
            {
                throw new ArgumentNullException(nameof(name));
            }
            if (configureBuilder == null)
            {
                throw new ArgumentNullException(nameof(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;
        }

        /// 
        /// Adds an .
        /// 
        /// The  responsible for the scheme.
        /// The name of the scheme being added.
        /// The display name for the scheme.
        public void AddScheme(string name, string displayName) where THandler : IAuthenticationHandler
            => AddScheme(name, b =>
            {
                b.DisplayName = displayName;
                b.HandlerType = typeof(THandler);
            });

        /// 
        /// Used as the fallback default scheme for all the other defaults.
        /// 
        public string DefaultScheme { get; set; }

        /// 
        /// Used as the default scheme by .
        /// 
        public string DefaultAuthenticateScheme { get; set; }

        /// 
        /// Used as the default scheme by .
        /// 
        public string DefaultSignInScheme { get; set; }

        /// 
        /// Used as the default scheme by .
        /// 
        public string DefaultSignOutScheme { get; set; }

        /// 
        /// Used as the default scheme by .
        /// 
        public string DefaultChallengeScheme { get; set; }

        /// 
        /// Used as the default scheme by .
        /// 
        public string DefaultForbidScheme { get; set; }

        /// 
        /// If true, SignIn should throw if attempted with a ClaimsPrincipal.Identity.IsAuthenticated = false.
        /// 
        public bool RequireAuthenticatedSignIn { get; set; } = true;
    }

AuthenticationSchemeBuilder

  public class AuthenticationSchemeBuilder
  {
    public AuthenticationSchemeBuilder(string name) => this.Name = name;

    public string Name { get; }

    public string DisplayName { get; set; }

    public Type HandlerType { get; set; }

    public AuthenticationScheme Build() => new AuthenticationScheme(this.Name, this.DisplayName, this.HandlerType);
  }

IAuthenticationSchemeProvider

    /// 
    /// Responsible for managing what authenticationSchemes are supported.
    /// 
    public interface IAuthenticationSchemeProvider
    {
        /// 
        /// Returns all currently registered s.
        /// 
        /// All currently registered s.
        Task> GetAllSchemesAsync();

        /// 
        /// Returns the  matching the name, or null.
        /// 
        /// The name of the authenticationScheme.
        /// The scheme or null if not found.
        Task GetSchemeAsync(string name);

        /// 
        /// Returns the scheme that will be used by default for .
        /// This is typically specified via .
        /// Otherwise, this will fallback to .
        /// 
        /// The scheme that will be used by default for .
        Task GetDefaultAuthenticateSchemeAsync();

        /// 
        /// Returns the scheme that will be used by default for .
        /// This is typically specified via .
        /// Otherwise, this will fallback to .
        /// 
        /// The scheme that will be used by default for .
        Task GetDefaultChallengeSchemeAsync();

        /// 
        /// Returns the scheme that will be used by default for .
        /// This is typically specified via .
        /// Otherwise, this will fallback to  .
        /// 
        /// The scheme that will be used by default for .
        Task GetDefaultForbidSchemeAsync();

        /// 
        /// Returns the scheme that will be used by default for .
        /// This is typically specified via .
        /// Otherwise, this will fallback to .
        /// 
        /// The scheme that will be used by default for .
        Task GetDefaultSignInSchemeAsync();

        /// 
        /// Returns the scheme that will be used by default for .
        /// This is typically specified via .
        /// Otherwise, this will fallback to  .
        /// 
        /// The scheme that will be used by default for .
        Task GetDefaultSignOutSchemeAsync();

        /// 
        /// Registers a scheme for use by . 
        /// 
        /// The scheme.
        void AddScheme(AuthenticationScheme scheme);

        /// 
        /// Removes a scheme, preventing it from being used by .
        /// 
        /// The name of the authenticationScheme being removed.
        void RemoveScheme(string name);

        /// 
        /// Returns the schemes in priority order for request handling.
        /// 
        /// The schemes in priority order for request handling
        Task> GetRequestHandlerSchemesAsync();
    }

AuthenticationSchemeProvider

通过AuthenticateOptions中的AuthenticationSchemeBuilder来获取AuthenticationScheme

    /// 
    /// Implements .
    /// 
    public class AuthenticationSchemeProvider : IAuthenticationSchemeProvider
    {
        /// 
        /// Creates an instance of 
        /// using the specified ,
        /// 
        /// The  options.
        public AuthenticationSchemeProvider(IOptions options)
            : this(options, new Dictionary(StringComparer.Ordinal))
        {
        }

        /// 
        /// Creates an instance of 
        /// using the specified  and .
        /// 
        /// The  options.
        /// The dictionary used to store authentication schemes.
        protected AuthenticationSchemeProvider(IOptions options, IDictionary schemes)
        {
            _options = options.Value;

            _schemes = schemes ?? throw new ArgumentNullException(nameof(schemes));
            _requestHandlers = new List();

            foreach (var builder in _options.Schemes)
            {
                var scheme = builder.Build();
                AddScheme(scheme);
            }
        }

        private readonly AuthenticationOptions _options;
        private readonly object _lock = new object();

        private readonly IDictionary _schemes;
        private readonly List _requestHandlers;
        // Used as a safe return value for enumeration apis
        private IEnumerable _schemesCopy = Array.Empty();
        private IEnumerable _requestHandlersCopy = Array.Empty();

        private Task GetDefaultSchemeAsync()
            => _options.DefaultScheme != null
            ? GetSchemeAsync(_options.DefaultScheme)
            : Task.FromResult(null);

        /// 
        /// Returns the scheme that will be used by default for .
        /// This is typically specified via .
        /// Otherwise, this will fallback to .
        /// 
        /// The scheme that will be used by default for .
        public virtual Task GetDefaultAuthenticateSchemeAsync()
            => _options.DefaultAuthenticateScheme != null
            ? GetSchemeAsync(_options.DefaultAuthenticateScheme)
            : GetDefaultSchemeAsync();

        /// 
        /// Returns the scheme that will be used by default for .
        /// This is typically specified via .
        /// Otherwise, this will fallback to .
        /// 
        /// The scheme that will be used by default for .
        public virtual Task GetDefaultChallengeSchemeAsync()
            => _options.DefaultChallengeScheme != null
            ? GetSchemeAsync(_options.DefaultChallengeScheme)
            : GetDefaultSchemeAsync();

        /// 
        /// Returns the scheme that will be used by default for .
        /// This is typically specified via .
        /// Otherwise, this will fallback to  .
        /// 
        /// The scheme that will be used by default for .
        public virtual Task GetDefaultForbidSchemeAsync()
            => _options.DefaultForbidScheme != null
            ? GetSchemeAsync(_options.DefaultForbidScheme)
            : GetDefaultChallengeSchemeAsync();

        /// 
        /// Returns the scheme that will be used by default for .
        /// This is typically specified via .
        /// Otherwise, this will fallback to .
        /// 
        /// The scheme that will be used by default for .
        public virtual Task GetDefaultSignInSchemeAsync()
            => _options.DefaultSignInScheme != null
            ? GetSchemeAsync(_options.DefaultSignInScheme)
            : GetDefaultSchemeAsync();

        /// 
        /// Returns the scheme that will be used by default for .
        /// This is typically specified via .
        /// Otherwise this will fallback to  if that supports sign out.
        /// 
        /// The scheme that will be used by default for .
        public virtual Task GetDefaultSignOutSchemeAsync()
            => _options.DefaultSignOutScheme != null
            ? GetSchemeAsync(_options.DefaultSignOutScheme)
            : GetDefaultSignInSchemeAsync();

        /// 
        /// Returns the  matching the name, or null.
        /// 
        /// The name of the authenticationScheme.
        /// The scheme or null if not found.
        public virtual Task GetSchemeAsync(string name)
            => Task.FromResult(_schemes.ContainsKey(name) ? _schemes[name] : null);

        /// 
        /// Returns the schemes in priority order for request handling.
        /// 
        /// The schemes in priority order for request handling
        public virtual Task> GetRequestHandlerSchemesAsync()
            => Task.FromResult(_requestHandlersCopy);

        /// 
        /// Registers a scheme for use by . 
        /// 
        /// The scheme.
        public virtual void AddScheme(AuthenticationScheme scheme)
        {
            if (_schemes.ContainsKey(scheme.Name))
            {
                throw new InvalidOperationException("Scheme already exists: " + scheme.Name);
            }
            lock (_lock)
            {
                if (_schemes.ContainsKey(scheme.Name))
                {
                    throw new InvalidOperationException("Scheme already exists: " + scheme.Name);
                }
                if (typeof(IAuthenticationRequestHandler).IsAssignableFrom(scheme.HandlerType))
                {
                    _requestHandlers.Add(scheme);
                    _requestHandlersCopy = _requestHandlers.ToArray();
                }
                _schemes[scheme.Name] = scheme;
                _schemesCopy = _schemes.Values.ToArray();
            }
        }

        /// 
        /// Removes a scheme, preventing it from being used by .
        /// 
        /// The name of the authenticationScheme being removed.
        public virtual void RemoveScheme(string name)
        {
            if (!_schemes.ContainsKey(name))
            {
                return;
            }
            lock (_lock)
            {
                if (_schemes.ContainsKey(name))
                {
                    var scheme = _schemes[name];
                    if (_requestHandlers.Remove(scheme))
                    {
                        _requestHandlersCopy = _requestHandlers.ToArray();
                    }
                    _schemes.Remove(name);
                    _schemesCopy = _schemes.Values.ToArray();
                }
            }
        }

        public virtual Task> GetAllSchemesAsync()
            => Task.FromResult(_schemesCopy);
    }

AuthenticationHandlerProvider

提供Handler,对应5种认证方法

IAuthenticationHandlerProvider

    /// Responsible for managing what authenticationSchemes are supported.
    /// 
    public interface IAuthenticationSchemeProvider
    {
        /// 
        /// Returns all currently registered s.
        /// 
        /// All currently registered s.
        Task> GetAllSchemesAsync();

        /// 
        /// Returns the  matching the name, or null.
        /// 
        /// The name of the authenticationScheme.
        /// The scheme or null if not found.
        Task GetSchemeAsync(string name);

        /// 
        /// Returns the scheme that will be used by default for .
        /// This is typically specified via .
        /// Otherwise, this will fallback to .
        /// 
        /// The scheme that will be used by default for .
        Task GetDefaultAuthenticateSchemeAsync();

        /// 
        /// Returns the scheme that will be used by default for .
        /// This is typically specified via .
        /// Otherwise, this will fallback to .
        /// 
        /// The scheme that will be used by default for .
        Task GetDefaultChallengeSchemeAsync();

        /// 
        /// Returns the scheme that will be used by default for .
        /// This is typically specified via .
        /// Otherwise, this will fallback to  .
        /// 
        /// The scheme that will be used by default for .
        Task GetDefaultForbidSchemeAsync();

        /// 
        /// Returns the scheme that will be used by default for .
        /// This is typically specified via .
        /// Otherwise, this will fallback to .
        /// 
        /// The scheme that will be used by default for .
        Task GetDefaultSignInSchemeAsync();

        /// 
        /// Returns the scheme that will be used by default for .
        /// This is typically specified via .
        /// Otherwise, this will fallback to  .
        /// 
        /// The scheme that will be used by default for .
        Task GetDefaultSignOutSchemeAsync();

        /// 
        /// Registers a scheme for use by . 
        /// 
        /// The scheme.
        void AddScheme(AuthenticationScheme scheme);

        /// 
        /// Registers a scheme for use by . 
        /// 
        /// The scheme.
        /// true if the scheme was added successfully.
        bool TryAddScheme(AuthenticationScheme scheme)
        {
            try
            {
                AddScheme(scheme);
                return true;
            }
            catch {
                return false;
            }
        }

        /// 
        /// Removes a scheme, preventing it from being used by .
        /// 
        /// The name of the authenticationScheme being removed.
        void RemoveScheme(string name);

        /// 
        /// Returns the schemes in priority order for request handling.
        /// 
        /// The schemes in priority order for request handling
        Task> GetRequestHandlerSchemesAsync();
    }

AuthenticationHandlerProvider

通过AuthenticationScheme反射创建IAuthenticationHandler对象,并进行缓存

    /// 
    /// Implementation of .
    /// 
    public class AuthenticationHandlerProvider : IAuthenticationHandlerProvider
    {
        /// 
        /// Constructor.
        /// 
        /// The .
        public AuthenticationHandlerProvider(IAuthenticationSchemeProvider schemes)
        {
            Schemes = schemes;
        }

        /// 
        /// The .
        /// 
        public IAuthenticationSchemeProvider Schemes { get; }

        // handler instance cache, need to initialize once per request
        private Dictionary _handlerMap = new Dictionary(StringComparer.Ordinal);

        /// 
        /// Returns the handler instance that will be used.
        /// 
        /// The context.
        /// The name of the authentication scheme being handled.
        /// The handler instance.
        public async Task GetHandlerAsync(HttpContext context, string authenticationScheme)
        {
            if (_handlerMap.ContainsKey(authenticationScheme))
            {
                return _handlerMap[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;
        }
    }

IAuthenticationHandler

/// 
/// Created per request to handle authentication for to a particular scheme.
/// 
public interface IAuthenticationHandler
{
    /// 
    /// The handler should initialize anything it needs from the request and scheme here.
    /// 
    /// The  scheme.
    /// The  context.
    /// 
    Task InitializeAsync(AuthenticationScheme scheme, HttpContext context);

    /// 
    /// Authentication behavior.
    /// 
    /// The  result.
    Task AuthenticateAsync();

    /// 
    /// Challenge behavior.
    /// 
    /// The  that contains the extra meta-data arriving with the authentication.
    /// A task.
    Task ChallengeAsync(AuthenticationProperties properties);

    /// 
    /// Forbid behavior.
    /// 
    /// The  that contains the extra meta-data arriving with the authentication.
    /// A task.
    Task ForbidAsync(AuthenticationProperties properties);
}

AuthenticationHandler

public abstract class AuthenticationHandler : IAuthenticationHandler where TOptions : AuthenticationSchemeOptions, new()
{
    private Task _authenticateTask;

    public AuthenticationScheme Scheme { get; private set; }
    public TOptions Options { get; private set; }
    protected HttpContext Context { get; private set; }

    protected HttpRequest Request
    {
        get => Context.Request;
    }

    protected HttpResponse Response
    {
        get => Context.Response;
    }

    protected PathString OriginalPath => Context.Features.Get()?.OriginalPath ?? Request.Path;

    protected PathString OriginalPathBase => Context.Features.Get()?.OriginalPathBase ?? Request.PathBase;

    protected ILogger Logger { get; }

    protected UrlEncoder UrlEncoder { get; }

    protected ISystemClock Clock { get; }

    protected IOptionsMonitor OptionsMonitor { get; }

    /// 
    /// The handler calls methods on the events which give the application control at certain points where processing is occurring. 
    /// If it is not provided a default instance is supplied which does nothing when the methods are called.
    /// 
    protected virtual object Events { get; set; }

    protected virtual string ClaimsIssuer => Options.ClaimsIssuer ?? Scheme.Name;

    protected string CurrentUri
    {
        get => Request.Scheme + "://" + Request.Host + Request.PathBase + Request.Path + Request.QueryString;
    }

    protected AuthenticationHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
    {
        Logger = logger.CreateLogger(this.GetType().FullName);
        UrlEncoder = encoder;
        Clock = clock;
        OptionsMonitor = options;
    }

    /// 
    /// Initialize the handler, resolve the options and validate them.
    /// 
    /// 
    /// 
    /// 
    public async Task InitializeAsync(AuthenticationScheme scheme, HttpContext context)
    {
        if (scheme == null)
        {
            throw new ArgumentNullException(nameof(scheme));
        }
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        Scheme = scheme;
        Context = context;

        Options = OptionsMonitor.Get(Scheme.Name);

        await InitializeEventsAsync();
        await InitializeHandlerAsync();
    }

    /// 
    /// Initializes the events object, called once per request by .
    /// 
    protected virtual async Task InitializeEventsAsync()
    {
        Events = Options.Events;
        if (Options.EventsType != null)
        {
            Events = Context.RequestServices.GetRequiredService(Options.EventsType);
        }
        Events = Events ?? await CreateEventsAsync();
    }

    /// 
    /// Creates a new instance of the events instance.
    /// 
    /// A new instance of the events instance.
    protected virtual Task CreateEventsAsync() => Task.FromResult(new object());

    /// 
    /// Called after options/events have been initialized for the handler to finish initializing itself.
    /// 
    /// A task
    protected virtual Task InitializeHandlerAsync() => Task.CompletedTask;

    protected string BuildRedirectUri(string targetPath)
        => Request.Scheme + "://" + Request.Host + OriginalPathBase + targetPath;

    protected virtual string ResolveTarget(string scheme)
    {
        var target = scheme ?? Options.ForwardDefaultSelector?.Invoke(Context) ?? Options.ForwardDefault;

        // Prevent self targetting
        return string.Equals(target, Scheme.Name, StringComparison.Ordinal)
            ? null
            : target;
    }

    public async Task AuthenticateAsync()
    {
        var target = ResolveTarget(Options.ForwardAuthenticate);
        if (target != null)
        {
            return await Context.AuthenticateAsync(target);
        }

        // Calling Authenticate more than once should always return the original value.
        var result = await HandleAuthenticateOnceAsync();
        if (result?.Failure == null)
        {
            var ticket = result?.Ticket;
            if (ticket?.Principal != null)
            {
                Logger.AuthenticationSchemeAuthenticated(Scheme.Name);
            }
            else
            {
                Logger.AuthenticationSchemeNotAuthenticated(Scheme.Name);
            }
        }
        else
        {
            Logger.AuthenticationSchemeNotAuthenticatedWithFailure(Scheme.Name, result.Failure.Message);
        }
        return result;
    }

    /// 
    /// Used to ensure HandleAuthenticateAsync is only invoked once. The subsequent calls
    /// will return the same authenticate result.
    /// 
    protected Task HandleAuthenticateOnceAsync()
    {
        if (_authenticateTask == null)
        {
            _authenticateTask = HandleAuthenticateAsync();
        }

        return _authenticateTask;
    }

    /// 
    /// Used to ensure HandleAuthenticateAsync is only invoked once safely. The subsequent
    /// calls will return the same authentication result. Any exceptions will be converted
    /// into a failed authentication result containing the exception.
    /// 
    protected async Task HandleAuthenticateOnceSafeAsync()
    {
        try
        {
            return await HandleAuthenticateOnceAsync();
        }
        catch (Exception ex)
        {
            return AuthenticateResult.Fail(ex);
        }
    }

    protected abstract Task HandleAuthenticateAsync();

    /// 
    /// Override this method to handle Forbid.
    /// 
    /// 
    /// A Task.
    protected virtual Task HandleForbiddenAsync(AuthenticationProperties properties)
    {
        Response.StatusCode = 403;
        return Task.CompletedTask;
    }

    /// 
    /// Override this method to deal with 401 challenge concerns, if an authentication scheme in question
    /// deals an authentication interaction as part of it's request flow. (like adding a response header, or
    /// changing the 401 result to 302 of a login page or external sign-in location.)
    /// 
    /// 
    /// A Task.
    protected virtual Task HandleChallengeAsync(AuthenticationProperties properties)
    {
        Response.StatusCode = 401;
        return Task.CompletedTask;
    }

    public async Task ChallengeAsync(AuthenticationProperties properties)
    {
        var target = ResolveTarget(Options.ForwardChallenge);
        if (target != null)
        {
            await Context.ChallengeAsync(target, properties);
            return;
        }

        properties = properties ?? new AuthenticationProperties();
        await HandleChallengeAsync(properties);
        Logger.AuthenticationSchemeChallenged(Scheme.Name);
    }

    public async Task ForbidAsync(AuthenticationProperties properties)
    {
        var target = ResolveTarget(Options.ForwardForbid);
        if (target != null)
        {
            await Context.ForbidAsync(target, properties);
            return;
        }

        properties = properties ?? new AuthenticationProperties();
        await HandleForbiddenAsync(properties);
        Logger.AuthenticationSchemeForbidden(Scheme.Name);
    }
}

AuthenticationSchemeOptions

认证处理器的配置

    /// 
    /// Contains the options used by the .
    /// 
    public class AuthenticationSchemeOptions
    {
        /// 
        /// Check that the options are valid. Should throw an exception if things are not ok.
        /// 
        public virtual void Validate() { }

        /// 
        /// Checks that the options are valid for a specific scheme
        /// 
        /// The scheme being validated.
        public virtual void Validate(string scheme)
            => Validate();

        /// 
        /// Gets or sets the issuer that should be used for any claims that are created
        /// 
        public string ClaimsIssuer { get; set; }

        /// 
        /// Instance used for events
        /// 
        public object Events { get; set; }

        /// 
        /// If set, will be used as the service type to get the Events instance instead of the property.
        /// 
        public Type EventsType { get; set; }

        /// 
        /// If set, this specifies a default scheme that authentication handlers should forward all authentication operations to
        /// by default. The default forwarding logic will check the most specific ForwardAuthenticate/Challenge/Forbid/SignIn/SignOut
        /// setting first, followed by checking the ForwardDefaultSelector, followed by ForwardDefault. The first non null result
        /// will be used as the target scheme to forward to.
        /// 
        public string ForwardDefault { get; set; }

        /// 
        /// If set, this specifies the target scheme that this scheme should forward AuthenticateAsync calls to.
        /// For example Context.AuthenticateAsync("ThisScheme") => Context.AuthenticateAsync("ForwardAuthenticateValue");
        /// Set the target to the current scheme to disable forwarding and allow normal processing.
        /// 
        public string ForwardAuthenticate { get; set; }

        /// 
        /// If set, this specifies the target scheme that this scheme should forward ChallengeAsync calls to.
        /// For example Context.ChallengeAsync("ThisScheme") => Context.ChallengeAsync("ForwardChallengeValue");
        /// Set the target to the current scheme to disable forwarding and allow normal processing.
        /// 
        public string ForwardChallenge { get; set; }

        /// 
        /// If set, this specifies the target scheme that this scheme should forward ForbidAsync calls to.
        /// For example Context.ForbidAsync("ThisScheme") => Context.ForbidAsync("ForwardForbidValue");
        /// Set the target to the current scheme to disable forwarding and allow normal processing.
        /// 
        public string ForwardForbid { get; set; }

        /// 
        /// If set, this specifies the target scheme that this scheme should forward SignInAsync calls to.
        /// For example Context.SignInAsync("ThisScheme") => Context.SignInAsync("ForwardSignInValue");
        /// Set the target to the current scheme to disable forwarding and allow normal processing.
        /// 
        public string ForwardSignIn { get; set; }

        /// 
        /// If set, this specifies the target scheme that this scheme should forward SignOutAsync calls to.
        /// For example Context.SignOutAsync("ThisScheme") => Context.SignOutAsync("ForwardSignOutValue");
        /// Set the target to the current scheme to disable forwarding and allow normal processing.
        /// 
        public string ForwardSignOut { get; set; }

        /// 
        /// Used to select a default scheme for the current request that authentication handlers should forward all authentication operations to
        /// by default. The default forwarding logic will check the most specific ForwardAuthenticate/Challenge/Forbid/SignIn/SignOut
        /// setting first, followed by checking the ForwardDefaultSelector, followed by ForwardDefault. The first non null result
        /// will be used as the target scheme to forward to.
        /// 
        public Func ForwardDefaultSelector { get; set; }

    }

SignOutAuthenticationHandler

    /// 
    /// Used to determine if a handler supports SignOut.
    /// 
    public interface IAuthenticationSignOutHandler : IAuthenticationHandler
    {
        /// 
        /// Signout behavior.
        /// 
        /// The  that contains the extra meta-data arriving with the authentication.
        /// A task.
        Task SignOutAsync(AuthenticationProperties properties);
    }
    /// 
    /// Adds support for SignOutAsync
    /// 
    public abstract class SignOutAuthenticationHandler : AuthenticationHandler, IAuthenticationSignOutHandler
        where TOptions : AuthenticationSchemeOptions, new()
    {
        public SignOutAuthenticationHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
        { }

        public virtual Task SignOutAsync(AuthenticationProperties properties)
        {
            var target = ResolveTarget(Options.ForwardSignOut);
            return (target != null)
                ? Context.SignOutAsync(target, properties)
                : HandleSignOutAsync(properties ?? new AuthenticationProperties());
        }

        /// 
        /// Override this method to handle SignOut.
        /// 
        /// 
        /// A Task.
        protected abstract Task HandleSignOutAsync(AuthenticationProperties properties);
    }

SignInAuthenticationHandler

    /// 
    /// Used to determine if a handler supports SignIn.
    /// 
    public interface IAuthenticationSignInHandler : IAuthenticationSignOutHandler
    {
        /// 
        /// Handle sign in.
        /// 
        /// The  user.
        /// The  that contains the extra meta-data arriving with the authentication.
        /// A task.
        Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties properties);
    }
    /// 
    /// Adds support for SignInAsync
    /// 
    public abstract class SignInAuthenticationHandler : SignOutAuthenticationHandler, IAuthenticationSignInHandler
        where TOptions : AuthenticationSchemeOptions, new()
    {
        public SignInAuthenticationHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
        { }

        public virtual Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties properties)
        {
            var target = ResolveTarget(Options.ForwardSignIn);
            return (target != null)
                ? Context.SignInAsync(target, user, properties)
                : HandleSignInAsync(user, properties ?? new AuthenticationProperties());
        }

        /// 
        /// Override this method to handle SignIn.
        /// 
        /// 
        /// 
        /// A Task.
        protected abstract Task HandleSignInAsync(ClaimsPrincipal user, AuthenticationProperties properties);

    }

AuthenticationService

认证服务

    /// 
    /// Implements .
    /// 
    public class AuthenticationService : IAuthenticationService
    {
        /// 
        /// Constructor.
        /// 
        /// The .
        /// The .
        /// The .
        /// The .
        public AuthenticationService(IAuthenticationSchemeProvider schemes, IAuthenticationHandlerProvider handlers, IClaimsTransformation transform, IOptions options)
        {
            Schemes = schemes;
            Handlers = handlers;
            Transform = transform;
            Options = options.Value;
        }

        /// 
        /// Used to lookup AuthenticationSchemes.
        /// 
        public IAuthenticationSchemeProvider Schemes { get; }

        /// 
        /// Used to resolve IAuthenticationHandler instances.
        /// 
        public IAuthenticationHandlerProvider Handlers { get; }

        /// 
        /// Used for claims transformation.
        /// 
        public IClaimsTransformation Transform { get; }

        /// 
        /// The .
        /// 
        public AuthenticationOptions Options { get; }

        /// 
        /// Authenticate for the specified authentication scheme.
        /// 
        /// The .
        /// The name of the authentication scheme.
        /// The result.
        public virtual async Task AuthenticateAsync(HttpContext context, string scheme)
        {
            if (scheme == null)
            {
                var defaultScheme = await Schemes.GetDefaultAuthenticateSchemeAsync();
                scheme = defaultScheme?.Name;
                if (scheme == null)
                {
                    throw new InvalidOperationException($"No authenticationScheme was specified, and there was no DefaultAuthenticateScheme found. The default schemes can be set using either AddAuthentication(string defaultScheme) or AddAuthentication(Action configureOptions).");
                }
            }

            var handler = await Handlers.GetHandlerAsync(context, scheme);
            if (handler == null)
            {
                throw await CreateMissingHandlerException(scheme);
            }

            var result = await handler.AuthenticateAsync();
            if (result != null && result.Succeeded)
            {
                var transformed = await Transform.TransformAsync(result.Principal);
                return AuthenticateResult.Success(new AuthenticationTicket(transformed, result.Properties, result.Ticket.AuthenticationScheme));
            }
            return result;
        }

        /// 
        /// Challenge the specified authentication scheme.
        /// 
        /// The .
        /// The name of the authentication scheme.
        /// The .
        /// A task.
        public virtual async Task ChallengeAsync(HttpContext context, string scheme, AuthenticationProperties properties)
        {
            if (scheme == null)
            {
                var defaultChallengeScheme = await Schemes.GetDefaultChallengeSchemeAsync();
                scheme = defaultChallengeScheme?.Name;
                if (scheme == null)
                {
                    throw new InvalidOperationException($"No authenticationScheme was specified, and there was no DefaultChallengeScheme found. The default schemes can be set using either AddAuthentication(string defaultScheme) or AddAuthentication(Action configureOptions).");
                }
            }

            var handler = await Handlers.GetHandlerAsync(context, scheme);
            if (handler == null)
            {
                throw await CreateMissingHandlerException(scheme);
            }

            await handler.ChallengeAsync(properties);
        }

        /// 
        /// Forbid the specified authentication scheme.
        /// 
        /// The .
        /// The name of the authentication scheme.
        /// The .
        /// A task.
        public virtual async Task ForbidAsync(HttpContext context, string scheme, AuthenticationProperties properties)
        {
            if (scheme == null)
            {
                var defaultForbidScheme = await Schemes.GetDefaultForbidSchemeAsync();
                scheme = defaultForbidScheme?.Name;
                if (scheme == null)
                {
                    throw new InvalidOperationException($"No authenticationScheme was specified, and there was no DefaultForbidScheme found. The default schemes can be set using either AddAuthentication(string defaultScheme) or AddAuthentication(Action configureOptions).");
                }
            }

            var handler = await Handlers.GetHandlerAsync(context, scheme);
            if (handler == null)
            {
                throw await CreateMissingHandlerException(scheme);
            }

            await handler.ForbidAsync(properties);
        }

        /// 
        /// Sign a principal in for the specified authentication scheme.
        /// 
        /// The .
        /// The name of the authentication scheme.
        /// The  to sign in.
        /// The .
        /// A task.
        public virtual async Task SignInAsync(HttpContext context, string scheme, ClaimsPrincipal principal, AuthenticationProperties properties)
        {
            if (principal == null)
            {
                throw new ArgumentNullException(nameof(principal));
            }

            if (Options.RequireAuthenticatedSignIn)
            {
                if (principal.Identity == null)
                {
                    throw new InvalidOperationException("SignInAsync when principal.Identity == null is not allowed when AuthenticationOptions.RequireAuthenticatedSignIn is true.");
                }
                if (!principal.Identity.IsAuthenticated)
                {
                    throw new InvalidOperationException("SignInAsync when principal.Identity.IsAuthenticated is false is not allowed when AuthenticationOptions.RequireAuthenticatedSignIn is true.");
                }
            }

            if (scheme == null)
            {
                var defaultScheme = await Schemes.GetDefaultSignInSchemeAsync();
                scheme = defaultScheme?.Name;
                if (scheme == null)
                {
                    throw new InvalidOperationException($"No authenticationScheme was specified, and there was no DefaultSignInScheme found. The default schemes can be set using either AddAuthentication(string defaultScheme) or AddAuthentication(Action configureOptions).");
                }
            }

            var handler = await Handlers.GetHandlerAsync(context, scheme);
            if (handler == null)
            {
                throw await CreateMissingSignInHandlerException(scheme);
            }

            var signInHandler = handler as IAuthenticationSignInHandler;
            if (signInHandler == null)
            {
                throw await CreateMismatchedSignInHandlerException(scheme, handler);
            }

            await signInHandler.SignInAsync(principal, properties);
        }

        /// 
        /// Sign out the specified authentication scheme.
        /// 
        /// The .
        /// The name of the authentication scheme.
        /// The .
        /// A task.
        public virtual async Task SignOutAsync(HttpContext context, string scheme, AuthenticationProperties properties)
        {
            if (scheme == null)
            {
                var defaultScheme = await Schemes.GetDefaultSignOutSchemeAsync();
                scheme = defaultScheme?.Name;
                if (scheme == null)
                {
                    throw new InvalidOperationException($"No authenticationScheme was specified, and there was no DefaultSignOutScheme found. The default schemes can be set using either AddAuthentication(string defaultScheme) or AddAuthentication(Action configureOptions).");
                }
            }

            var handler = await Handlers.GetHandlerAsync(context, scheme);
            if (handler == null)
            {
                throw await CreateMissingSignOutHandlerException(scheme);
            }

            var signOutHandler = handler as IAuthenticationSignOutHandler;
            if (signOutHandler == null)
            {
                throw await CreateMismatchedSignOutHandlerException(scheme, handler);
            }

            await signOutHandler.SignOutAsync(properties);
        }

        private async Task CreateMissingHandlerException(string scheme)
        {
            var schemes = string.Join(", ", (await Schemes.GetAllSchemesAsync()).Select(sch => sch.Name));

            var footer = $" Did you forget to call AddAuthentication().Add[SomeAuthHandler](\"{scheme}\",...)?";

            if (string.IsNullOrEmpty(schemes))
            {
                return new InvalidOperationException(
                    $"No authentication handlers are registered." + footer);
            }

            return new InvalidOperationException(
                $"No authentication handler is registered for the scheme '{scheme}'. The registered schemes are: {schemes}." + footer);
        }

        private async Task GetAllSignInSchemeNames()
        {
            return string.Join(", ", (await Schemes.GetAllSchemesAsync())
                .Where(sch => typeof(IAuthenticationSignInHandler).IsAssignableFrom(sch.HandlerType))
                .Select(sch => sch.Name));
        }

        private async Task CreateMissingSignInHandlerException(string scheme)
        {
            var schemes = await GetAllSignInSchemeNames();

            // CookieAuth is the only implementation of sign-in.
            var footer = $" Did you forget to call AddAuthentication().AddCookies(\"{scheme}\",...)?";

            if (string.IsNullOrEmpty(schemes))
            {
                return new InvalidOperationException(
                    $"No sign-in authentication handlers are registered." + footer);
            }

            return new InvalidOperationException(
                $"No sign-in authentication handler is registered for the scheme '{scheme}'. The registered sign-in schemes are: {schemes}." + footer);
        }

        private async Task CreateMismatchedSignInHandlerException(string scheme, IAuthenticationHandler handler)
        {
            var schemes = await GetAllSignInSchemeNames();

            var mismatchError = $"The authentication handler registered for scheme '{scheme}' is '{handler.GetType().Name}' which cannot be used for SignInAsync. ";

            if (string.IsNullOrEmpty(schemes))
            {
                // CookieAuth is the only implementation of sign-in.
                return new InvalidOperationException(mismatchError
                    + $"Did you forget to call AddAuthentication().AddCookies(\"Cookies\") and SignInAsync(\"Cookies\",...)?");
            }

            return new InvalidOperationException(mismatchError + $"The registered sign-in schemes are: {schemes}.");
        }

        private async Task GetAllSignOutSchemeNames()
        {
            return string.Join(", ", (await Schemes.GetAllSchemesAsync())
                .Where(sch => typeof(IAuthenticationSignOutHandler).IsAssignableFrom(sch.HandlerType))
                .Select(sch => sch.Name));
        }

        private async Task CreateMissingSignOutHandlerException(string scheme)
        {
            var schemes = await GetAllSignOutSchemeNames();

            var footer = $" Did you forget to call AddAuthentication().AddCookies(\"{scheme}\",...)?";

            if (string.IsNullOrEmpty(schemes))
            {
                // CookieAuth is the most common implementation of sign-out, but OpenIdConnect and WsFederation also support it.
                return new InvalidOperationException($"No sign-out authentication handlers are registered." + footer);
            }

            return new InvalidOperationException(
                $"No sign-out authentication handler is registered for the scheme '{scheme}'. The registered sign-out schemes are: {schemes}." + footer);
        }

        private async Task CreateMismatchedSignOutHandlerException(string scheme, IAuthenticationHandler handler)
        {
            var schemes = await GetAllSignOutSchemeNames();

            var mismatchError = $"The authentication handler registered for scheme '{scheme}' is '{handler.GetType().Name}' which cannot be used for {nameof(SignOutAsync)}. ";

            if (string.IsNullOrEmpty(schemes))
            {
                // CookieAuth is the most common implementation of sign-out, but OpenIdConnect and WsFederation also support it.
                return new InvalidOperationException(mismatchError
                    + $"Did you forget to call AddAuthentication().AddCookies(\"Cookies\") and {nameof(SignOutAsync)}(\"Cookies\",...)?");
            }

            return new InvalidOperationException(mismatchError + $"The registered sign-out schemes are: {schemes}.");
        }
    }

AuthenticationMiddleware

认证核心逻辑

context.AuthenticateAsync实际是调用的AuthenticationService

    /// 
    /// Middleware that performs authentication.
    /// 
    public class AuthenticationMiddleware
    {
        private readonly RequestDelegate _next;

        /// 
        /// Initializes a new instance of .
        /// 
        /// The next item in the middleware pipeline.
        /// The .
        public AuthenticationMiddleware(RequestDelegate next, IAuthenticationSchemeProvider schemes)
        {
            if (next == null)
            {
                throw new ArgumentNullException(nameof(next));
            }
            if (schemes == null)
            {
                throw new ArgumentNullException(nameof(schemes));
            }

            _next = next;
            Schemes = schemes;
        }

        /// 
        /// Gets or sets the .
        /// 
        public IAuthenticationSchemeProvider Schemes { get; set; }

        /// 
        /// Invokes the middleware performing authentication.
        /// 
        /// The .
        public async Task Invoke(HttpContext context)
        {
            context.Features.Set(new AuthenticationFeature
            {
                OriginalPath = context.Request.Path,
                OriginalPathBase = context.Request.PathBase
            });

            // Give any IAuthenticationRequestHandler schemes a chance to handle the request
            var handlers = context.RequestServices.GetRequiredService();
            foreach (var scheme in await Schemes.GetRequestHandlerSchemesAsync())
            {
                var handler = await handlers.GetHandlerAsync(context, scheme.Name) as IAuthenticationRequestHandler;
                if (handler != null && await handler.HandleRequestAsync())
                {
                    return;
                }
            }

            var defaultAuthenticate = await Schemes.GetDefaultAuthenticateSchemeAsync();
            if (defaultAuthenticate != null)
            {
                var result = await context.AuthenticateAsync(defaultAuthenticate.Name);
                if (result?.Principal != null)
                {
                    context.User = result.Principal;
                }
            }

            await _next(context);
        }
    }

AuthenticationHttpContextExtensions

    /// 
    /// Extension methods to expose Authentication on HttpContext.
    /// 
    public static class AuthenticationHttpContextExtensions
    {
        /// 
        /// Extension method for authenticate using the  scheme.
        /// 
        /// The  context.
        /// The .
        public static Task AuthenticateAsync(this HttpContext context) =>
            context.AuthenticateAsync(scheme: null);

        /// 
        /// Extension method for authenticate.
        /// 
        /// The  context.
        /// The name of the authentication scheme.
        /// The .
        public static Task AuthenticateAsync(this HttpContext context, string scheme) =>
            context.RequestServices.GetRequiredService().AuthenticateAsync(context, scheme);

        /// 
        /// Extension method for Challenge.
        /// 
        /// The  context.
        /// The name of the authentication scheme.
        /// The result.
        public static Task ChallengeAsync(this HttpContext context, string scheme) =>
            context.ChallengeAsync(scheme, properties: null);

        /// 
        /// Extension method for authenticate using the  scheme.
        /// 
        /// The  context.
        /// The task.
        public static Task ChallengeAsync(this HttpContext context) =>
            context.ChallengeAsync(scheme: null, properties: null);

        /// 
        /// Extension method for authenticate using the  scheme.
        /// 
        /// The  context.
        /// The  properties.
        /// The task.
        public static Task ChallengeAsync(this HttpContext context, AuthenticationProperties properties) =>
            context.ChallengeAsync(scheme: null, properties: properties);

        /// 
        /// Extension method for Challenge.
        /// 
        /// The  context.
        /// The name of the authentication scheme.
        /// The  properties.
        /// The task.
        public static Task ChallengeAsync(this HttpContext context, string scheme, AuthenticationProperties properties) =>
            context.RequestServices.GetRequiredService().ChallengeAsync(context, scheme, properties);

        /// 
        /// Extension method for Forbid.
        /// 
        /// The  context.
        /// The name of the authentication scheme.
        /// The task.
        public static Task ForbidAsync(this HttpContext context, string scheme) =>
            context.ForbidAsync(scheme, properties: null);

        /// 
        /// Extension method for Forbid using the  scheme..
        /// 
        /// The  context.
        /// The task.
        public static Task ForbidAsync(this HttpContext context) =>
            context.ForbidAsync(scheme: null, properties: null);

        /// 
        /// Extension method for Forbid.
        /// 
        /// The  context.
        /// The  properties.
        /// The task.
        public static Task ForbidAsync(this HttpContext context, AuthenticationProperties properties) =>
            context.ForbidAsync(scheme: null, properties: properties);

        /// 
        /// Extension method for Forbid.
        /// 
        /// The  context.
        /// The name of the authentication scheme.
        /// The  properties.
        /// The task.
        public static Task ForbidAsync(this HttpContext context, string scheme, AuthenticationProperties properties) =>
            context.RequestServices.GetRequiredService().ForbidAsync(context, scheme, properties);

        /// 
        /// Extension method for SignIn.
        /// 
        /// The  context.
        /// The name of the authentication scheme.
        /// The user.
        /// The task.
        public static Task SignInAsync(this HttpContext context, string scheme, ClaimsPrincipal principal) =>
            context.SignInAsync(scheme, principal, properties: null);

        /// 
        /// Extension method for SignIn using the .
        /// 
        /// The  context.
        /// The user.
        /// The task.
        public static Task SignInAsync(this HttpContext context, ClaimsPrincipal principal) =>
            context.SignInAsync(scheme: null, principal: principal, properties: null);

        /// 
        /// Extension method for SignIn using the .
        /// 
        /// The  context.
        /// The user.
        /// The  properties.
        /// The task.
        public static Task SignInAsync(this HttpContext context, ClaimsPrincipal principal, AuthenticationProperties properties) =>
            context.SignInAsync(scheme: null, principal: principal, properties: properties);

        /// 
        /// Extension method for SignIn.
        /// 
        /// The  context.
        /// The name of the authentication scheme.
        /// The user.
        /// The  properties.
        /// The task.
        public static Task SignInAsync(this HttpContext context, string scheme, ClaimsPrincipal principal, AuthenticationProperties properties) =>
            context.RequestServices.GetRequiredService().SignInAsync(context, scheme, principal, properties);

        /// 
        /// Extension method for SignOut using the .
        /// 
        /// The  context.
        /// The task.
        public static Task SignOutAsync(this HttpContext context) => context.SignOutAsync(scheme: null, properties: null);

        /// 
        /// Extension method for SignOut using the .
        /// 
        /// The  context.
        /// The  properties.
        /// The task.
        public static Task SignOutAsync(this HttpContext context, AuthenticationProperties properties) => context.SignOutAsync(scheme: null, properties: properties);

        /// 
        /// Extension method for SignOut.
        /// 
        /// The  context.
        /// The name of the authentication scheme.
        /// The task.
        public static Task SignOutAsync(this HttpContext context, string scheme) => context.SignOutAsync(scheme, properties: null);

        /// 
        /// Extension method for SignOut.
        /// 
        /// The  context.
        /// The name of the authentication scheme.
        /// The  properties.
        /// The task.
        public static Task SignOutAsync(this HttpContext context, string scheme, AuthenticationProperties properties) =>
            context.RequestServices.GetRequiredService().SignOutAsync(context, scheme, properties);

        /// 
        /// Extension method for getting the value of an authentication token.
        /// 
        /// The  context.
        /// The name of the authentication scheme.
        /// The name of the token.
        /// The value of the token.
        public static Task GetTokenAsync(this HttpContext context, string scheme, string tokenName) =>
            context.RequestServices.GetRequiredService().GetTokenAsync(context, scheme, tokenName);

        /// 
        /// Extension method for getting the value of an authentication token.
        /// 
        /// The  context.
        /// The name of the token.
        /// The value of the token.
        public static Task GetTokenAsync(this HttpContext context, string tokenName) =>
            context.RequestServices.GetRequiredService().GetTokenAsync(context, tokenName);

依赖注入

AuthenticationCoreServiceCollectionExtensions

    /// 
    /// Extension methods for setting up authentication services in an .
    /// 
    public static class AuthenticationCoreServiceCollectionExtensions
    {
        /// 
        /// Add core authentication services needed for .
        /// 
        /// The .
        /// The service collection.
        public static IServiceCollection AddAuthenticationCore(this IServiceCollection services)
        {
            if (services == null)
            {
                throw new ArgumentNullException(nameof(services));
            }

            services.TryAddScoped();
            services.TryAddSingleton(); // Can be replaced with scoped ones that use DbContext
            services.TryAddScoped();
            services.TryAddSingleton();
            return services;
        }

        /// 
        /// Add core authentication services needed for .
        /// 
        /// The .
        /// Used to configure the .
        /// The service collection.
        public static IServiceCollection AddAuthenticationCore(this IServiceCollection services, Action configureOptions) {
            if (services == null)
            {
                throw new ArgumentNullException(nameof(services));
            }

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

            services.AddAuthenticationCore();
            services.Configure(configureOptions);
            return services;
        }
    }

AuthAppBuilderExtensions

    /// 
    /// Extension methods to add authentication capabilities to an HTTP application pipeline.
    /// 
    public static class AuthAppBuilderExtensions
    {
        /// 
        /// Adds the  to the specified , which enables authentication capabilities.
        /// 
        /// The  to add the middleware to.
        /// A reference to this instance after the operation has completed.
        public static IApplicationBuilder UseAuthentication(this IApplicationBuilder app)
        {
            if (app == null)
            {
                throw new ArgumentNullException(nameof(app));
            }
            
            return app.UseMiddleware();
        }
    }