前言:本文将会结合asp.net core 认证源码来分析起认证的原理与流程。asp.net core版本2.2

对于大部分使用asp.net core开发的人来说。

下面这几行代码应该很熟悉了。

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.RequireHttpsMetadata = false;
options.Audience = "sp_api";
options.Authority = "http://localhost:5001";
options.SaveToken = true; })
 app.UseAuthentication();

废话不多说。直接看 app.UseAuthentication()的源码

 public class AuthenticationMiddleware
{
private readonly RequestDelegate _next; 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;
} public IAuthenticationSchemeProvider Schemes { get; set; } public async Task Invoke(HttpContext context)
{
context.Features.Set<IAuthenticationFeature>(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<IAuthenticationHandlerProvider>();
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);
}

现在来看看var defaultAuthenticate = await Schemes.GetDefaultAuthenticateSchemeAsync(); 干了什么。

在这之前。我们更应该要知道上面代码中  public IAuthenticationSchemeProvider Schemes { get; set; } ,假如脑海中对这个IAuthenticationSchemeProvider类型的来源,有个清晰认识,对后面的理解会有很大的帮助

现在来揭秘IAuthenticationSchemeProvider 是从哪里来添加到ioc的。

  public static AuthenticationBuilder AddAuthentication(this IServiceCollection services)
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
} services.AddAuthenticationCore();
services.AddDataProtection();
services.AddWebEncoders();
services.TryAddSingleton<ISystemClock, SystemClock>();
return new AuthenticationBuilder(services);
}

红色代码内部逻辑中就把IAuthenticationSchemeProvider添加到了IOC中。先来看看services.AddAuthenticationCore()的源码,这个源码的所在的解决方案的仓库地址是https://github.com/aspnet/HttpAbstractions,这个仓库目前已不再维护,其代码都转移到了asp.net core 仓库 。

下面为services.AddAuthenticationCore()的源码

 public static class AuthenticationCoreServiceCollectionExtensions
{
/// <summary>
/// Add core authentication services needed for <see cref="IAuthenticationService"/>.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/>.</param>
/// <returns>The service collection.</returns>
public static IServiceCollection AddAuthenticationCore(this IServiceCollection services)
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
} services.TryAddScoped<IAuthenticationService, AuthenticationService>();
services.TryAddSingleton<IClaimsTransformation, NoopClaimsTransformation>(); // Can be replaced with scoped ones that use DbContext
services.TryAddScoped<IAuthenticationHandlerProvider, AuthenticationHandlerProvider>();
services.TryAddSingleton<IAuthenticationSchemeProvider, AuthenticationSchemeProvider>();
return services;
} /// <summary>
/// Add core authentication services needed for <see cref="IAuthenticationService"/>.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/>.</param>
/// <param name="configureOptions">Used to configure the <see cref="AuthenticationOptions"/>.</param>
/// <returns>The service collection.</returns>
public static IServiceCollection AddAuthenticationCore(this IServiceCollection services, Action<AuthenticationOptions> configureOptions) {
if (services == null)
{
throw new ArgumentNullException(nameof(services));
} if (configureOptions == null)
{
throw new ArgumentNullException(nameof(configureOptions));
} services.AddAuthenticationCore();
services.Configure(configureOptions);
return services;
}
}

完全就可以看待添加了一个全局单例的IAuthenticationSchemeProvider对象。现在让我们回到MiddleWare中探究Schemes.GetDefaultAuthenticateSchemeAsync(); 干了什么。光看方法的名字都能猜出就是获取的默认的认证策略。

进入到IAuthenticationSchemeProvider 实现的源码中,按我的经验,来看先不急看GetDefaultAuthenticateSchemeAsync()里面的内部逻辑。必须的看下IAuthenticationSchemeProvider实现类的构造函数。它的实现类是AuthenticationSchemeProvider。

