整体概况

整个asp.net core在StartUp类的Configure方法中使用一个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));
this._next = next;
this.Schemes = schemes;
} public IAuthenticationSchemeProvider Schemes { get; set; } public async Task Invoke(HttpContext context)
{
context.Features.Set<IAuthenticationFeature>((IAuthenticationFeature) new AuthenticationFeature()
{
OriginalPath = context.Request.Path,
OriginalPathBase = context.Request.PathBase
});
IAuthenticationHandlerProvider handlers = context.RequestServices.GetRequiredService<IAuthenticationHandlerProvider>();
foreach (AuthenticationScheme authenticationScheme in await this.Schemes.GetRequestHandlerSchemesAsync())//GetReRequestHandlersAsync方法返回_requestHandlers字段保存的所有远程认证的Scheme
{
IAuthenticationRequestHandler handlerAsync = await handlers.GetHandlerAsync(context, authenticationScheme.Name) as IAuthenticationRequestHandler;
bool flag = handlerAsync != null;
if (flag)
flag = await handlerAsync.HandleRequestAsync();
if (flag)
return;
}
AuthenticationScheme authenticateSchemeAsync = await this.Schemes.GetDefaultAuthenticateSchemeAsync();
if (authenticateSchemeAsync != null)
{
AuthenticateResult authenticateResult = await context.AuthenticateAsync(authenticateSchemeAsync.Name);
if (authenticateResult?.Principal != null)
context.User = authenticateResult.Principal;
}
await this._next(context);
}
}

基本的流程是先找一个IAuthenticationRequestHandler,这个接口代表了远程认证,逻辑是①找到一个不为空的handler并执行HandlerRequestAsync;②如果执行结果为true,则返回,否则,继续下一个循环。如果没有找到任何IAuthenticationRequestHandler,则继续由GetDefaultAuthenticateSchemeAsync方法来找一个默认的本地认证Scheme,这个本地认证Scheme的逻辑是从AuthenticationOptions中先查看DefaultAuthenticationScheme是否有值(string),如果没有,再从DefaultScheme中查看是否有值(string)如果两者都没有,那么返回一个null。(注:认证中间件依赖一个IAuthenticationSchemeProvider(构造函数注入),后者的默认实现AuthenticationSchemeProvider依赖AuthenticationOptions类)。

整个asp.net core 认证的线索为IAuthenticationService,这个接口声明了5个动作方法,其中,具体的执行是由IAuthenticationHandler来执行的,这个接口定义了AuthenticateAsync()、ForbiddenAsync()和ChallengeAsync()。而SignInAsync和SignOutAsync则分别由IAuthenticationSignInHandler和IAuthenticationSignOutHandler来定义的。后两个接口也继承自IAuthenticationHandler,IAuthenticationHandler由IAuthenticationHandlerProvider来提供,IAuthenticationHandlerProvider使用IAuthenticationSchemeProvider来提供一个具体的AuthenticationScheme,AuthenticationScheme代表一个具体的方案,这个Scheme中包含了执行这个方案需要的HandlerType,也即是IAuthenticationHandler,从Scheme中拿到这个HandlerType之后,从DI或者ActivityUtils中得到具体的IAuthenticaitonHandler来执行最终逻辑。其他的诸如AuthenticateResult代表一个认证验证结果,他里面维护了一个AuthenticationTicket,还有一个AuthenticateProperties表示认证的一些特征,如颁发时间、过期时间等等。

这篇文章涉及的源码包括Microsoft.AspNETCore.Authentication.Abstraction、Microsoft.AspNETCore.Authentication.Core和Microsoft.AspNETCore.Authentication

认证和授权很相似,他们的英文也很相似,一个是Authentication认证,一个是Authorization授权。

asp.net core中的认证需要在Startup类中进行配置:

//ConfigureServices方法中:
services.AddAuthentication(option =>
{
option.DefaultScheme = "Cookie";
option.DefaultChallengeScheme = "Cookie";
option.DefaultAuthenticateScheme = "Cookie";
option.DefaultForbidScheme = "Cookie";
option.DefaultSignInScheme = "Cookie";
option.DefaultSignOutScheme = "Cookie";
}).AddCookie("Cookie", option =>
{
option.LoginPath = "/Account/Login";
option.AccessDeniedPath = "/Account/Forbidden";
//.......
});
//Configure方法中
app.UseAuthentication();

看一看到如果需要认证的话是需要分别在ConfigureService方法和Configure方法中分别进行配置的。

我们看到上面在AddAuthentication方法中配置了一个option,这个option是一个Action<AuthenticationOption>,在里面,写了一堆scheme。这个scheme是什么意思呢?我们先解释一下在asp.neet core中发生的这几个动作。在asp.net core中是有5个动作要发生的:

1、登陆(Signin):用户要进行登陆的动作。

2、登出(Signout):用户要进行登出。

3、Challenge:这个不好翻译,意思当用户需要请求一个被保护的资源时,系统要求用户进行登陆。总之他也是一个登陆的动作,但是被动的登陆,一般返回401。

4、Authenticate:认证,系统将用户的信息从token/cookie中读取出来。和登陆这个动作正好相反。

5、Forbid:系统对用户执行了拒绝的操作。一般返回403

上面这些动作最后都是由一个Handler来执行的,这个handler就是一个IAuthenticationHandler的实现。

我们先给出了上面的总结,再看一下具体的情况。asp.net core2.0开始上面的这些动作的执行都是通过HttpContext的扩展方法来执行的。我们拿登陆来说,其他都大同小异。

先看HttpContext.SigninAsync这个方法:

            var claim = new Claim("name", "wallee");//claim相当于我的众多信息中的一个信息单元,还有年龄、性别、家庭等等
var identity = new ClaimsIdentity("身份证");//identity表示一个claim集合,这个集合代表了一个完整的“证件信息”,比如我的身份证
identity.AddClaim(claim);//将上面那个信息片段添加到我的身份证里面
var me=new ClaimsPrincipal(identity);//将身份证作为我个人的初始化参数,初始化一个ClaimsPrincipal就代表了我这个主体。还可以添加其他的identity,如还有驾驶证、准考证、会计证、计算机二级证等等
            HttpContext.SignInAsync(me);//最后,利用这个主体,调用HttpContext的扩展方法进行登陆。

上面的代码中注释解释了一些和本文无关但又非常重要的信息,我们关键看最后哪一行:HttpContext.SigninAsync(principal);这行代码实现了最终的登陆。现在我们看一下它的实现:

