在现代应用程序中,认证已不再是简单的将用户凭证保存在浏览器中,而要适应多种场景,如App,WebAPI,第三方登录等等。在 ASP.NET 4.x 时代的Windows认证和Forms认证已无法满足现代化的需求,因此在ASP.NET Core 中对认证及授权进行了全新设计,使其更加灵活,可以应付各种场景。在上一章中,我们提到HttpContext中认证相关的功能放在了独立的模块中,以扩展的方式来展现,以保证HttpContext的简洁性,本章就来介绍一下 ASP.NET Core 认证系统的整个轮廓,以及它的切入点。

AuthenticationHttpContextExtensions

AuthenticationHttpContextExtensions 类是对 HttpContext 认证相关的扩展,它提供了如下扩展方法:

public static class AuthenticationHttpContextExtensions{   

 public static Task<AuthenticateResult> AuthenticateAsync(this HttpContext context, string scheme) =>context.RequestServices.GetRequiredService<IAuthenticationService>().AuthenticateAsync(context, scheme);    public static Task ChallengeAsync(this HttpContext context, string scheme, AuthenticationProperties properties) { }    public static Task ForbidAsync(this HttpContext context, string scheme, AuthenticationProperties properties) { }    public static Task SignInAsync(this HttpContext context, string scheme, ClaimsPrincipal principal, AuthenticationProperties properties) {}    public static Task SignOutAsync(this HttpContext context, string scheme, AuthenticationProperties properties) { }    public static Task<string> GetTokenAsync(this HttpContext context, string scheme, string tokenName) { }
}

主要包括如上6个扩展方法,其它的只是一些参数重载:

  • SignInAsync 用户登录成功后颁发一个证书(加密的用户凭证),用来标识用户的身份。

  • SignOutAsync 退出登录,如清除Coookie等。

  • AuthenticateAsync 验证在 SignInAsync 中颁发的证书,并返回一个 AuthenticateResult 对象,表示用户的身份。

  • ChallengeAsync 返回一个需要认证的标识来提示用户登录,通常会返回一个 401 状态码。

  • ForbidAsync 禁上访问,表示用户权限不足,通常会返回一个 403 状态码。

  • GetTokenAsync 用来获取 AuthenticationProperties 中保存的额外信息。

它们的实现都非常简单,与展示的第一个方法类似,从DI系统中获取到 IAuthenticationService 接口实例,然后调用其同名方法。

因此,如果我们希望使用认证服务,那么首先要注册 IAuthenticationService 的实例,ASP.NET Core 中也提供了对应注册扩展方法:

public static class AuthenticationCoreServiceCollectionExtensions{    public static IServiceCollection AddAuthenticationCore(this IServiceCollection services)    {services.TryAddScoped<IAuthenticationService, AuthenticationService>();services.TryAddSingleton<IClaimsTransformation, NoopClaimsTransformation>(); // Can be replaced with scoped ones that use DbContextservices.TryAddScoped<IAuthenticationHandlerProvider, AuthenticationHandlerProvider>();services.TryAddSingleton<IAuthenticationSchemeProvider, AuthenticationSchemeProvider>();        return services;}    

   public static IServiceCollection AddAuthenticationCore(this IServiceCollection services, Action<AuthenticationOptions> configureOptions)    {services.AddAuthenticationCore();services.Configure(configureOptions);           return services;}
}

如上,AddAuthenticationCore 中注册了认证系统的三大核心对象:IAuthenticationSchemeProviderIAuthenticationHandlerProvider 和 IAuthenticationService,以及一个对Claim进行转换的 IClaimsTransformation(不常用),下面就来介绍一下这三大对象。

IAuthenticationSchemeProvider

首先来解释一下 Scheme 是用来做什么的。因为在 ASP.NET Core 中可以支持各种各样的认证方式(如,cookie, bearer, oauth, openid 等等),而 Scheme 用来标识使用的是哪种认证方式,不同的认证方式其处理方式是完全不一样的,所以Scheme是非常重要的。

IAuthenticationSchemeProvider 用来提供对Scheme的注册和查询,定义如下:

public interface IAuthenticationSchemeProvider{   