先看看AuthenticationSchemeProvider的构造方法

 public class AuthenticationSchemeProvider : IAuthenticationSchemeProvider
{
/// <summary>
/// Creates an instance of <see cref="AuthenticationSchemeProvider"/>
/// using the specified <paramref name="options"/>,
/// </summary>
/// <param name="options">The <see cref="AuthenticationOptions"/> options.</param>
public AuthenticationSchemeProvider(IOptions<AuthenticationOptions> options)
: this(options, new Dictionary<string, AuthenticationScheme>(StringComparer.Ordinal))
{
} /// <summary>
/// Creates an instance of <see cref="AuthenticationSchemeProvider"/>
/// using the specified <paramref name="options"/> and <paramref name="schemes"/>.
/// </summary>
/// <param name="options">The <see cref="AuthenticationOptions"/> options.</param>
/// <param name="schemes">The dictionary used to store authentication schemes.</param>
protected AuthenticationSchemeProvider(IOptions<AuthenticationOptions> options, IDictionary<string, AuthenticationScheme> schemes)
{
_options = options.Value; _schemes = schemes ?? throw new ArgumentNullException(nameof(schemes));
_requestHandlers = new List<AuthenticationScheme>(); foreach (var builder in _options.Schemes)
{
var scheme = builder.Build();
AddScheme(scheme);
}
} private readonly AuthenticationOptions _options;
private readonly object _lock = new object(); private readonly IDictionary<string, AuthenticationScheme> _schemes;
private readonly List<AuthenticationScheme> _requestHandlers;

不难看出,上面的构造方法需要一个IOptions<AuthenticationOptions> 类型。没有这个类型,而这个类型是从哪里的了?

答:不知到各位是否记得addJwtBearer这个方法,再找个方法里面就注入了AuthenticationOptions找个类型。

看源码把

 public static class JwtBearerExtensions
{
public static AuthenticationBuilder AddJwtBearer(this AuthenticationBuilder builder)
=> builder.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, _ => { }); public static AuthenticationBuilder AddJwtBearer(this AuthenticationBuilder builder, Action<JwtBearerOptions> configureOptions)
=> builder.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, configureOptions); public static AuthenticationBuilder AddJwtBearer(this AuthenticationBuilder builder, string authenticationScheme, Action<JwtBearerOptions> configureOptions)
=> builder.AddJwtBearer(authenticationScheme, displayName: null, configureOptions: configureOptions); public static AuthenticationBuilder AddJwtBearer(this AuthenticationBuilder builder, string authenticationScheme, string displayName, Action<JwtBearerOptions> configureOptions)
{
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<JwtBearerOptions>, JwtBearerPostConfigureOptions>());
return builder.AddScheme<JwtBearerOptions, JwtBearerHandler>(authenticationScheme, displayName, configureOptions);
}
}

不难通过上述代码看出它是及一个基于AuthenticationBuilder的扩展方法,而注入AuthenticationOptions的关键就在于 builder.AddScheme<JwtBearerOptions, JwtBearerHandler>(authenticationScheme, displayName, configureOptions);  这行代码,按下F12看下源码

 public virtual AuthenticationBuilder AddScheme<TOptions, THandler>(string authenticationScheme, string displayName, Action<TOptions> configureOptions)
where TOptions : AuthenticationSchemeOptions, new()
where THandler : AuthenticationHandler<TOptions>
=> AddSchemeHelper<TOptions, THandler>(authenticationScheme, displayName, configureOptions);
private AuthenticationBuilder AddSchemeHelper<TOptions, THandler>(string authenticationScheme, string displayName, Action<TOptions> configureOptions)
where TOptions : class, new()
where THandler : class, IAuthenticationHandler
{
Services.Configure<AuthenticationOptions>(o =>
{
o.AddScheme(authenticationScheme, scheme => {
scheme.HandlerType = typeof(THandler);
scheme.DisplayName = displayName;
});
});
if (configureOptions != null)
{
Services.Configure(authenticationScheme, configureOptions);
}
Services.AddTransient<THandler>();
return this;
}

照旧还是分为2个方法来进行调用,其重点就是AddSchemeHelper找个方法。其里面配置AuthenticationOptions类型。现在我们已经知道了IAuthenticationSchemeProvider何使注入的。还由AuthenticationSchemeProvider构造方法中IOptions<AuthenticationOptions> options是何使配置的,这样我们就对于认证有了一个初步的认识。现在可以知道对于认证中间件,必须要有一个IAuthenticationSchemeProvider 类型。而这个IAuthenticationSchemeProvider的实现类的构造函数必须要由IOptions<AuthenticationOptions> options,没有这两个类型,认证中间件应该是不会工作的。

回到认证中间件中。继续看var defaultAuthenticate = await Schemes.GetDefaultAuthenticateSchemeAsync();这句代码,源码如下

  public virtual Task<AuthenticationScheme> GetDefaultAuthenticateSchemeAsync()
=> _options.DefaultAuthenticateScheme != null
? GetSchemeAsync(_options.DefaultAuthenticateScheme)
: GetDefaultSchemeAsync(); public virtual Task<AuthenticationScheme> GetSchemeAsync(string name)
=> Task.FromResult(_schemes.ContainsKey(name) ? _schemes[name] : null);
private Task<AuthenticationScheme> GetDefaultSchemeAsync()
=> _options.DefaultScheme != null
? GetSchemeAsync(_options.DefaultScheme)
: Task.FromResult<AuthenticationScheme>(null);