public static Task SignInAsync(this HttpContext context, string scheme, ClaimsPrincipal principal, AuthenticationProperties properties)
{
return context.RequestServices.GetRequiredService<IAuthenticationService>().SignInAsync(context, scheme, principal, properties);
}

其实针对HttpContext的扩展方法都是调用IAuthenticationService来执行的,IAuthenticationService里面定义了针对上面描述的所有动作方法:

 /// <summary>Used to provide authentication.</summary>
public interface IAuthenticationService
{
/// <summary>Authenticate for the specified authentication scheme.</summary>
/// <param name="context">The <see cref="T:Microsoft.AspNetCore.Http.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="T:Microsoft.AspNetCore.Http.HttpContext" />.</param>
/// <param name="scheme">The name of the authentication scheme.</param>
/// <param name="properties">The <see cref="T:Microsoft.AspNetCore.Authentication.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="T:Microsoft.AspNetCore.Http.HttpContext" />.</param>
/// <param name="scheme">The name of the authentication scheme.</param>
/// <param name="properties">The <see cref="T:Microsoft.AspNetCore.Authentication.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="T:Microsoft.AspNetCore.Http.HttpContext" />.</param>
/// <param name="scheme">The name of the authentication scheme.</param>
/// <param name="principal">The <see cref="T:System.Security.Claims.ClaimsPrincipal" /> to sign in.</param>
/// <param name="properties">The <see cref="T:Microsoft.AspNetCore.Authentication.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="T:Microsoft.AspNetCore.Http.HttpContext" />.</param>
/// <param name="scheme">The name of the authentication scheme.</param>
/// <param name="properties">The <see cref="T:Microsoft.AspNetCore.Authentication.AuthenticationProperties" />.</param>
/// <returns>A task.</returns>
Task SignOutAsync(HttpContext context, string scheme, AuthenticationProperties properties);
}

IAuthenticateService在services.AddAuthentication()方法中以scoped的生命周期被注入到了DI,表示针对每次请求的新的实例。同时还被注入的有另外几个比较关键的接口:

services.TryAddSingleton<IClaimsTransformation, NoopClaimsTransformation>();//ClaimsTransFormation用来在全局将ClaimsPrincipal添加一些新的Claims,默认注入的这个是啥都没干
services.TryAddScoped<IAuthenticationHandlerProvider, AuthenticationHandlerProvider>();//AuthenticationHandler的提供者,提供执行Authenticatoin、Forbidden和Challenge等动作的handler
services.TryAddSingleton<IAuthenticationSchemeProvider, AuthenticationSchemeProvider>();//AuthenticationScheme的提供者,AuthenticationScheme是对AuthenticationHandler的描述,通过Scheme找到相应的Handler

IAuthenticationService是对IAuthenticationSchemeProvider和IAuthenticationHandlerProvider的封装,它有一个AuthenticationService的实现:

 /// <summary>
