编辑
2023-03-06
.NET
00
请注意,本文编写于 631 天前,最后修改于 471 天前,其中某些信息可能已经过时。

目录

关系
认证
Claim(声明)
IIdentity(身份)
IPrincipal(主体)
AuthenticationTicket(认证票据)
Challenge(质询)
IAuthenticationHandler
AuthenticationScheme(认证方案)
IAuthenticationService

0_Keys and locks_esrgan-v1-x2plus.png

关系

认证(Authentication),指确定请求访问者真实身份的过程。 授权(Authrization)

认证

有几个概念和定义需要清楚

Claim(声明)

简单理解是key-value的陈述,比如我想陈述我的邮件是123@qq.com,那就key就是email,value就是123@qq.com

namespace System.Security.Claims { public class Claim { /// <summary> /// 声明的类型,比如上面说的email /// <seealso cref="ClaimTypes"/>. /// </summary> public string Type { get; } /// <summary> /// Value,比如上面说的123@qq.com /// </summary> public string Value { get; } /// <summary> /// Value的类型,比如字符串或日期 /// <seealso cref="ClaimValueTypes"/> /// </summary> public string ValueType { get; } /// <summary> /// 颁发人 /// </summary> public string Issuer { get; } /// <summary> /// 最初颁发人 /// </summary> public string OriginalIssuer { get; } //... } }

IIdentity(身份)

namespace System.Security.Principal { public interface IIdentity { //名称 string? Name { get; } //认证类型 string? AuthenticationType { get; } //是否已经过认证 bool IsAuthenticated { get; } } }

有两个主要的实现,ClaimsIdentityGenericIdentity

IPrincipal(主体)

认证的主体,可以是人,但也可以是一个应用或服务,所以不能叫用户,这里.net起名叫做主体(Principal)。

namespace System.Security.Principal { public interface IPrincipal { // 身份 IIdentity? Identity { get; } // Perform a check for a specific role bool IsInRole(string role); } }

也有两个主要实现,ClaimsPrincipalGenericPrincipal

目前为止,他们的关系

image.png

AuthenticationTicket(认证票据)

.net采用基于票据的认证方式,从请求中提取能够验证用户真实身份的数据,该数据被称为安全令牌(Security Token),在.net里被称为认证票据。

AuthenticationMiddleware中间件所作,就是3件事,都是针对票据的