让我们先验证下方法1的三元表达式,应该执行那边呢?通过前面的代码我们知道AuthenticationOptions是在AuthenticationBuilder类型的AddSchemeHelper方法里面进行配置的。经过我的调试,发现方法1会走右边。其实最终还是从一个字典中取到了默认的AuthenticationScheme对象。到这里中间件的里面var defaultAuthenticate = await Schemes.GetDefaultAuthenticateSchemeAsync();代码就完了。最终就那到了AuthenticationScheme的对象。

下面来看看 中间件中var result = await context.AuthenticateAsync(defaultAuthenticate.Name);这句代码干了什么。按下F12发现是一个扩展方法,还是到HttpAbstractions解决方案里面找下源码

源码如下

 public static Task<AuthenticateResult> AuthenticateAsync(this HttpContext context, string scheme) =>
context.RequestServices.GetRequiredService<IAuthenticationService>().AuthenticateAsync(context, scheme);

通过上面的方法,发现是通过IAuthenticationService的AuthenticateAsync() 来进行认证的。那么现在IAuthenticationService这个类是干什么 呢?

下面为IAuthenticationService的定义

 public interface IAuthenticationService
{
Task<AuthenticateResult> AuthenticateAsync(HttpContext context, string scheme); Task ChallengeAsync(HttpContext context, string scheme, AuthenticationProperties properties); Task ForbidAsync(HttpContext context, string scheme, AuthenticationProperties properties); Task SignInAsync(HttpContext context, string scheme, ClaimsPrincipal principal, AuthenticationProperties properties); Task SignOutAsync(HttpContext context, string scheme, AuthenticationProperties properties);
}

IAuthenticationService的AuthenticateAsync()方法的实现源码

public class AuthenticationService : IAuthenticationService
{
/// <summary>
/// Constructor.
/// </summary>
/// <param name="schemes">The <see cref="IAuthenticationSchemeProvider"/>.</param>
/// <param name="handlers">The <see cref="IAuthenticationRequestHandler"/>.</param>
/// <param name="transform">The <see cref="IClaimsTransformation"/>.</param>
public AuthenticationService(IAuthenticationSchemeProvider schemes, IAuthenticationHandlerProvider handlers, IClaimsTransformation transform)
{
Schemes = schemes;
Handlers = handlers;
Transform = transform;
}
 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.");
                }
            }

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

通过构造方法可以看到这个类的构造方法需要IAuthenticationSchemeProvider类型和IAuthenticationHandlerProvider 类型,前面已经了解了IAuthenticationSchemeProvider是干什么的,取到配置的授权策略的名称,那现在IAuthenticationHandlerProvider 是干什么的,看名字感觉应该是取到具体授权策略的handler.废话补多少,看IAuthenticationHandlerProvider 接口定义把

 public interface IAuthenticationHandlerProvider
{
/// <summary>
/// Returns the handler instance that will be used.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="authenticationScheme">The name of the authentication scheme being handled.</param>
/// <returns>The handler instance.</returns>
Task<IAuthenticationHandler> GetHandlerAsync(HttpContext context, string authenticationScheme);
}

通过上面的源码,跟我猜想的不错,果然就是取得具体的授权策略

现在我就可以知道AuthenticationService是对IAuthenticationSchemeProvider和IAuthenticationHandlerProvider封装。最终调用IAuthentionHandel的AuthenticateAsync()方法进行认证。最终返回一个AuthenticateResult对象。

总结,对于asp.net core的认证来水,他需要下面这几个对象

AuthenticationBuilder      扶着对认证策略的配置与初始话

IAuthenticationHandlerProvider AuthenticationHandlerProvider 负责获取配置了的认证策略的名称

IAuthenticationSchemeProvider AuthenticationSchemeProvider 负责获取具体认证策略的handle

IAuthenticationService AuthenticationService 实对上面两个Provider 的封装,来提供一个具体处理认证的入口

IAuthenticationHandler 和的实现类,是以哦那个来处理具体的认证的,对不同认证策略的出来,全是依靠的它的AuthenticateAsync()方法。

AuthenticateResult  最终的认证结果。

哎写的太垃圾了。