/// Implements <see cref="T:Microsoft.AspNetCore.Authentication.IAuthenticationService" />.
/// </summary>
public class AuthenticationService : IAuthenticationService
{
/// <summary>Constructor.</summary>
/// <param name="schemes">The <see cref="T:Microsoft.AspNetCore.Authentication.IAuthenticationSchemeProvider" />.</param>
/// <param name="handlers">The <see cref="T:Microsoft.AspNetCore.Authentication.IAuthenticationRequestHandler" />.</param>
/// <param name="transform">The <see cref="T:Microsoft.AspNetCore.Authentication.IClaimsTransformation" />.</param>
public AuthenticationService(
IAuthenticationSchemeProvider schemes,
IAuthenticationHandlerProvider handlers,
IClaimsTransformation transform)
{
this.Schemes = schemes;
this.Handlers = handlers;
this.Transform = transform;
} /// <summary>Used to lookup AuthenticationSchemes.</summary>
public IAuthenticationSchemeProvider Schemes { get; } /// <summary>Used to resolve IAuthenticationHandler instances.</summary>
public IAuthenticationHandlerProvider Handlers { get; } /// <summary>Used for claims transformation.</summary>
public IClaimsTransformation Transform { get; } /// <summary>Authenticate for the specified authentication scheme.</summary>
/// <param name="context">The <see cref="T:Microsoft.AspNetCore.Http.HttpContext" />.</param>
/// <param name="scheme">The name of the authentication scheme.</param>
/// <returns>The result.</returns>
public virtual async Task<AuthenticateResult> AuthenticateAsync(
HttpContext context,
string scheme)
{
if (scheme == null)
{
scheme = (await this.Schemes.GetDefaultAuthenticateSchemeAsync())?.Name;
if (scheme == null)
throw new InvalidOperationException("No authenticationScheme was specified, and there was no DefaultAuthenticateScheme found.");
}
IAuthenticationHandler handlerAsync = await this.Handlers.GetHandlerAsync(context, scheme);
if (handlerAsync == null)
throw await this.CreateMissingHandlerException(scheme);
AuthenticateResult result = await handlerAsync.AuthenticateAsync();
return result != null && result.Succeeded ? AuthenticateResult.Success(new AuthenticationTicket(await this.Transform.TransformAsync(result.Principal), result.Properties, result.Ticket.AuthenticationScheme)) : result;
} /// <summary>Challenge the specified authentication scheme.</summary>
/// <param name="context">The <see cref="T:Microsoft.AspNetCore.Http.HttpContext" />.</param>
/// <param name="scheme">The name of the authentication scheme.</param>
/// <param name="properties">The <see cref="T:Microsoft.AspNetCore.Authentication.AuthenticationProperties" />.</param>
/// <returns>A task.</returns>
public virtual async Task ChallengeAsync(
HttpContext context,
string scheme,
AuthenticationProperties properties)
{
if (scheme == null)
{
scheme = (await this.Schemes.GetDefaultChallengeSchemeAsync())?.Name;
if (scheme == null)
throw new InvalidOperationException("No authenticationScheme was specified, and there was no DefaultChallengeScheme found.");
}
IAuthenticationHandler handlerAsync = await this.Handlers.GetHandlerAsync(context, scheme);
if (handlerAsync == null)
throw await this.CreateMissingHandlerException(scheme);
await handlerAsync.ChallengeAsync(properties);
} /// <summary>Forbid the specified authentication scheme.</summary>
/// <param name="context">The <see cref="T:Microsoft.AspNetCore.Http.HttpContext" />.</param>
/// <param name="scheme">The name of the authentication scheme.</param>
/// <param name="properties">The <see cref="T:Microsoft.AspNetCore.Authentication.AuthenticationProperties" />.</param>
/// <returns>A task.</returns>
public virtual async Task ForbidAsync(
HttpContext context,
string scheme,
AuthenticationProperties properties)
{
if (scheme == null)
{
scheme = (await this.Schemes.GetDefaultForbidSchemeAsync())?.Name;
if (scheme == null)
throw new InvalidOperationException("No authenticationScheme was specified, and there was no DefaultForbidScheme found.");
}
IAuthenticationHandler handlerAsync = await this.Handlers.GetHandlerAsync(context, scheme);
if (handlerAsync == null)
throw await this.CreateMissingHandlerException(scheme);
await handlerAsync.ForbidAsync(properties);
} /// <summary>
/// Sign a principal in for the specified authentication scheme.
/// </summary>
/// <param name="context">The <see cref="T:Microsoft.AspNetCore.Http.HttpContext" />.</param>
/// <param name="scheme">The name of the authentication scheme.</param>
/// <param name="principal">The <see cref="T:System.Security.Claims.ClaimsPrincipal" /> to sign in.</param>
/// <param name="properties">The <see cref="T:Microsoft.AspNetCore.Authentication.AuthenticationProperties" />.</param>
/// <returns>A task.</returns>
public virtual async Task SignInAsync(
HttpContext context,
string scheme,
ClaimsPrincipal principal,
AuthenticationProperties properties)
{
if (principal == null)
throw new ArgumentNullException(nameof (principal));
if (scheme == null)
{
scheme = (await this.Schemes.GetDefaultSignInSchemeAsync())?.Name;
if (scheme == null)
throw new InvalidOperationException("No authenticationScheme was specified, and there was no DefaultSignInScheme found.");
}
IAuthenticationHandler handlerAsync = await this.Handlers.GetHandlerAsync(context, scheme);
if (handlerAsync == null)
throw await this.CreateMissingSignInHandlerException(scheme);
if (!(handlerAsync is IAuthenticationSignInHandler authenticationSignInHandler))
throw await this.CreateMismatchedSignInHandlerException(scheme, handlerAsync);
await authenticationSignInHandler.SignInAsync(principal, properties);
} /// <summary>Sign out the specified authentication scheme.</summary>
/// <param name="context">The <see cref="T:Microsoft.AspNetCore.Http.HttpContext" />.</param>
/// <param name="scheme">The name of the authentication scheme.</param>
/// <param name="properties">The <see cref="T:Microsoft.AspNetCore.Authentication.AuthenticationProperties" />.</param>
/// <returns>A task.</returns>
public virtual async Task SignOutAsync(
HttpContext context,
string scheme,
AuthenticationProperties properties)
{
if (scheme == null)
{
scheme = (await this.Schemes.GetDefaultSignOutSchemeAsync())?.Name;
if (scheme == null)
throw new InvalidOperationException("No authenticationScheme was specified, and there was no DefaultSignOutScheme found.");
}
IAuthenticationHandler handlerAsync = await this.Handlers.GetHandlerAsync(context, scheme);
if (handlerAsync == null)
throw await this.CreateMissingSignOutHandlerException(scheme);
if (!(handlerAsync is IAuthenticationSignOutHandler authenticationSignOutHandler))
throw await this.CreateMismatchedSignOutHandlerException(scheme, handlerAsync);
await authenticationSignOutHandler.SignOutAsync(properties);
} private async Task<Exception> CreateMissingHandlerException(string scheme)
{
string str1 = string.Join(", ", (await this.Schemes.GetAllSchemesAsync()).Select<AuthenticationScheme, string>((Func<AuthenticationScheme, string>) (sch => sch.Name)));
string str2 = string.Format(" Did you forget to call AddAuthentication().Add[SomeAuthHandler](\"{0}\",...)?", (object) scheme);
return !string.IsNullOrEmpty(str1) ? (Exception) new InvalidOperationException(string.Format("No authentication handler is registered for the scheme '{0}'. The registered schemes are: {1}.", (object) scheme, (object) str1) + str2) : (Exception) new InvalidOperationException("No authentication handlers are registered." + str2);
} private async Task<string> GetAllSignInSchemeNames()
{
return string.Join(", ", (await this.Schemes.GetAllSchemesAsync()).Where<AuthenticationScheme>((Func<AuthenticationScheme, bool>) (sch => typeof (IAuthenticationSignInHandler).IsAssignableFrom(sch.HandlerType))).Select<AuthenticationScheme, string>((Func<AuthenticationScheme, string>) (sch => sch.Name)));
} private async Task<Exception> CreateMissingSignInHandlerException(string scheme)
{
string signInSchemeNames = await this.GetAllSignInSchemeNames();
string str = string.Format(" Did you forget to call AddAuthentication().AddCookies(\"{0}\",...)?", (object) scheme);
return !string.IsNullOrEmpty(signInSchemeNames) ? (Exception) new InvalidOperationException(string.Format("No sign-in authentication handler is registered for the scheme '{0}'. The registered sign-in schemes are: {1}.", (object) scheme, (object) signInSchemeNames) + str) : (Exception) new InvalidOperationException("No sign-in authentication handlers are registered." + str);
} private async Task<Exception> CreateMismatchedSignInHandlerException(
string scheme,
IAuthenticationHandler handler)
{
string signInSchemeNames = await this.GetAllSignInSchemeNames();
string str = string.Format("The authentication handler registered for scheme '{0}' is '{1}' which cannot be used for SignInAsync. ", (object) scheme, (object) handler.GetType().Name);
return !string.IsNullOrEmpty(signInSchemeNames) ? (Exception) new InvalidOperationException(str + string.Format("The registered sign-in schemes are: {0}.", (object) signInSchemeNames)) : (Exception) new InvalidOperationException(str + "Did you forget to call AddAuthentication().AddCookies(\"Cookies\") and SignInAsync(\"Cookies\",...)?");
} private async Task<string> GetAllSignOutSchemeNames()
{
return string.Join(", ", (await this.Schemes.GetAllSchemesAsync()).Where<AuthenticationScheme>((Func<AuthenticationScheme, bool>) (sch => typeof (IAuthenticationSignOutHandler).IsAssignableFrom(sch.HandlerType))).Select<AuthenticationScheme, string>((Func<AuthenticationScheme, string>) (sch => sch.Name)));
} private async Task<Exception> CreateMissingSignOutHandlerException(string scheme)
{
string signOutSchemeNames = await this.GetAllSignOutSchemeNames();
string str = string.Format(" Did you forget to call AddAuthentication().AddCookies(\"{0}\",...)?", (object) scheme);
return !string.IsNullOrEmpty(signOutSchemeNames) ? (Exception) new InvalidOperationException(string.Format("No sign-out authentication handler is registered for the scheme '{0}'. The registered sign-out schemes are: {1}.", (object) scheme, (object) signOutSchemeNames) + str) : (Exception) new InvalidOperationException("No sign-out authentication handlers are registered." + str);
} private async Task<Exception> CreateMismatchedSignOutHandlerException(
string scheme,
IAuthenticationHandler handler)
{
string signOutSchemeNames = await this.GetAllSignOutSchemeNames();
string str = string.Format("The authentication handler registered for scheme '{0}' is '{1}' which cannot be used for {2}. ", (object) scheme, (object) handler.GetType().Name, (object) "SignOutAsync");
return !string.IsNullOrEmpty(signOutSchemeNames) ? (Exception) new InvalidOperationException(str + string.Format("The registered sign-out schemes are: {0}.", (object) signOutSchemeNames)) : (Exception) new InvalidOperationException(str + string.Format("Did you forget to call AddAuthentication().AddCookies(\"Cookies\") and {0}(\"Cookies\",...)?", (object) "SignOutAsync"));
}
}