 void AddScheme(AuthenticationScheme scheme);     Task<IEnumerable<AuthenticationScheme>> GetAllSchemesAsync();     Task<AuthenticationScheme> GetSchemeAsync(string name);     Task<IEnumerable<AuthenticationScheme>> GetRequestHandlerSchemesAsync();    Task<AuthenticationScheme> GetDefaultAuthenticateSchemeAsync();    Task<AuthenticationScheme> GetDefaultChallengeSchemeAsync();    Task<AuthenticationScheme> GetDefaultForbidSchemeAsync();    Task<AuthenticationScheme> GetDefaultSignInSchemeAsync();   Task<AuthenticationScheme> GetDefaultSignOutSchemeAsync();
}

其 AddScheme 方法,用来注册Scheme,而每一种Scheme最终体现为一个 AuthenticationScheme 类型的对象:

public class AuthenticationScheme{   

 public AuthenticationScheme(string name, string displayName, Type handlerType)    {         if (!typeof(IAuthenticationHandler).IsAssignableFrom(handlerType)){               throw new ArgumentException("handlerType must implement IAuthenticationSchemeHandler.");}...}    public string Name { get; }    public string DisplayName { get; }    public Type HandlerType { get; }
}

每一个Scheme中还包含一个对应的IAuthenticationHandler类型的Handler,由它来完成具体的处理逻辑,看一下它的默认实现:

public class AuthenticationSchemeProvider : IAuthenticationSchemeProvider{    private IDictionary<string, AuthenticationScheme> _map = new Dictionary<string, AuthenticationScheme>(StringComparer.Ordinal);  

   public AuthenticationSchemeProvider(IOptions<AuthenticationOptions> options)    {_options = options.Value;           foreach (var builder in _options.Schemes){                  var scheme = builder.Build();AddScheme(scheme);}}   

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

如上,通过一个内部的字典来保存我们所注册的Scheme,key为Scheme名称,然后提供一系列对该字典的查询。它还提供了一系列的GetDefaultXXXSchemeAsync方法,所使用的Key是通过构造函数中接收的AuthenticationOptions对象来获取的,如果未配置,则返回为null

对于 AuthenticationOptions 对象,大家可能会比较熟悉,在上面介绍的 AddAuthenticationCore 扩展方法中,也是使用该对象来配置认证系统:

public class AuthenticationOptions{  

  private readonly IList<AuthenticationSchemeBuilder> _schemes = new List<AuthenticationSchemeBuilder>();    

  public IEnumerable<AuthenticationSchemeBuilder> Schemes => _schemes;  

    public IDictionary<string, AuthenticationSchemeBuilder> SchemeMap { get; } = new Dictionary<string, AuthenticationSchemeBuilder>(StringComparer.Ordinal);   

     public void AddScheme(string name, Action<AuthenticationSchemeBuilder> configureBuilder)    {             if (SchemeMap.ContainsKey(name)){                  throw new InvalidOperationException("Scheme already exists: " + name);}               var builder = new AuthenticationSchemeBuilder(name);configureBuilder(builder);_schemes.Add(builder);SchemeMap[name] = builder;}    

    public void AddScheme<THandler>(string name, string displayName) where THandler : IAuthenticationHandler=> AddScheme(name, b =>{b.DisplayName = displayName;b.HandlerType = typeof(THandler);});   

    public string DefaultScheme { get; set; }  

      public string DefaultAuthenticateScheme { get; set; }          public string DefaultSignInScheme { get; set; }   

         public string DefaultSignOutScheme { get; set; } 

       public string DefaultChallengeScheme { get; set; }  

      public string DefaultForbidScheme { get; set; }
}

该对象可以帮助我们更加方便的注册Scheme,提供泛型和 AuthenticationSchemeBuilder 两种方式配置方式。

到此,我们了解到,要想使用认证系统,必要先注册Scheme,而每一个Scheme必须指定一个Handler,否则会抛出异常,下面我们就来了解一下Handler。

IAuthenticationHandlerProvider

在 ASP.NET Core 的认证系统中,AuthenticationHandler 负责对用户凭证的验证,它定义了如下接口:

public interface IAuthenticationHandler{   

 Task InitializeAsync(AuthenticationScheme scheme, HttpContext context);    Task<AuthenticateResult> AuthenticateAsync();   

  Task ChallengeAsync(AuthenticationProperties properties);   