  • 认证票据的颁发
  • 认证票据的检验
  • 认证票据的撤销
namespace Microsoft.AspNetCore.Authentication { public class AuthenticationTicket { // 采取的认证方案 public string AuthenticationScheme { get; } // 主题人 public ClaimsPrincipal Principal { get; } // 其他属性值 public AuthenticationProperties Properties { get; } } }
namespace Microsoft.AspNetCore.Authentication { public class AuthenticationProperties { // 票据颁发时间 [JsonIgnore] public DateTimeOffset? IssuedUtc { get => GetDateTimeOffset(IssuedUtcKey); set => SetDateTimeOffset(IssuedUtcKey, value); } // 票据过期时间 [JsonIgnore] public DateTimeOffset? ExpiresUtc { get => GetDateTimeOffset(ExpiresUtcKey); set => SetDateTimeOffset(ExpiresUtcKey, value); } // 是否允许自动刷新票据(就是我们说的token滑动过期模式) [JsonIgnore] public bool? AllowRefresh { get => GetBool(RefreshKey); set => SetBool(RefreshKey, value); } // 是否希望被客户端持久化,比如客户端时浏览器,持久化后,重启浏览器还可以继续使用 [JsonIgnore] public bool IsPersistent { get => GetString(IsPersistentKey) != null; set => SetString(IsPersistentKey, value ? string.Empty : null); } // 重定向地址 [JsonIgnore] public string? RedirectUri { get => GetString(RedirectUriKey); set => SetString(RedirectUriKey, value); } public IDictionary<string, string?> Items { get; }

Challenge(质询)

Challenge一般以401 Unauthorized或403 Forbidden的形式,向申请认证方质询,要求提供合法凭证进行认证。

IAuthenticationHandler

public interface IAuthenticationHandler { Task InitializeAsync(AuthenticationScheme scheme, HttpContext context); Task<AuthenticateResult> AuthenticateAsync(); Task ChallengeAsync(AuthenticationProperties properties); Task ForbidAsync(AuthenticationProperties properties); }

另外3个派生出的接口:

public interface IAuthenticationSignInHandler : IAuthenticationSignOutHandler { Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties properties); } public interface IAuthenticationSignOutHandler : IAuthenticationHandler { Task SignOutAsync(AuthenticationProperties properties); } public interface IAuthenticationRequestHandler : IAuthenticationHandler { Task<bool> HandleRequestAsync(); }

AuthenticationScheme(认证方案)

/// <summary> /// AuthenticationSchemes assign a name to a specific <see cref="IAuthenticationHandler"/> /// handlerType. /// </summary> public class AuthenticationScheme { public string Name { get; } public string DisplayName { get; } /// <summary> /// The <see cref="IAuthenticationHandler"/> type that handles this scheme. /// </summary> public Type HandlerType { get; } }

一个认证方案对应一种IAuthenticationHandler。

IAuthenticationService

方法与上面的IAuthenticationHandler基本一致,不一样的是,如参是HttpContext,是针对的请求的。

public interface IAuthenticationService { /// <summary> /// Authenticate for the specified authentication scheme. /// </summary> /// <param name="context">The <see cref="HttpContext"/>.</param> /// <param name="scheme">The name of the authentication scheme.</param> /// <returns>The result.</returns> Task<AuthenticateResult> AuthenticateAsync(HttpContext context, string scheme); /// <summary> /// Challenge the specified authentication scheme. /// </summary> /// <param name="context">The <see cref="HttpContext"/>.</param> /// <param name="scheme">The name of the authentication scheme.</param> /// <param name="properties">The <see cref="AuthenticationProperties"/>.</param> /// <returns>A task.</returns> Task ChallengeAsync(HttpContext context, string scheme, AuthenticationProperties properties); /// <summary> /// Forbids the specified authentication scheme. /// </summary> /// <param name="context">The <see cref="HttpContext"/>.</param> /// <param name="scheme">The name of the authentication scheme.</param> /// <param name="properties">The <see cref="AuthenticationProperties"/>.</param> /// <returns>A task.</returns> Task ForbidAsync(HttpContext context, string scheme, AuthenticationProperties properties); /// <summary> /// Sign a principal in for the specified authentication scheme. /// </summary> /// <param name="context">The <see cref="HttpContext"/>.</param> /// <param name="scheme">The name of the authentication scheme.</param> /// <param name="principal">The <see cref="ClaimsPrincipal"/> to sign in.</param> /// <param name="properties">The <see cref="AuthenticationProperties"/>.</param> /// <returns>A task.</returns> Task SignInAsync(HttpContext context, string scheme, ClaimsPrincipal principal, AuthenticationProperties properties); /// <summary> /// Sign out the specified authentication scheme. /// </summary> /// <param name="context">The <see cref="HttpContext"/>.</param> /// <param name="scheme">The name of the authentication scheme.</param> /// <param name="properties">The <see cref="AuthenticationProperties"/>.</param> /// <returns>A task.</returns> Task SignOutAsync(HttpContext context, string scheme, AuthenticationProperties properties); }

默认实现:

namespace Microsoft.AspNetCore.Authentication { public class AuthenticationService : IAuthenticationService { public AuthenticationService(IAuthenticationSchemeProvider schemes, IAuthenticationHandlerProvider handlers, IClaimsTransformation transform, IOptions<AuthenticationOptions> options) { Schemes = schemes; Handlers = handlers; Transform = transform; Options = options.Value; } public IAuthenticationSchemeProvider Schemes { get; } public IAuthenticationHandlerProvider Handlers { get; } public IClaimsTransformation Transform { get; } public AuthenticationOptions Options { get; } public virtual async Task<AuthenticateResult> 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<AuthenticationOptions> 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; } 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<AuthenticationOptions> configureOptions)."); } } var handler = await Handlers.GetHandlerAsync(context, scheme); if (handler == null) { throw await CreateMissingHandlerException(scheme); } await handler.ChallengeAsync(properties); } 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<AuthenticationOptions> configureOptions)."); } } var handler = await Handlers.GetHandlerAsync(context, scheme); if (handler == null) { throw await CreateMissingHandlerException(scheme); } await handler.ForbidAsync(properties); } 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<AuthenticationOptions> 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); } 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<AuthenticationOptions> 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<Exception> 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<string> GetAllSignInSchemeNames() { return string.Join(", ", (await Schemes.GetAllSchemesAsync()) .Where(sch => typeof(IAuthenticationSignInHandler).IsAssignableFrom(sch.HandlerType)) .Select(sch => sch.Name)); } private async Task<Exception> 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<Exception> 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<string> GetAllSignOutSchemeNames() { return string.Join(", ", (await Schemes.GetAllSchemesAsync()) .Where(sch => typeof(IAuthenticationSignOutHandler).IsAssignableFrom(sch.HandlerType)) .Select(sch => sch.Name)); } private async Task<Exception> 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<Exception> 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}."); } } }
如果对你有用的话,可以打赏哦
打赏
ali pay
wechat pay

本文作者:Ray

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

特别感谢:alicenetworks