这个基本就是asp.net core 认证的整体概况了,先看一下整体的逻辑,在下一部分,我们再把一些相关的类型做一些补充说明。

可以看到AuthenticationService里面定义的这些AuthenticationAsync、ForbiddenAsync等等动作方法基本都遵循了一个逻辑:

①首先判断传入的scheme是否为null,如果为null,那么根据IAuthenticationSchemeProvider类型的Schemes属性来找出一个默认的scheme,如果上述两个都失败,则抛出异常。

②第一步成功的前提下,利用IAuthenticationHandlerProvider类型的Handlers属性根据找出的scheme来生成一个handler。

③在AuthenticationAsync、ForbiddenAsync和ChallengeAsync方法中,直接调用生成的handler的同名方法来执行最终的逻辑,而在SignInAsync和SignOutAsync中要判断拿到的这个Handler是否分别实现了IAuthenticationSignInHandler和IAuthenticationSignOutHandler这两个接口,如果没有实现,则爆出异常。

至于SignIn和SignOut这两个接口从IAuthenticatoinHandler中剥离出去是因为IAuthenticationHandler只是实现了对用户凭证的验证,至于SignIn和SignOut,现在大多数应用使用的登录方式都有所不同,况且这两个功能实现的是凭证的获取/发放,放在IAuthenticationHandler不合适。

public interface IAuthenticationSignInHandler : IAuthenticationSignOutHandler, IAuthenticationHandler
{
/// <summary>Handle sign in.</summary>
/// <param name="user">The <see cref="T:System.Security.Claims.ClaimsPrincipal" /> user.</param>
/// <param name="properties">The <see cref="T:Microsoft.AspNetCore.Authentication.AuthenticationProperties" /> that contains the extra meta-data arriving with the authentication.</param>
/// <returns>A task.</returns>
Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties properties);
}
public interface IAuthenticationSignOutHandler : IAuthenticationHandler
{
/// <summary>Signout behavior.</summary>
/// <param name="properties">The <see cref="T:Microsoft.AspNetCore.Authentication.AuthenticationProperties" /> that contains the extra meta-data arriving with the authentication.</param>
/// <returns>A task.</returns>
Task SignOutAsync(AuthenticationProperties properties);
}

关键类型

AuthenticationScheme

 public class AuthenticationScheme
{
/// <summary>Constructor.</summary>
/// <param name="name">The name for the authentication scheme.</param>
/// <param name="displayName">The display name for the authentication scheme.</param>
/// <param name="handlerType">The <see cref="T:Microsoft.AspNetCore.Authentication.IAuthenticationHandler" /> type that handles this scheme.</param>
public AuthenticationScheme(string name, string displayName, Type handlerType)
{
if (name == null)
throw new ArgumentNullException(nameof (name));
if (handlerType == (Type) null)
throw new ArgumentNullException(nameof (handlerType));
if (!typeof (IAuthenticationHandler).IsAssignableFrom(handlerType))
throw new ArgumentException("handlerType must implement IAuthenticationHandler.");
this.Name = name;
this.HandlerType = handlerType;
this.DisplayName = displayName;
} /// <summary>The name of the authentication scheme.</summary>
public string Name { get; } /// <summary>
/// The display name for the scheme. Null is valid and used for non user facing schemes.
/// </summary>
public string DisplayName { get; } /// <summary>
/// The <see cref="T:Microsoft.AspNetCore.Authentication.IAuthenticationHandler" /> type that handles this scheme.
/// </summary>
public Type HandlerType { get; }
}

一个AuthenticationScheme就是一个对IAuthenticationHandler的描述,它为一个特定的handlerType分配了一个名称(name)。它的构造函数接收三个参数了分别初始化了内部的三个属性,并对这三个参数做了严格的校验保证输入的参数有效。

AuthenticationSchemeBuilder

 public class AuthenticationSchemeBuilder
{
/// <summary>Constructor.</summary>
/// <param name="name">The name of the scheme being built.</param>
public AuthenticationSchemeBuilder(string name)
{
this.Name = name;
} /// <summary>The name of the scheme being built.</summary>
public string Name { get; } /// <summary>The display name for the scheme being built.</summary>
public string DisplayName { get; set; } /// <summary>
/// The <see cref="T:Microsoft.AspNetCore.Authentication.IAuthenticationHandler" /> type responsible for this scheme.
/// </summary>
public Type HandlerType { get; set; } /// <summary>
/// Builds the <see cref="T:Microsoft.AspNetCore.Authentication.AuthenticationScheme" /> instance.
/// </summary>
/// <returns></returns>
public AuthenticationScheme Build()
{
return new AuthenticationScheme(this.Name, this.DisplayName, this.HandlerType);
}
}

从名字就能看出来它是AuthenticationScheme的建造者,它内部包含的三个属性和AuthenticationScheme是一样的。它的构造函数需要一个string类型的name参数来初始化name,没有对输入的参数做任何校验,另外毫无意外的它有一个Build方法,用自身的这三个属性来new一个AuthenticationScheme。