   Task ForbidAsync(AuthenticationProperties properties);
}

AuthenticationHandler的创建是通过 IAuthenticationHandlerProvider 来完成的:

public interface IAuthenticationHandlerProvider{    Task<IAuthenticationHandler> GetHandlerAsync(HttpContext context, string authenticationScheme);
}

Provider 只定义了一个 GetHandlerAsync 方法,来获取指定的Scheme的Hander,在 ASP.NET Core 中,很多地方都使用了类似的 Provider 模式。

而HandlerProvider的实现,我们通过对上面SchemeProvider的了解,应该可以猜到一二,因为在 AuthenticationScheme 中已经包含了Hander:

public class AuthenticationHandlerProvider : IAuthenticationHandlerProvider{    

public AuthenticationHandlerProvider(IAuthenticationSchemeProvider schemes)    {Schemes = schemes;}   

 public IAuthenticationSchemeProvider Schemes { get; }   

  private Dictionary<string, IAuthenticationHandler> _handlerMap = new Dictionary<string, IAuthenticationHandler>(StringComparer.Ordinal);    

  public async Task<IAuthenticationHandler> 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;}
}

可以看到,AuthenticationHandlerProvider 首先使用 IAuthenticationSchemeProvider 获取到当前Scheme,然后先从DI中查找是否有此Scheme中的Handler,如果未注册到DI系统中,则使用 ActivatorUtilities 来创建其实例,并缓存到内部的 _handlerMap 字典中。

IAuthenticationService

IAuthenticationService 本质上是对 IAuthenticationSchemeProvider 和 IAuthenticationHandlerProvider 封装,用来对外提供一个统一的认证服务接口:

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

这5个方法中,都需要接收一个 scheme 参数,因为只有先指定你要使用的认证方式,才能知道该如何进行认证。

对于上面的前三个方法,我们知道在IAuthenticationHandler中都有对应的实现,而SignInAsyncSignOutAsync则使用了独立的定义接口:

public interface IAuthenticationSignInHandler : IAuthenticationSignOutHandler{      Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties properties);
}

   public interface IAuthenticationSignOutHandler : IAuthenticationHandler{    Task SignOutAsync(AuthenticationProperties properties);
}

SignInAsync 和 SignOutAsync 之所以使用独立的接口,是因为在现代架构中,通常会提供一个统一的认证中心,负责证书的颁发及销毁(登入和登出),而其它服务只用来验证证书,并用不到SingIn/SingOut。

而 IAuthenticationService 的默认实现 AuthenticationService 中的逻辑就非常简单了,只是调用Handler中的同名方法:

public class AuthenticationService : IAuthenticationService{  

  public IAuthenticationSchemeProvider Schemes { get; }    

  public IAuthenticationHandlerProvider Handlers { get; }  

  public IClaimsTransformation Transform { 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.");}}              var handler = await Handlers.GetHandlerAsync(context, 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;}
}

AuthenticationService中对这5个方法的实现大致相同,首先会在我们传入的scheme为null时,来获取我们所注册的默认scheme,然后获取调用相应Handler的即可。针对 SignInAsync 和 SignOutAsync 的实现则会判断Handler是否实现了对应的接口,若未实现则抛出异常。

不过在这里还涉及到如下两个对象:

AuthenticateResult

AuthenticateResult 用来表示认证的结果:

public class AuthenticateResult{    public AuthenticationTicket Ticket { get; protected set; }        public bool Succeeded => Ticket != null;         public ClaimsPrincipal Principal => Ticket?.Principal;        public AuthenticationProperties Properties => Ticket?.Properties;                         public Exception Failure { get; protected set; }       public bool None { get; protected set; }   

      public static AuthenticateResult Success(AuthenticationTicket ticket) => new AuthenticateResult() { Ticket = ticket }; 

      public static AuthenticateResult NoResult() => new AuthenticateResult() { None = true };

      public static AuthenticateResult Fail(Exception failure) => new AuthenticateResult() { Failure = failure }; 

      public static AuthenticateResult Fail(string failureMessage) => new AuthenticateResult() { Failure = new Exception(failureMessage) };
}

它主要包含一个核心属性 AuthenticationTicket

public class AuthenticationTicket{ public string AuthenticationScheme { get; private set; }    public ClaimsPrincipal Principal { get; private set; }      public AuthenticationProperties Properties { get; private set; }
}

我们可以把AuthenticationTicket看成是一个经过认证后颁发的证书,

其 ClaimsPrincipal 属性我们较为熟悉,表示证书的主体,在基于声明的认证中,用来标识一个人的身份(如:姓名,邮箱等等),后续会详细介绍一下基于声明的认证。

而 AuthenticationProperties 属性用来表示证书颁发的相关信息,如颁发时间,过期时间,重定向地址等等:

public class AuthenticationProperties{  