asp.net core 使用identityServer4的密码模式来进行身份认证(2) 认证授权原理的更多相关文章

  1. asp.net core 使用identityServer4的密码模式来进行身份认证(一)

    IdentityServer4是ASP.NET Core的一个包含OpenID和OAuth 2.0协议的框架.具体Oauth 2.0和openId请百度. 前言本博文适用于前后端分离或者为移动产品来后 ...

  2. Asp.Net Core 中IdentityServer4 授权中心之自定义授权模式

    一.前言 上一篇我分享了一篇关于 Asp.Net Core 中IdentityServer4 授权中心之应用实战 的文章,其中有不少博友给我提了问题,其中有一个博友问我的一个场景,我给他解答的还不够完 ...

  3. Asp.Net Core 中IdentityServer4 授权中心之应用实战

    一.前言 查阅了大多数相关资料,查阅到的IdentityServer4 的相关文章大多是比较简单并且多是翻译官网的文档编写的,我这里在 Asp.Net Core 中IdentityServer4 的应 ...

  4. Asp.Net Core 中IdentityServer4 授权原理及刷新Token的应用

    一.前言 上面分享了IdentityServer4 两篇系列文章,核心主题主要是密码授权模式及自定义授权模式,但是仅仅是分享了这两种模式的使用,这篇文章进一步来分享IdentityServer4的授权 ...

  5. Asp.Net Core 中IdentityServer4 实战之 Claim详解

    一.前言 由于疫情原因,让我开始了以博客的方式来学习和分享技术(持续分享的过程也是自己学习成长的过程),同时也让更多的初学者学习到相关知识,如果我的文章中有分析不到位的地方,还请大家多多指教:以后我会 ...

  6. Asp.Net Core 中IdentityServer4 实战之角色授权详解

    一.前言 前几篇文章分享了IdentityServer4密码模式的基本授权及自定义授权等方式,最近由于改造一个网关服务,用到了IdentityServer4的授权,改造过程中发现比较适合基于Role角 ...

  7. ASP.NET Core路由中间件[2]: 路由模式

    一个Web应用本质上体现为一组终结点的集合.终结点则体现为一个暴露在网络中可供外界采用HTTP协议调用的服务,路由的作用就是建立一个请求URL模式与对应终结点之间的映射关系.借助这个映射关系,客户端可 ...

  8. 【.NET Core】ASP.NET Core之IdentityServer4(1):快速入门

    [.NET Core]ASP.NET Core之IdentityServer4 本文中的IdentityServer4基于上节的jenkins 进行docker自动化部署. 使用了MariaDB,EF ...

  9. 避免在ASP.NET Core中使用服务定位器模式

    (此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 题记:服务定位器(Service Locator)作为一种反模式,一般情况下应该避免使用,在 ...

随机推荐

  1. iframe高度自适应实现方案

    iframe高度动态自适应,一直是个头疼的问题,今天我们从事件监听这个角度,来实现iframe高度实时更新. 方案一:监听iframe体的点击事件 <iframe src="casca ...

  2. 在Eclipse中运行JAVA代码远程操作HBase的示例

    在Eclipse中运行JAVA代码远程操作HBase的示例 分类: 大数据 2014-03-04 13:47 3762人阅读 评论(2) 收藏 举报 下面是一个在Windows的Eclipse中通过J ...

  3. 解决ubuntu常见问题

    cd /usr/local/hadoop 1. bash: cd: ~:Permission denied 报错:bash: cd: /usr/local/hadoop:Permission deni ...

  4. 2017年多校get点

    杨辉三角形变形??? lucas定理

  5. oracle 分组中排序(rank函数)

    需求: 查询每个供应商在每个类型产品销售的top50中有多少 分析: 1.查询,以指定字段(供应商.产品类型)分组,取每个分组的前50行,查看每个供应商的数量 2.使用rank函数给每个供应商.每个类 ...

  6. java正则表达式笔记

    import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.regex.PatternSyntax ...

  7. 蓝绿部署、红黑部署、AB测试、灰度发布、金丝雀发布、滚动发布的概念与区别(转)

    出处:https://www.baidu.com/link?url=QjboallwNm_jxcL3fHG57wEakiBfAs_3-TChTGu1eBXstlHEsGBc-NDA7AKTqsiroB ...

  8. NIO原理解析

    NIO中主要包括几大组件,selector.channel.buffer.selector后面介绍,channel则类似于BIO中的流,但是流的读取是单向的,例如只能读,或只能写,但是channel则 ...

  9. tflite笔记

    固化模型 方法一:freeze_graph方法 把tf.train.write_graph()生成的pb文件与tf.train.saver()生成的chkp文件固化之后重新生成一个pb文件 with ...

  10. Router pipeline

    from 2013-HPCA-Breaking the On-Chip Latency Barrier Using SMART book_Principles and Practices of Int ...