AutenticaitonOptions

 public class AuthenticationOptions
{
private readonly IList<AuthenticationSchemeBuilder> _schemes = (IList<AuthenticationSchemeBuilder>) new List<AuthenticationSchemeBuilder>(); /// <summary>
/// Returns the schemes in the order they were added (important for request handling priority)
/// </summary>
public IEnumerable<AuthenticationSchemeBuilder> Schemes
{
get
{
return (IEnumerable<AuthenticationSchemeBuilder>) this._schemes;
}
} /// <summary>Maps schemes by name.</summary>
public IDictionary<string, AuthenticationSchemeBuilder> SchemeMap { get; } = (IDictionary<string, AuthenticationSchemeBuilder>) new Dictionary<string, AuthenticationSchemeBuilder>((IEqualityComparer<string>) StringComparer.Ordinal); /// <summary>
/// Adds an <see cref="T:Microsoft.AspNetCore.Authentication.AuthenticationScheme" />.
/// </summary>
/// <param name="name">The name of the scheme being added.</param>
/// <param name="configureBuilder">Configures the scheme.</param>
public void AddScheme(
string name,
Action<AuthenticationSchemeBuilder> configureBuilder)
{
if (name == null)
throw new ArgumentNullException(nameof (name));
if (configureBuilder == null)
throw new ArgumentNullException(nameof (configureBuilder));
if (this.SchemeMap.ContainsKey(name))
throw new InvalidOperationException("Scheme already exists: " + name);
AuthenticationSchemeBuilder authenticationSchemeBuilder = new AuthenticationSchemeBuilder(name);
configureBuilder(authenticationSchemeBuilder);
this._schemes.Add(authenticationSchemeBuilder);
this.SchemeMap[name] = authenticationSchemeBuilder;
} /// <summary>
/// Adds an <see cref="T:Microsoft.AspNetCore.Authentication.AuthenticationScheme" />.
/// </summary>
/// <typeparam name="THandler">The <see cref="T:Microsoft.AspNetCore.Authentication.IAuthenticationHandler" /> responsible for the scheme.</typeparam>
/// <param name="name">The name of the scheme being added.</param>
/// <param name="displayName">The display name for the scheme.</param>
public void AddScheme<THandler>(string name, string displayName) where THandler : IAuthenticationHandler
{
this.AddScheme(name, (Action<AuthenticationSchemeBuilder>) (b =>
{
b.DisplayName = displayName;
b.HandlerType = typeof (THandler);
}));
} /// <summary>
/// Used as the fallback default scheme for all the other defaults.
/// </summary>
public string DefaultScheme { get; set; } /// <summary>
/// Used as the default scheme by <see cref="M:Microsoft.AspNetCore.Authentication.IAuthenticationService.AuthenticateAsync(Microsoft.AspNetCore.Http.HttpContext,System.String)" />.
/// </summary>
public string DefaultAuthenticateScheme { get; set; } /// <summary>
/// Used as the default scheme by <see cref="M:Microsoft.AspNetCore.Authentication.IAuthenticationService.SignInAsync(Microsoft.AspNetCore.Http.HttpContext,System.String,System.Security.Claims.ClaimsPrincipal,Microsoft.AspNetCore.Authentication.AuthenticationProperties)" />.
/// </summary>
public string DefaultSignInScheme { get; set; } /// <summary>
/// Used as the default scheme by <see cref="M:Microsoft.AspNetCore.Authentication.IAuthenticationService.SignOutAsync(Microsoft.AspNetCore.Http.HttpContext,System.String,Microsoft.AspNetCore.Authentication.AuthenticationProperties)" />.
/// </summary>
public string DefaultSignOutScheme { get; set; } /// <summary>
/// Used as the default scheme by <see cref="M:Microsoft.AspNetCore.Authentication.IAuthenticationService.ChallengeAsync(Microsoft.AspNetCore.Http.HttpContext,System.String,Microsoft.AspNetCore.Authentication.AuthenticationProperties)" />.
/// </summary>
public string DefaultChallengeScheme { get; set; } /// <summary>
/// Used as the default scheme by <see cref="M:Microsoft.AspNetCore.Authentication.IAuthenticationService.ForbidAsync(Microsoft.AspNetCore.Http.HttpContext,System.String,Microsoft.AspNetCore.Authentication.AuthenticationProperties)" />.
/// </summary>
public string DefaultForbidScheme { get; set; }
}

这个类为添加Scheme提供了便利,它维护了一个AuthenticationSchemeBuilder的列表和一个同类型的字典。------------------------

IAuthenticationHandlerProvider

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

IAuthenticationHandler负责对用户的凭证进行校验,而这个Provider就是用来提供一个IAuthenticationHandler,它只有一个方法就是GetHandlerAsync(HttpContext context,string authenticationScheme)。从方法的参数传入也能知道IAuthenticationHnadlerProvider的实现依赖于AuthenticationScheme,确实,它的实现AuthenticationHandlerProvider里面确实维护了一个IAuthenticationSchemeProvider:

 public class AuthenticationHandlerProvider : IAuthenticationHandlerProvider
{
private Dictionary<string, IAuthenticationHandler> _handlerMap = new Dictionary<string, IAuthenticationHandler>((IEqualityComparer<string>) StringComparer.Ordinal); /// <summary>Constructor.</summary>
/// <param name="schemes">The <see cref="T:Microsoft.AspNetCore.Authentication.IAuthenticationHandlerProvider" />.</param>
public AuthenticationHandlerProvider(IAuthenticationSchemeProvider schemes)
{
this.Schemes = schemes;
} /// <summary>
/// The <see cref="T:Microsoft.AspNetCore.Authentication.IAuthenticationHandlerProvider" />.
/// </summary>
public IAuthenticationSchemeProvider Schemes { get; } /// <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>
public async Task<IAuthenticationHandler> GetHandlerAsync(
HttpContext context,
string authenticationScheme)
{
if (this._handlerMap.ContainsKey(authenticationScheme))
return this._handlerMap[authenticationScheme];
AuthenticationScheme schemeAsync = await this.Schemes.GetSchemeAsync(authenticationScheme);
if (schemeAsync == null)
return (IAuthenticationHandler) null;
if ((context.RequestServices.GetService(schemeAsync.HandlerType) ?? ActivatorUtilities.CreateInstance(context.RequestServices, schemeAsync.HandlerType)) is IAuthenticationHandler handler)
{
await handler.InitializeAsync(schemeAsync, context);
this._handlerMap[authenticationScheme] = handler;
}
return handler;
}
}