  public IDictionary<string, string> Items { get; }  

    public string RedirectUri{            get{                 string value;                  return Items.TryGetValue(RedirectUriKey, out value) ? value : null;}                set{                     if (value != null) Items[RedirectUriKey] = value;              else{                            if (Items.ContainsKey(RedirectUriKey)) Items.Remove(RedirectUriKey);}}}...
}

在上面最开始介绍的HttpContext中的 GetTokenAsync 扩展方法便是对AuthenticationProperties的扩展:

public static class AuthenticationTokenExtensions{   

 private static string TokenNamesKey = ".TokenNames";     private static string TokenKeyPrefix = ".Token.";      public static void StoreTokens(this AuthenticationProperties properties, IEnumerable<AuthenticationToken> tokens) {}    public static bool UpdateTokenValue(this AuthenticationProperties properties, string tokenName, string tokenValue) {}     public static IEnumerable<AuthenticationToken> GetTokens(this AuthenticationProperties properties) { }     public static string GetTokenValue(this AuthenticationProperties properties, string tokenName)    {          var tokenKey = TokenKeyPrefix + tokenName;           return properties.Items.ContainsKey(tokenKey) ? properties.Items[tokenKey] : null;}    public static Task<string> GetTokenAsync(this IAuthenticationService auth, HttpContext context, string tokenName)        => auth.GetTokenAsync(context, scheme: null, tokenName: tokenName);     public static async Task<string> GetTokenAsync(this IAuthenticationService auth, HttpContext context, string scheme, string tokenName)    {         var result = await auth.AuthenticateAsync(context, scheme);        return result?.Properties?.GetTokenValue(tokenName);}
}

如上,Token扩展只是对AuthenticationProperties中的 Items 属性进行添加和读取。

IClaimsTransformation

IClaimsTransformation 用来对由我们的应用程序传入的 ClaimsPrincipal 进行转换,它只定义了一个 Transform 方法:

public interface IClaimsTransformation{    Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal);
}

其默认实现,不做任何处理,直接返回。它适合于全局的为 ClaimsPrincipal 添加一些预定义的声明,如添加当前时间等,然后在DI中把我们的实现注册进去即可。

Usage

下面我们演示一下 ASP.NET Core 认证系统的实际用法:

首先,我们要定义一个Handler:

public class MyHandler : IAuthenticationHandler, IAuthenticationSignInHandler, IAuthenticationSignOutHandler{   

 public AuthenticationScheme Scheme { get; private set; }  

   protected HttpContext Context { get; private set; }   

    public Task InitializeAsync(AuthenticationScheme scheme, HttpContext context)    {Scheme = scheme;Context = context;        return Task.CompletedTask;}   

 public async Task<AuthenticateResult> AuthenticateAsync()    {         var cookie = Context.Request.Cookies["mycookie"];          if (string.IsNullOrEmpty(cookie)){            return AuthenticateResult.NoResult();}            return AuthenticateResult.Success(Deserialize(cookie));}    

public Task ChallengeAsync(AuthenticationProperties properties)    {Context.Response.Redirect("/login");        return Task.CompletedTask;}   

 public Task ForbidAsync(AuthenticationProperties properties)    {Context.Response.StatusCode = 403;        return Task.CompletedTask;}  

  public Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties properties)    {        var ticket = new AuthenticationTicket(user, properties, Scheme.Name);Context.Response.Cookies.Append("myCookie", Serialize(ticket));        return Task.CompletedTask;}    

  public Task SignOutAsync(AuthenticationProperties properties)    {Context.Response.Cookies.Delete("myCookie");          return Task.CompletedTask;}
}

如上,在 SignInAsync 中将用户的Claim序列化后保存到Cookie中,在 AuthenticateAsync 中从Cookie中读取并反序列化成用户Claim。

然后在DI系统中注册我们的Handler和Scheme:

public void ConfigureServices(IServiceCollection services){services.AddAuthenticationCore(options => options.AddScheme<MyHandler>("myScheme", "demo scheme"));
}

最后,便可以通过HttpContext来调用认证系统了:

public void Configure(IApplicationBuilder app){    // 登录app.Map("/login", builder => builder.Use(next =>{         return async (context) =>{              var claimIdentity = new ClaimsIdentity();                 claimIdentity.AddClaim(new Claim(ClaimTypes.Name, "jim"));            await context.SignInAsync("myScheme", new ClaimsPrincipal(claimIdentity));};}));          // 退出app.Map("/logout", builder => builder.Use(next =>{               return async (context) =>{                    await context.SignOutAsync("myScheme");};}));    // 认证app.Use(next =>{                 return async (context) =>{                     var result = await context.AuthenticateAsync("myScheme");                     if (result?.Principal != null) context.User = result.Principal;                         await next(context);};});    // 授权app.Use(async (context, next) =>{  

         var user = context.User;           if (user?.Identity?.IsAuthenticated ?? false){              if (user.Identity.Name != "jim") await context.ForbidAsync("myScheme");             else await next();}                else{                    await context.ChallengeAsync("myScheme");}});    // 访问受保护资源app.Map("/resource", builder => builder.Run(async (context) => await context.Response.WriteAsync("Hello, ASP.NET Core!")));
}

在这里完整演示了 ASP.NET Core 认证系统的基本用法,当然,在实际使用中要比这更加复杂,如安全性,易用性等方面的完善,但本质上也就这么多东西。

总结

本章基于 HttpAbstractions 对 ASP.NET Core 认证系统做了一个简单的介绍,但大多是一些抽象层次的定义,并未涉及到具体的实现。因为现实中有各种各样的场景无法预测,HttpAbstractions 提供了统一的认证规范,在我们的应用程序中,可以根据具体需求来灵活的扩展适合的认证方式。不过在 Security 提供了更加具体的实现方式,也包含了 Cookie, JwtBearer, OAuth, OpenIdConnect 等较为常用的认证实现。在下个系列会来详细介绍一下 ASP.NET Core 的认证与授权,更加偏向于实战,敬请期待!

ASP.NET Core 在GitHub上的开源地址为:https://github.com/aspnet,包含了100多个项目,ASP.NET Core 的核心是 HttpAbstractions ,其它的都是围绕着 HttpAbstractions 进行的扩展。本系列文章所涉及到的源码只包含 Hosting 和 HttpAbstractions ,它们两个已经构成了一个完整的 ASP.NET Core 运行时,不需要其它模块,就可以轻松应对一些简单的场景。当然,更多的时候我们还会使用比较熟悉的 Mvc 来大大提高开发速度和体验,后续再来介绍一下MVC的运行方式。

相关文章:

  • .NET Core 2.0 正式发布信息汇总

  • .NET Standard 2.0 特性介绍和使用指南

  • .NET Core 2.0 的dll实时更新、https、依赖包变更问题及解决

  • .NET Core 2.0 特性介绍和使用指南

  • Entity Framework Core 2.0 新特性

  • 体验 PHP under .NET Core

  • .NET Core 2.0使用NLog

  • 升级项目到.NET Core 2.0,在Linux上安装Docker,并成功部署

  • 解决Visual Studio For Mac Restore失败的问题

  • ASP.NET Core 2.0 特性介绍和使用指南

  • .Net Core下通过Proxy 模式 使用 WCF

  • .NET Core 2.0 开源Office组件 NPOI

  • ASP.NET Core Razor页面 vs MVC

  • Razor Page–Asp.Net Core 2.0新功能  Razor Page介绍

  • MySql 使用 EF Core 2.0 CodeFirst、DbFirst、数据库迁移(Migration)介绍及示例

  • .NET Core 2.0迁移技巧之web.config配置文件

  • asp.net core MVC 过滤器之ExceptionFilter过滤器(一)

  • ASP.NET Core 使用Cookie验证身份

  • ASP.NET Core MVC – Tag Helpers 介绍

  • ASP.NET Core MVC – Caching Tag Helpers

  • ASP.NET Core MVC – Form Tag Helpers

  • ASP.NET Core MVC – 自定义 Tag Helpers

  • ASP.NET Core MVC – Tag Helper 组件

  • ASP.NET Core 运行原理解剖[1]:Hosting

  • ASP.NET Core 运行原理解剖[2]:Hosting补充之配置介绍

  • ASP.NET Core 运行原理解剖[3]:Middleware-请求管道的构成

  • ASP.NET Core 运行原理解剖[4]:进入HttpContext的世界

原文地址:http://www.cnblogs.com/RainingNight/p/authentication-in-asp-net-core.html


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