IAuthenticationHandlerProvider的目的很明确,就是要提供一个IAuthenticationHandler,它的逻辑如下:

①首先根据authenticationScheme查看_handlerMap是否包含相应的handler如果包含直接返回这个handler。

②如果没有从第一步中找到,那么就从Schemes属性中根据authenticationScheme找出相应的AuthenticationScheme,如果这个Scheme为空,那么直接返回一个null。

③如果从Schemes属性中找出的AuthenticationScheme不为空,那么或者从传入的HttpContext类型的context.RequestServices.GetService方法中找Handler,或者使用ActivatorUtilities来创建一个,不管使用那种方式都是从拿到的scheme中记录的handlerType来获取handler的。如果是一个IAuthenticationHandler,那么调用handler. InitializeAsync方法(注一)来初始化本身,并将这个handler放入自身的_handlerMap字典中。最后将这个handler返回。

AuthenticateResult

 public class AuthenticateResult
{
protected AuthenticateResult()
{
} /// <summary>
/// If a ticket was produced, authenticate was successful.
/// </summary>
public bool Succeeded
{
get
{
return this.Ticket != null;
}
} /// <summary>The authentication ticket.</summary>
public AuthenticationTicket Ticket { get; protected set; } /// <summary>
/// Gets the claims-principal with authenticated user identities.
/// </summary>
public ClaimsPrincipal Principal
{
get
{
return this.Ticket?.Principal;
}
} /// <summary>
/// Additional state values for the authentication session.
/// </summary>
public AuthenticationProperties Properties { get; protected set; } /// <summary>Holds failure information from the authentication.</summary>
public Exception Failure { get; protected set; } /// <summary>
/// Indicates that there was no information returned for this authentication scheme.
/// </summary>
public bool None { get; protected set; } /// <summary>Indicates that authentication was successful.</summary>
/// <param name="ticket">The ticket representing the authentication result.</param>
/// <returns>The result.</returns>
public static AuthenticateResult Success(AuthenticationTicket ticket)
{
if (ticket == null)
throw new ArgumentNullException(nameof (ticket));
return new AuthenticateResult()
{
Ticket = ticket,
Properties = ticket.Properties
};
} /// <summary>
/// Indicates that there was no information returned for this authentication scheme.
/// </summary>
/// <returns>The result.</returns>
public static AuthenticateResult NoResult()
{
return new AuthenticateResult() { None = true };
} /// <summary>
/// Indicates that there was a failure during authentication.
/// </summary>
/// <param name="failure">The failure exception.</param>
/// <returns>The result.</returns>
public static AuthenticateResult Fail(Exception failure)
{
return new AuthenticateResult() { Failure = failure };
} /// <summary>
/// Indicates that there was a failure during authentication.
/// </summary>
/// <param name="failure">The failure exception.</param>
/// <param name="properties">Additional state values for the authentication session.</param>
/// <returns>The result.</returns>
public static AuthenticateResult Fail(
Exception failure,
AuthenticationProperties properties)
{
return new AuthenticateResult()
{
Failure = failure,
Properties = properties
};
} /// <summary>
/// Indicates that there was a failure during authentication.
/// </summary>
/// <param name="failureMessage">The failure message.</param>
/// <returns>The result.</returns>
public static AuthenticateResult Fail(string failureMessage)
{
return AuthenticateResult.Fail(new Exception(failureMessage));
} /// <summary>
/// Indicates that there was a failure during authentication.
/// </summary>
/// <param name="failureMessage">The failure message.</param>
/// <param name="properties">Additional state values for the authentication session.</param>
/// <returns>The result.</returns>
public static AuthenticateResult Fail(
string failureMessage,
AuthenticationProperties properties)
{
return AuthenticateResult.Fail(new Exception(failureMessage), properties);
}
}

一个认证结果,表示AuthenticationService中的方法返回的结果。典型的状态模式。它内部维护一个AuthenticationTicket。可直接调用Success方法或这Fail方法来返回包含相应状态的Result。

AuthenticatoinTicket

 public class AuthenticationTicket
{
/// <summary>
/// Initializes a new instance of the <see cref="T:Microsoft.AspNetCore.Authentication.AuthenticationTicket" /> class
/// </summary>
/// <param name="principal">the <see cref="T:System.Security.Claims.ClaimsPrincipal" /> that represents the authenticated user.</param>
/// <param name="properties">additional properties that can be consumed by the user or runtime.</param>
/// <param name="authenticationScheme">the authentication middleware that was responsible for this ticket.</param>
public AuthenticationTicket(
ClaimsPrincipal principal,
AuthenticationProperties properties,
string authenticationScheme)
{
if (principal == null)
throw new ArgumentNullException(nameof (principal));
this.AuthenticationScheme = authenticationScheme;
this.Principal = principal;
this.Properties = properties ?? new AuthenticationProperties();
} /// <summary>
/// Initializes a new instance of the <see cref="T:Microsoft.AspNetCore.Authentication.AuthenticationTicket" /> class
/// </summary>
/// <param name="principal">the <see cref="T:System.Security.Claims.ClaimsPrincipal" /> that represents the authenticated user.</param>
/// <param name="authenticationScheme">the authentication middleware that was responsible for this ticket.</param>
public AuthenticationTicket(ClaimsPrincipal principal, string authenticationScheme)
: this(principal, (AuthenticationProperties) null, authenticationScheme)
{
} /// <summary>Gets the authentication type.</summary>
public string AuthenticationScheme { get; private set; } /// <summary>
/// Gets the claims-principal with authenticated user identities.
/// </summary>
public ClaimsPrincipal Principal { get; private set; } /// <summary>
/// Additional state values for the authentication session.
/// </summary>
public AuthenticationProperties Properties { get; private set; }
}

表示一个经过认证后颁发的证书,里面包含ClaimsPrincipal、AuthenticationScheme和AuthenticationProperties三个属性,ClaimsPrincipal表示证书的主体,如姓名、电话等等信息,AuthenticationProperties表示证书的相关信息,如颁发时间、过期时间和重定向地址等等。

AuthenticationProperties

  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"; /// <summary>