ASP.NET Core 运行原理解剖[5]:Authentication相关推荐

  1. ASP.NET Core 运行原理解剖[1]:Hosting

    SP.NET Core 是新一代的 ASP.NET,第一次出现时代号为 ASP.NET vNext,后来命名为ASP.NET 5,随着它的完善与成熟,最终命名为 ASP.NET Core,表明它不是 ...

  2. ASP.NET Core 运行原理解剖[4]:进入HttpContext的世界

    本系列文章从源码分析的角度来探索 ASP.NET Core 的运行原理,分为以下几个章节: ASP.NET Core 运行原理解剖[1]:Hosting ASP.NET Core 运行原理解剖[2]: ...

  3. ASP.NET Core 运行原理解剖[3]:Middleware-请求管道的构成

    在 ASP.NET 中,我们知道,它有一个面向切面的请求管道,有19个主要的事件构成,能够让我们进行灵活的扩展.通常是在 web.config 中通过注册 HttpModule 来实现对请求管道事件监 ...

  4. ASP.NET Core 运行原理解剖[2]:Hosting补充之配置介绍

    在上一章ASP.NET Core 运行原理解剖[1]:Hosting中,我们介绍了 ASP.NET Core 的启动过程,主要是对 WebHost 源码的探索.而本文则是对上文的一个补充,更加偏向于实 ...

  5. ASP.NET Core 运行原理剖析2:Startup 和 Middleware(中间件)

    在上一节(ASP.NET Core 运行原理剖析1:初始化WebApp模版并运行)中提到ASP.NET Core WebApp 必须含有Startup类,在本节中将重点讲解Startup类以及Midd ...

  6. ASP.NET Core 运行原理剖析1:初始化WebApp模版并运行

    之前两篇文章简析.NET Core 以及与 .NET Framework的关系和.NET Core的构成体系从总体上介绍.NET Core,接下来计划用一个系列对ASP.NET Core的运行原理进行 ...

  7. ASP.NET Core 运行原理剖析

    1.1. 概述 在ASP.NET Core之前,ASP.NET Framework应用程序由IIS加载.Web应用程序的入口点由InetMgr.exe创建并调用托管.以初始化过程中触发HttpAppl ...

  8. ASP.NET Core 运行原理

    1.1. 概述 在ASP.NET Core之前,ASP.NET Framework应用程序由IIS加载.Web应用程序的入口点由InetMgr.exe创建并调用托管.以初始化过程中触发HttpAppl ...

  9. 一张大图了解ASP.NET Core 3.1 中的Authentication与Authorization

    下面是一张ASP.NET Core 3.1 中关于Authentication与Authorization的主流程框线图,点击这里查看全图:https://johnnyqian.net/images/ ...

最新文章

  1. JavaScript原型-进阶者指南
  2. ubuntu下解决oracle sqlplus不能查看历史命令问题
  3. python格式化输出区别_python格式化输出的区别
  4. java属性错误_在java中读取属性文件时发生文件未找到错误
  5. ACM数论【乘法逆元】
  6. php_redis配置安装php_redis-5.1.1-7.4-nts-vc15-x64.zip
  7. ChartControl柱状图指定每一个柱子的颜色
  8. Simulink汽车动力学仿真
  9. win10锁屏账户和计算机名,如何玩转Win10锁屏和主题
  10. 谷歌:新人工智能(AI)算法预测人类死亡时间,意念可指挥机器人
  11. 修改内存卡里linux系统文件怎么恢复,EMMC转SD卡做成内存卡恢复数据教程
  12. 第三方接口该如何调用
  13. kubernetes简介
  14. Swift语法基础:19 - Swift的控制转移的语句, Continue, Break, Fallthrough, Labeled Statements
  15. 基于遗传算法改进BP神经网络的电池容量SOC预测
  16. Fomo3D式套利模式再现:“聪明”玩家狠狠地薅了一把羊毛!
  17. 如果风有轨迹,那么尽头是你
  18. Python 爬虫进阶必备 | 关于某租房网站数据加密的分析(送两本 Python 书)
  19. MySql cmd下的学习笔记 —— 有关select的操作(in, and, where, like等等)
  20. 【王道操作系统】2.2.1 处理机调度的概念与层次

热门文章

  1. Openssl搭建私有CA认证
  2. NSPredicate 谓词
  3. NO.106 需求的状态、研发阶段及注意事项。
  4. Locations Section of OpenCascade BRep
  5. UILabel自适应高度和自动换行
  6. MySQL IFNull 详解
  7. 10分钟采集凡客最新的省、市、区、邮政编码和电话区号(附源码)
  8. 完美:C# Blazor中显示Markdown并添加代码高亮
  9. C# 使用Awaiter
  10. C# 死锁的原理与排查方法详解