/// Initializes a new instance of the <see cref="T:Microsoft.AspNetCore.Authentication.AuthenticationProperties" /> class.
/// </summary>
public AuthenticationProperties()
: this((IDictionary<string, string>) null, (IDictionary<string, object>) null)
{
} /// <summary>
/// Initializes a new instance of the <see cref="T:Microsoft.AspNetCore.Authentication.AuthenticationProperties" /> class.
/// </summary>
/// <param name="items">State values dictionary to use.</param>
public AuthenticationProperties(IDictionary<string, string> items)
: this(items, (IDictionary<string, object>) null)
{
} /// <summary>
/// Initializes a new instance of the <see cref="T:Microsoft.AspNetCore.Authentication.AuthenticationProperties" /> class.
/// </summary>
/// <param name="items">State values dictionary to use.</param>
/// <param name="parameters">Parameters dictionary to use.</param>
public AuthenticationProperties(
IDictionary<string, string> items,
IDictionary<string, object> parameters)
{
this.Items = items ?? (IDictionary<string, string>) new Dictionary<string, string>((IEqualityComparer<string>) StringComparer.Ordinal);
this.Parameters = parameters ?? (IDictionary<string, object>) new Dictionary<string, object>((IEqualityComparer<string>) StringComparer.Ordinal);
} /// <summary>State values about the authentication session.</summary>
public IDictionary<string, string> Items { get; } /// <summary>
/// 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.
/// </summary>
public IDictionary<string, object> Parameters { get; } /// <summary>
/// Gets or sets whether the authentication session is persisted across multiple requests.
/// </summary>
public bool IsPersistent
{
get
{
return this.GetString(".persistent") != null;
}
set
{
this.SetString(".persistent", value ? string.Empty : (string) null);
}
} /// <summary>
/// Gets or sets the full path or absolute URI to be used as an http redirect response value.
/// </summary>
public string RedirectUri
{
get
{
return this.GetString(".redirect");
}
set
{
this.SetString(".redirect", value);
}
} /// <summary>
/// Gets or sets the time at which the authentication ticket was issued.
/// </summary>
public DateTimeOffset? IssuedUtc
{
get
{
return this.GetDateTimeOffset(".issued");
}
set
{
this.SetDateTimeOffset(".issued", value);
}
} /// <summary>
/// Gets or sets the time at which the authentication ticket expires.
/// </summary>
public DateTimeOffset? ExpiresUtc
{
get
{
return this.GetDateTimeOffset(".expires");
}
set
{
this.SetDateTimeOffset(".expires", value);
}
} /// <summary>
/// Gets or sets if refreshing the authentication session should be allowed.
/// </summary>
public bool? AllowRefresh
{
get
{
return this.GetBool(".refresh");
}
set
{
this.SetBool(".refresh", value);
}
} /// <summary>
/// Get a string value from the <see cref="P:Microsoft.AspNetCore.Authentication.AuthenticationProperties.Items" /> collection.
/// </summary>
/// <param name="key">Property key.</param>
/// <returns>Retrieved value or <c>null</c> if the property is not set.</returns>
public string GetString(string key)
{
string str;
return !this.Items.TryGetValue(key, out str) ? (string) null : str;
} /// <summary>
/// Set a string value in the <see cref="P:Microsoft.AspNetCore.Authentication.AuthenticationProperties.Items" /> collection.
/// </summary>
/// <param name="key">Property key.</param>
/// <param name="value">Value to set or <c>null</c> to remove the property.</param>
public void SetString(string key, string value)
{
if (value != null)
{
this.Items[key] = value;
}
else
{
if (!this.Items.ContainsKey(key))
return;
this.Items.Remove(key);
}
} /// <summary>
/// Get a parameter from the <see cref="P:Microsoft.AspNetCore.Authentication.AuthenticationProperties.Parameters" /> collection.
/// </summary>
/// <typeparam name="T">Parameter type.</typeparam>
/// <param name="key">Parameter key.</param>
/// <returns>Retrieved value or the default value if the property is not set.</returns>
public T GetParameter<T>(string key)
{
object obj1;
return this.Parameters.TryGetValue(key, out obj1) && obj1 is T obj2 ? obj2 : default (T);
} /// <summary>
/// Set a parameter value in the <see cref="P:Microsoft.AspNetCore.Authentication.AuthenticationProperties.Parameters" /> collection.
/// </summary>
/// <typeparam name="T">Parameter type.</typeparam>
/// <param name="key">Parameter key.</param>
/// <param name="value">Value to set.</param>
public void SetParameter<T>(string key, T value)
{
this.Parameters[key] = (object) value;
} /// <summary>
/// Get a bool value from the <see cref="P:Microsoft.AspNetCore.Authentication.AuthenticationProperties.Items" /> collection.
/// </summary>
/// <param name="key">Property key.</param>
/// <returns>Retrieved value or <c>null</c> if the property is not set.</returns>
protected bool? GetBool(string key)
{
string str;
bool result;
return this.Items.TryGetValue(key, out str) && bool.TryParse(str, out result) ? new bool?(result) : new bool?();
} /// <summary>
/// Set a bool value in the <see cref="P:Microsoft.AspNetCore.Authentication.AuthenticationProperties.Items" /> collection.
/// </summary>
/// <param name="key">Property key.</param>
/// <param name="value">Value to set or <c>null</c> to remove the property.</param>
protected void SetBool(string key, bool? value)
{
if (value.HasValue)
{
this.Items[key] = value.Value.ToString();
}
else
{
if (!this.Items.ContainsKey(key))
return;
this.Items.Remove(key);
}
} /// <summary>
/// Get a DateTimeOffset value from the <see cref="P:Microsoft.AspNetCore.Authentication.AuthenticationProperties.Items" /> collection.
/// </summary>
/// <param name="key">Property key.</param>
/// <returns>Retrieved value or <c>null</c> if the property is not set.</returns>
protected DateTimeOffset? GetDateTimeOffset(string key)
{
string input;
DateTimeOffset result;
return this.Items.TryGetValue(key, out input) && DateTimeOffset.TryParseExact(input, "r", (IFormatProvider) CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out result) ? new DateTimeOffset?(result) : new DateTimeOffset?();
} /// <summary>
/// Set a DateTimeOffset value in the <see cref="P:Microsoft.AspNetCore.Authentication.AuthenticationProperties.Items" /> collection.
/// </summary>
/// <param name="key">Property key.</param>
/// <param name="value">Value to set or <c>null</c> to remove the property.</param>
protected void SetDateTimeOffset(string key, DateTimeOffset? value)
{
if (value.HasValue)
{
this.Items[key] = value.Value.ToString("r", (IFormatProvider) CultureInfo.InvariantCulture);
}
else
{
if (!this.Items.ContainsKey(key))
return;
this.Items.Remove(key);
}
}
}

IClaimsTransformation

  public interface IClaimsTransformation
{
/// <summary>
/// Provides a central transformation point to change the specified principal.
/// Note: this will be run on each AuthenticateAsync call, so its safer to
/// return a new ClaimsPrincipal if your transformation is not idempotent.
/// </summary>
/// <param name="principal">The <see cref="T:System.Security.Claims.ClaimsPrincipal" /> to transform.</param>
/// <returns>The transformed principal.</returns>
Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal);
}

这个接口可以为传入的principal提供转换,适合全局为principal添加一些额外的claim。

asp.net core 2.1认证的更多相关文章

  1. Asp.Net Core基于JWT认证的数据接口网关Demo

    近日,应一位朋友的邀请写了个Asp.Net Core基于JWT认证的数据接口网关Demo.朋友自己开了个公司,接到的一个升级项目,客户要求用Aps.Net Core做数据网关服务且基于JWT认证实现对 ...

  2. ASP.NET Core的身份认证框架IdentityServer4--入门

    ASP.NET Core的身份认证框架IdentityServer4--入门 2018年08月11日 10:09:00 qq_42606051 阅读数 4002   https://blog.csdn ...

  3. ASP.NET Core的身份认证框架IdentityServer4(7)- 使用客户端证书控制API访问

    前言 今天(2017-9-8,写于9.8,今天才发布)一口气连续把最后几篇IdentityServer4相关理论全部翻译完了,终于可以进入写代码的过程了,比较累.目前官方的文档和Demo以及一些相关组 ...

  4. ASP.NET Core的身份认证框架IdentityServer4(9)-使用OpenID Connect添加用户认证

    OpenID Connect OpenID Connect 1.0是OAuth 2.0协议之上的一个简单的身份层. 它允许客户端基于授权服务器执行的身份验证来验证最终用户的身份,以及以可互操作和类似R ...

  5. ASP.NET Core的身份认证框架IdentityServer4--(4)添加第三方快捷登录

    添加对外部认证的支持 接下来我们将添加对外部认证的支持.这非常简单,因为你真正需要的是一个兼容ASP.NET Core的认证处理程序. ASP.NET Core本身也支持Google,Facebook ...

  6. ASP.NET Core的身份认证框架IdentityServer4--(2)API跟WEB端配置

    API配置 可以使用ASP.NET Core Web API模板.同样,我们建议您控制端口并使用与之前一样的方法来配置Kestrel和启动配置文件.端口配置为http://localhost:5001 ...

  7. ASP.NET Core的身份认证框架IdentityServer4--(1)服务配置

    官网文档地址:点我点我 准备 创建一个名为IdentityServer的ASP.NET Core Web Api 项目,端口5000 创建一个名为Api的ASP.NET Core Web Api 项目 ...

  8. ASP.NET Core 实现带认证功能的Web代理服务器

    引言 最近在公司开发了一个项目,项目部署架构图如下: 思路 如图中文本所述,公司大数据集群不允许直接访问外网,需要一个网关服务器代理请求,本处服务器A就是边缘代理服务器的作用. 通常技术人员最快捷的思 ...

  9. asp.net core 登录身份认证(Cookie)

    asp.net core 2最简单的登录功能 源代码在此 创建asp.net core Web Mvc项目 配置下选项 项目目录结构 在Models文件夹下新建两个实体类 public class T ...

随机推荐

  1. 第一个Web应用

    这篇文章演示如何使用ASP.NET Core创建第一个web api服务. 开始 新建一个Project. 选择模板'ASP.NET Core Web应用程序',并且输入解决方案名称和项目名称. 然后 ...

  2. 学习笔记——二叉树相关算法的实现(Java语言版)

    二叉树遍历概念和算法 遍历(Traverse): 所谓遍历(Traversal)是指沿着某条搜索路线,依次对树中每个结点均做一次且仅做一次访问. 从二叉树的递归定义可知,一棵非空的二叉树由根结点及左. ...

  3. 外媒评李开复的《AI·未来》:四大浪潮正在席卷全球

    外媒评李开复的<AI·未来>:四大浪潮正在席卷全球 https://mp.weixin.qq.com/s/oElub0QOYjOROhqN3ULUkg [网易智能讯 9月17日消息]李开复 ...

  4. AEAI HR开源人力资源管理v1.6.0发版公告

    1 升级说明 AEAI HR v1.6.0版是AEAI HR v1.5.2版人力资源管理系统的升级版本,本次升级的系统是基于AEAI DP 3.8.0_20170228进行打包部署的,升级内容主要是针 ...

  5. 简易仿ios菊花加载loading图

    原文链接:https://mp.weixin.qq.com/s/wBbQgOfr59wntNK9ZJ5iRw 项目中经常会用到加载数据的loading显示图,除了设计根据app自身设计的动画loadi ...

  6. Java 图片爬虫,java打包jar文件

    目录 1. Java 图片爬虫,制作 .jar 文件 spider.java 制作 jar 文件 添加执行权限 1. Java 图片爬虫,制作 .jar 文件 spider.java spider.j ...

  7. Nagle 算法

    1. Nagel算法        TCP/IP协议中,无论发送多少数据,总是要在数据前面加上协议头,同时,对方接收到数据,也需要发送ACK表示确认.为了尽可能的利用网络带宽,TCP总是希望尽可能的发 ...

  8. C 存储类

    存储类定义 C 程序中变量/函数的范围(可见性)和生命周期.这些说明符放置在它们所修饰的类型之前.下面列出 C 程序中可用的存储类: auto.register.static.extern auto ...

  9. JavaScript -- JSON.parse 函数 和 JSON.stringify 函数

    JavaScript -- JSON.parse 函数 和 JSON.stringify 函数 1. JSON.parse 函数: 使用 JSON.parse 可将 JSON 字符串转换成对象. &l ...

  10. SQLServer之创建唯一非聚集索引

    创建唯一非聚集索引典型实现 唯一索引可通过以下方式实现: PRIMARY KEY 或 UNIQUE 约束 在创建 PRIMARY KEY 约束时,如果不存在该表的聚集索引且未指定唯一非聚集索引,则将自 ...