ASP.NET Core 认证与授权[4]:JwtBearer认证

Bearer认证

HTTP提供了一套标准的身份验证框架:服务器可以用来针对客户端的请求发送质询(challenge),客户端根据质询提供身份验证凭证。质询与应答的工作流程如下:服务器端向客户端返回401(Unauthorized,未授权)状态码,并在WWW-Authenticate头中添加如何进行验证的信息,其中至少包含有一种质询方式。然后客户端可以在请求中添加Authorization头进行验证,其Value为身份验证的凭证信息。

在HTTP标准验证方案中,我们比较熟悉的是"Basic"和"Digest",前者将用户名密码使用BASE64编码后作为验证凭证,后者是Basic的升级版,更加安全,因为Basic是明文传输密码信息,而Digest是加密后传输。在前文介绍的Cookie认证属于Form认证,并不属于HTTP标准验证。

本文要介绍的Bearer验证也属于HTTP协议标准验证,它随着OAuth协议而开始流行,详细定义见: RFC 6570。

A security token with the property that any party in possession of the token (a "bearer") can use the token in any way that any other party in possession of it can. Using a bearer token does not require a bearer to prove possession of cryptographic key material (proof-of-possession).

Bearer验证中的凭证称为BEARER_TOKEN,或者是access_token,它的颁发和验证完全由我们自己的应用程序来控制,而不依赖于系统和Web服务器,Bearer验证的标准请求方式如下:

Authorization: Bearer [BEARER_TOKEN] 

那么使用Bearer验证有什么好处呢?

  • CORS: cookies + CORS 并不能跨不同的域名。而Bearer验证在任何域名下都可以使用HTTP header头部来传输用户信息。

  • 对移动端友好: 当你在一个原生平台(iOS, Android, WindowsPhone等)时,使用Cookie验证并不是一个好主意,因为你得和Cookie容器打交道,而使用Bearer验证则简单的多。

  • CSRF: 因为Bearer验证不再依赖于cookies, 也就避免了跨站请求攻击。

  • 标准:在Cookie认证中,用户未登录时,返回一个302到登录页面,这在非浏览器情况下很难处理,而Bearer验证则返回的是标准的401 challenge

JWT(JSON WEB TOKEN)

上面介绍的Bearer认证,其核心便是BEARER_TOKEN,而最流行的Token编码方式便是:JSON WEB TOKEN。

Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准(RFC 7519)。该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。

JWT是由.分割的如下三部分组成:

头部(Header)

Header 一般由两个部分组成:

  • alg

  • typ

alg是是所使用的hash算法,如:HMAC SHA256或RSA,typ是Token的类型,在这里就是:JWT

{"alg": "HS256","typ": "JWT"}

然后使用Base64Url编码成第一部分:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.<second part>.<third part>

载荷(Payload)

这一部分是JWT主要的信息存储部分,其中包含了许多种的声明(claims)。

Claims的实体一般包含用户和一些元数据,这些claims分成三种类型:

  • reserved claims:预定义的 一些声明,并不是强制的但是推荐,它们包括 iss (issuer), exp (expiration time), sub (subject),aud(audience) 等(这里都使用三个字母的原因是保证 JWT 的紧凑)。

  • public claims: 公有声明,这个部分可以随便定义,但是要注意和 IANA JSON Web Token 冲突。

  • private claims: 私有声明,这个部分是共享被认定信息中自定义部分。

一个简单的Pyload可以是这样子的:

{"sub": "1234567890","name": "John Doe","admin": true}

这部分同样使用Base64Url编码成第二部分:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.<third part>

签名(Signature)

Signature是用来验证发送者的JWT的同时也能确保在期间不被篡改。

在创建该部分时候你应该已经有了编码后的Header和Payload,然后使用保存在服务端的秘钥对其签名,一个完整的JWT如下:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

因此使用JWT具有如下好处:

  • 通用:因为json的通用性,所以JWT是可以进行跨语言支持的,像JAVA,JavaScript,NodeJS,PHP等很多语言都可以使用。

  • 紧凑:JWT的构成非常简单,字节占用很小,可以通过 GET、POST 等放在 HTTP 的 header 中,非常便于传输。

  • 扩展:JWT是自我包涵的,包含了必要的所有信息,不需要在服务端保存会话信息, 非常易于应用的扩展。

关于更多JWT的介绍,网上非常多,这里就不再多做介绍。下面,演示一下 ASP.NET Core 中 JwtBearer 认证的使用方式。

示例

模拟Token

ASP.NET Core 内置的JwtBearer验证,并不包含Token的发放,我们先模拟一个简单的实现:

[HttpPost("authenticate")]
public IActionResult Authenticate([FromBody]UserDto userDto){
   var user = _store.FindUser(userDto.UserName, userDto.Password);  
     if (user == null)
     return Unauthorized();
     var tokenHandler = new JwtSecurityTokenHandler();
     var key = Encoding.ASCII.GetBytes(Consts.Secret);  
     var authTime = DateTime.UtcNow;  
     var expiresAt = authTime.AddDays(7);
     var tokenDescriptor = new SecurityTokenDescriptor{Subject = new ClaimsIdentity(new Claim[]{            new Claim(JwtClaimTypes.Audience,"api"),            new Claim(JwtClaimTypes.Issuer,"http://localhost:5200"),            new Claim(JwtClaimTypes.Id, user.Id.ToString()),            new Claim(JwtClaimTypes.Name, user.Name),            new Claim(JwtClaimTypes.Email, user.Email),            new Claim(JwtClaimTypes.PhoneNumber, user.PhoneNumber)}),Expires = expiresAt,SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)};  
      var token = tokenHandler.CreateToken(tokenDescriptor);    
      var tokenString = tokenHandler.WriteToken(token);  
     return Ok(new{access_token = tokenString,token_type = "Bearer",profile = new{sid = user.Id,name = user.Name,auth_time = new DateTimeOffset(authTime).ToUnixTimeSeconds(),expires_at = new DateTimeOffset(expiresAt).ToUnixTimeSeconds()}}); }

如上,使用微软提供的Microsoft.IdentityModel.Tokens帮助类(源码地址:azure-activedirectory-identitymodel-extensions-for-dotnet),可以很容易的创建出JwtToen,就不再多说。

注册JwtBearer认证

首先添加JwtBearer包引用:

dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer --version 2.0.0

然后在Startup类中添加如下配置:

public void ConfigureServices(IServiceCollection services){services.AddAuthentication(x =>{x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;}).AddJwtBearer(o =>{o.TokenValidationParameters = new TokenValidationParameters{NameClaimType = JwtClaimTypes.Name,RoleClaimType = JwtClaimTypes.Role,            // 将下面两个参数设置为false,可以不验证Issuer和Audience,但是不建议这样做。//ValidateIssuer = false, // 默认为true//ValidateAudience = false, // 默认为true      ValidIssuer = "http://localhost:5200",ValidAudience = "api",IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Consts.Secret))};});
}public void Configure(IApplicationBuilder app){app.UseAuthentication();
}

JwtBearerOptions的配置中,通常IssuerSigningKey(签名秘钥)ValidIssuer(Token颁发机构)ValidAudience(颁发给谁) 三个参数是必须的,后两者用于与TokenClaims中的IssuerAudience进行对比,不一致则验证失败(与上面发放Token中的Claims对应)。

NameClaimTypeRoleClaimType需与Token中的ClaimType一致,在IdentityServer中也是使用的JwtClaimTypes,否则会造成User.Identity.Name为空等问题。

添加受保护资源

创建一个需要授权的控制器,直接使用Authorize即可:

[Authorize]
[Route("api/[controller]")]
public class SampleDataController : Controller{[HttpGet("[action]")]  
 public IEnumerable<WeatherForecast> WeatherForecasts()    {    
     return ...} }

运行

最后运行,直接访问/api/SampleData/WeatherForecasts,将返回一个401:

HTTP/1.1 401 Unauthorized
Server: Kestrel
Content-Length: 0
WWW-Authenticate: Bearer

让我们调用api/oauth/authenticate,获取一个JWT:

请求:
POST http://localhost:5200/api/oauth/authenticate HTTP/1.1
content-type: application/json{"username": "alice","password": "alice"
}响应:
HTTP/1.1 200 OK
{"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjEiLCJuYW1lIjoiYWxpY2UiLCJlbWFpbCI6ImFsaWNlQGdtYWlsLmNvbSIsInBob25lX251bWJlciI6IjE4ODAwMDAwMDAxIiwibmJmIjoxNTA5NDY0MzQwLCJleHAiOjE1MTAwNjkxNDAsImlhdCI6MTUwOTQ2NDM0MH0.Y1TDz8KjLRh_vjQ_3iYP4oJw-fmhoboiAGPqIZ-ooNc","token_type":"Bearer","profile":{"sid":1,"name":"alice","auth_time":1509464340,"expires_at":1510069140}}

最后使用该Token,再次调用受保护资源:

GET http://localhost:5200/api/SampleData/WeatherForecasts HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjEiLCJuYW1lIjoiYWxpY2UiLCJlbWFpbCI6ImFsaWNlQGdtYWlsLmNvbSIsInBob25lX251bWJlciI6IjE4ODAwMDAwMDAxIiwibmJmIjoxNTA5NDY0MzQwLCJleHAiOjE1MTAwNjkxNDAsImlhdCI6MTUwOTQ2NDM0MH0.Y1TDz8KjLRh_vjQ_3iYP4oJw-fmhoboiAGPqIZ-ooNc

授权成功,返回了预期的数据:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8[{"dateFormatted":"2017/11/3","temperatureC":35,"summary":"Chilly","temperatureF":94}]

扩展

自定义Token获取方式

JwtBearer认证中,默认是通过Http的Authorization头来获取的,这也是最推荐的做法,但是在某些场景下,我们可能会使用Url或者是Cookie来传递Token,那要怎么来实现呢?

其实实现起来非常简单,如前几章介绍的一样,JwtBearer也在认证的各个阶段为我们提供了事件,来执行我们的自定义逻辑:

.AddJwtBearer(o =>
{o.Events = new JwtBearerEvents(){OnMessageReceived = context =>{context.Token = context.Request.Query["access_token"];            return Task.CompletedTask;}};o.TokenValidationParameters = new TokenValidationParameters{...};

然后在Url中添加access_token=[token],直接在浏览器中访问:

同样的,我们也可以很容易的在Cookie中读取Token,就不再演示。

除了OnMessageReceived外,还提供了如下几个事件:

  • TokenValidated:在Token验证通过后调用。

  • AuthenticationFailed: 认证失败时调用。

  • Challenge: 未授权时调用。

使用OIDC服务

在上面的示例中,我们简单模拟的Token颁发,功能非常简单,并不适合在生产环境中使用,可是微软也没有提供OIDC服务的实现,好在.NET社区中提供了几种实现,可供我们选择:

Name Description
AspNet.Security.OpenIdConnect.Server (ASOS) Low-level/protocol-first OpenID Connect server framework for ASP.NET Core and OWIN/Katana
IdentityServer4 OpenID Connect and OAuth 2.0 framework for ASP.NET Core - officially certified by the OpenID Foundation and under governance of the .NET Foundation
OpenIddict Easy-to-use OpenID Connect server for ASP.NET Core
PwdLess Simple, stateless, passwordless authentication for ASP.NET Core

我们在这里使用IdentityServer4来搭建一个OIDC服务器,并添加如下配置:

/********************OIDC服务器代码片段********************/public void ConfigureServices(IServiceCollection services){services.AddMvc();    // 配置IdentitryServerservices.AddIdentityServer().AddInMemoryPersistedGrants().AddInMemoryApiResources(Config.GetApis()).AddInMemoryIdentityResources(Config.GetIdentityResources()).AddInMemoryClients(Config.GetClients()).AddTestUsers(Config.GetUsers()).AddDeveloperSigningCredential();
}new Client
{ClientId = "jwt.implicit",ClientName = "Implicit Client (Web)",AllowedGrantTypes = GrantTypes.Implicit,AllowAccessTokensViaBrowser = true,RedirectUris = { "http://localhost:5200/callback" },PostLogoutRedirectUris = { "http://localhost:5200/home" },AllowedCorsOrigins = { "http://localhost:5200" },AllowedScopes = { "openid", "profile", "email", "api" },
}

而JwtBearer客户端的配置就更加简单了,因为OIDC具有配置发现的功能:

public void ConfigureServices(IServiceCollection services){services.AddAuthentication(x =>{x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;}).AddJwtBearer(o =>{o.Authority = "https://oidc.faasx.com/";o.Audience = "api";o.TokenValidationParameters = new TokenValidationParameters{NameClaimType = JwtClaimTypes.Name,RoleClaimType = JwtClaimTypes.Role,};});
}

如上,最重要的是Authority参数,用来表示OIDC服务的地址,然后便可以自动发现IssuerIssuerSigningKey等配置,而o.Audienceo.TokenValidationParameters = new TokenValidationParameters { ValidAudience = "api" }是等效的,后面分析源码时会介绍。

OIDC兼容OAuth2协议,我们可以使用上一章介绍的授权码模式来获取Token,也可以直接用户名密码模式来获取Token:

请求:
POST https://oidc.faasx.com/connect/token HTTP/1.1
Content-Type: application/x-www-form-urlencodedclient_id=client.rop&client_secret=secret&grant_type=password&scope=api&username=alice&password=alice响应:
HTTP/1.1 200 OK
Content-Type: application/json{"access_token":"eyJhbGciOiJSUzI1NiIsImtpZCI6IjdlYzk5MjVlMmUzMTA2NmY2ZmU2ODgzMDRhZjU1ZmM0IiwidHlwIjoiSldUIn0.eyJuYmYiOjE1MDk2NzI1NjksImV4cCI6MTUwOTY3NjE2OSwiaXNzIjoiaHR0cHM6Ly9vaWRjLmZhYXN4LmNvbSIsImF1ZCI6WyJodHRwczovL29pZGMuZmFhc3guY29tL3Jlc291cmNlcyIsImFwaSJdLCJjbGllbnRfaWQiOiJjbGllbnQucm9wIiwic3ViIjoiMDAxIiwiYXV0aF90aW1lIjoxNTA5NjcyNTY5LCJpZHAiOiJsb2NhbCIsIm5hbWUiOiJBbGljZSBTbWl0aCIsImVtYWlsIjoiQWxpY2VTbWl0aEBlbWFpbC5jb20iLCJzY29wZSI6WyJhcGkiXSwiYW1yIjpbInB3ZCJdfQ.PM93LThOZA3lkgPFVwieqGQQQtgmYDCY0oSFVmudv1hpKO6UaaZsmnn4ci9QjbGl5g2433JkDks5UIZsZ0xE62Qqq8PicPBBuaNoYrCf6dxR7j-0uZcoa7-FCKGu-0TrM8OL-NuMvN6_KEpbWa3jlkwibCK9YDIwJZilVoWUOrbbIEsKTa-DdLScmzHLUzksT8GBr0PAVhge9PRFiGqg8cgMLjsA62ZeDsR35f55BucSV5Pj0SAj26anYvrBNTHKOF7ze1DGW51Dbz6DRu1X7uEIxSzWiNi4cRVJ6Totjkwk5F78R9R38o_mYEdehZBjRHFe6zLd91hXcCKqOEh5eQ","expires_in":3600,"token_type":"Bearer"}

我在本章的示例代码中,使用前端Angular框架演示了如何从本地登录获取Tokek或使用简化模式(implicit)从OIDC服务器获取Token,然后保存到sesstionStorage,在发送请求时附加到请求头中的示例,可供大家参考:JwtBearerSample。

源码探索

JwtBearerPostConfigureOptions

在ASP.NET Core 2.0 Options框架中,新增了一种PostConfigure模式,用来在我们所注册的Options配置执行完之后,再对Options做一些修改。

JwtBearerPostConfigureOptions用来实现配置发现:

public class JwtBearerPostConfigureOptions : IPostConfigureOptions<JwtBearerOptions>
{    public void PostConfigure(string name, JwtBearerOptions options)    {        // 如果未设置options.TokenValidationParameters.ValidAudience,则使用options.Audienceif (string.IsNullOrEmpty(options.TokenValidationParameters.ValidAudience) && !string.IsNullOrEmpty(options.Audience)){options.TokenValidationParameters.ValidAudience = options.Audience;}        if (options.ConfigurationManager == null){            // 如果未设置MetadataAddress,则使用options.Authority+.well-known/openid-configuration....options.ConfigurationManager = new ConfigurationManager<OpenIdConnectConfiguration>(options.MetadataAddress, new OpenIdConnectConfigurationRetriever(), new HttpDocumentRetriever(httpClient) { RequireHttps = options.RequireHttpsMetadata });}}}
}

JwtBearerHandler

JwtBearerHandler相对于前几章介绍的CookieHandler, OpenIdConnectHandler等,都简单的多。

首先便是从请求中获取Token:

protected override async Task<AuthenticateResult> HandleAuthenticateAsync(){    var messageReceivedContext = new MessageReceivedContext(Context, Scheme, Options);    // 先触发MessageReceived事件,来获取Tokenawait Events.MessageReceived(messageReceivedContext);    if (messageReceivedContext.Result != null){        return messageReceivedContext.Result;}token = messageReceivedContext.Token;    // Token为空时,从Authorization头中获取if (string.IsNullOrEmpty(token)){        string authorization = Request.Headers["Authorization"];        if (string.IsNullOrEmpty(authorization)){            return AuthenticateResult.NoResult();}        if (authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)){token = authorization.Substring("Bearer ".Length).Trim();}        if (string.IsNullOrEmpty(token)){            return AuthenticateResult.NoResult();}}...        
}

然后初始化TokenValidationParameters参数,为Token验证做准备:

if (_configuration == null && Options.ConfigurationManager != null)
{_configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.RequestAborted);
}var validationParameters = Options.TokenValidationParameters.Clone();if (_configuration != null)
{    var issuers = new[] { _configuration.Issuer };validationParameters.ValidIssuers = validationParameters.ValidIssuers?.Concat(issuers) ?? issuers;validationParameters.IssuerSigningKeys = validationParameters.IssuerSigningKeys?.Concat(_configuration.SigningKeys)?? _configuration.SigningKeys;
}

可以看到,从OIDC服务器提供的配置发现中,获取ValidIssuersIssuerSigningKeys

最后对Token进行验证:

// Options.SecurityTokenValidators 默认为: new List<ISecurityTokenValidator> { new JwtSecurityTokenHandler() }foreach (var validator in Options.SecurityTokenValidators)
{    if (validator.CanReadToken(token)){ClaimsPrincipal principal;        try{principal = validator.ValidateToken(token, validationParameters, out validatedToken);}        catch (Exception ex){  // RefreshOnIssuerKeyNotFound默认为True, 在SignatureKey未找到时,重新从OIDC服务器获取if (Options.RefreshOnIssuerKeyNotFound && Options.ConfigurationManager != null&& ex is SecurityTokenSignatureKeyNotFoundException){Options.ConfigurationManager.RequestRefresh();}            continue;}...        // 触发TokenValidated事件await Events.TokenValidated(tokenValidatedContext);        // 默认为true,保存Token到`AuthenticationProperties`中,可以通过`context.AuthenticateAsync()`来获取,在我们需要在服务端使用用户Token调用其他资源是非常有用。if (Options.SaveToken){tokenValidatedContext.Properties.StoreTokens(new[]{                new AuthenticationToken { Name = "access_token", Value = token }});}        // 验证成功tokenValidatedContext.Success();        return tokenValidatedContext.Result;}
}

其核心的验证也是在Microsoft.IdentityModel.Tokens中,就不在深究。

当使用JwtBearer认证时,我们肯定不希望在未登录时返回一个302,因此在前面的示例中,我们配置了x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;,对应的,会执行JwtBearerHandler的HandleChallengeAsync方法:

protected override async Task HandleChallengeAsync(AuthenticationProperties properties){   
 var authResult = await HandleAuthenticateOnceSafeAsync();  
   var eventContext = new JwtBearerChallengeContext(Context, Scheme, Options, properties){AuthenticateFailure = authResult?.Failure};  
    if (Options.IncludeErrorDetails && eventContext.AuthenticateFailure != null){eventContext.Error = "invalid_token";eventContext.ErrorDescription = CreateErrorDescription(eventContext.AuthenticateFailure);}    
    
    await Events.Challenge(eventContext);  
     if (eventContext.Handled){        return;}Response.StatusCode = 401;    // 最终将相应报文拼接成如下:// https://tools.ietf.org/html/rfc6750#section-3.1// WWW-Authenticate: Bearer realm="example", error="invalid_token", error_description="The access token expired"}

ASP.NET Core JwtBearer认证的完整源码地址:Microsoft.AspNetCore.Authentication.JwtBearer。

总结

JwtToken其实与Cookie认证中加密后的Cookie值很像,他们都是基于Claim的,认证时无需STS(Security token service)的参与,这在分布式环境下提供了极大的便利。而他们的本质上的区别是:Cookie是微软式的,很难与其他语言集成,而JwtToken则是开放再开放,与平台,语言无关,在前端也可以直接解析出Claims。

PS: 在使用在Bearer认证时,通常还需与刷新Token配合来使用,因为JwtToken的验证是无需经过STS的,而当用户执行了退出,修改密码等操作时,是无法使该Token失效的。所以,通常会给access_token设置一个较短的有效期(JwtBearer认证默认会验证有效期,通过notBeforeexpires来验证),当access_token过期后,可以在用户无感知的情况下,使用refresh_token自动从STS重新获取access_token,但这就不属于Bearer认证的范畴了,在后续介绍IdentityServer时再来详细介绍一下。

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


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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/323041.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

隧道裂缝检测_2【C++PCL】

作者:迅卓科技 简介:本人从事过多项点云项目,并且负责的项目均已得到好评! 公众号:迅卓科技,一个可以让您可以学习点云的好地方 1.前言 我们团队注重每一个细节,确保代码的可读性、可维护性和可扩展性达到最高标准。我们严格遵循行业最佳实践,采用模块化和面向对象的设…

开源纯C#工控网关+组态软件(五)从网关到人机界面

一、 引子 之前都在讲网关&#xff0c;不少网友关注如何实现界面。想了解下位机变量变化&#xff0c;是怎样一步步触发人机界面动画的。 这个步步触发&#xff0c;实质上是变量组&#xff08;Group&#xff09;的批量数据变化&#xff08;DataChange&#xff09;事件&#xf…

P1315,jzoj3029-观光公交【费用流】

前言 你绝对想不到&#xff0c;我用费用流神仙构图做了一道 的题 正题 评测记录:https://www.luogu.org/recordnew/lists?uid52918&pidP1315 题目大意 有nnn个地方&#xff0c;iii到第i1i1i1的长度为did_idi​。 有mmm个人&#xff0c;从tit_iti​出发&#xff0c;从l…

Spring依赖注入和控制反转

文章目录1、依赖注入1.1、依赖注入和控制反转的概念1.2、依赖注入的实现方式1.3、控制反转的具体实现1.4、依赖注入的具体实现1.5、依赖注入和控制反转总结1、依赖注入 1.1、依赖注入和控制反转的概念 依赖注入(Dependency Injection, 简称DI)与控制反转(IoC)的含义相同&…

Hadoop入门(五)IO操作

一、HadoopIO操作意义 Hadoop自带一套用于I/O的原子性的操作 &#xff08;不会被线程调度机制打断&#xff0c;一直到结束&#xff0c;中间不会有任何context switch&#xff09; 特点 基于保障海量数据集的完整性和压缩性 Hadoop提供了一些用于开发分布式系统的API&#xff…

使用Identity Server 4建立Authorization Server (1)

预备知识: 学习Identity Server 4的预备知识 本文内容基本完全来自于Identity Server 4官方文档: https://identityserver4.readthedocs.io/ 官方文档很详细的. 使用OAuth可以更安全, 这里我们的authorization server和web api 以及网站将分别独立运行. 建立authorization ser…

Python和SQL Server 2017的力量

Python是SQL Server 2017的新功能。 它主要是为了允许在SQL Server中使用基于Python的机器学习&#xff0c;但是它可以与任何Python库或框架一起使用。为了提供可能的例子&#xff0c;Hitendra展示了如何安全地使用该功能来提供智能应用程序缓存&#xff0c;其中SQL Server可以…

Spring中的Bean配置、属性配置、装配内容详细叙述

文章目录1、Bean的配置1.1、配置方式2、Bean的实例化2.1、构造器实例化2.2、静态工厂方式实例化2.3、实例工厂方式实例化3、Bean的作用域3.1、作用域的种类4、Bean的生命周期5、Bean的装配方式5.1、基于XML的装配5.2、基于Annotation的装配5.3、自动装配1、Bean的配置 1.1、配…

Hadoop入门(六)Mapreduce

一、Mapreduce概述 MapReduce是一个编程模型&#xff0c;用以进行大数据量的计算 二、Hadoop MapReduce &#xff08;1&#xff09;MapReduce是什么 Hadoop MapReduce是一个软件框架&#xff0c;基于该框架能够容易地编写应用程序&#xff0c;这些应用程序能够运行在由上千个…

Ocelot API网关的实现剖析

在微软Tech Summit 2017 大会上和大家分享了一门课程《.NET Core 在腾讯财付通的企业级应用开发实践》&#xff0c;其中重点是基于ASP.NET Core打造可扩展的高性能企业级API网关&#xff0c;以开源的API网关Ocelot为基础结合自己的业务特性&#xff0c;当天课程只有40分钟&…

Hadoop入门(十二)Intellij IDEA远程向hadoop集群提交mapreduce作业

Intellij IDEA远程向hadoop集群提交mapreduce作业&#xff0c;需要依赖到hadoop的库&#xff0c;hadoop集群的配置信息&#xff0c;还有本地项目的jar包。 一、软件环境 &#xff08;1&#xff09;window本地安装hadoop软件 首先将集群上的hadoop环境下载到本地&#xff0c;…

Spring AOP知识点简介

文章目录1、什么是AOP1.1、AOP术语1.2、AOP框架2、动态代理2.1、JDK动态代理2.2、CGLIB动态代理3、基于代理类的AOP实现3.1、Spring的通知类型3.2、ProxyFactoryBean4、AspectJ开发4.1、基于XML的声明式AspectJ4.2、基于注解的声明式AspectJ1、什么是AOP 面向切面编程&#xf…

SQL2017 Azure SQL新功能:图形数据库

图形数据库是什么呢&#xff1f;如果从字面理解是进行图形处理的数据库&#xff0c;那么你就错了哈哈。 我们先来解释什么是图形数据库。 图形数据库是NoSQL数据库的一种类型&#xff0c;它应用图形理论存储实体之间的关系信息。最常见的例子&#xff0c;就是社会网络中人与人之…

Hadoop入门(十三)远程提交wordCout程序到hadoop集群

一、项目结构 用到的文件有WordCount.java、core-site.xml、mapreduce-site.xml、yarn-site.xml、log4j.properties、pom.xml 二、项目源码 &#xff08;1&#xff09;WordCount.java package com.mk.mapreduce;import org.apache.hadoop.conf.Configuration; import org.ap…

腾讯云短信服务使用记录与.NET Core C#代码分享

1、即使是相同的短信签名与短信正文模板&#xff0c;也需要针对“国内文本短信”与“海外文本短信”分别申请。开始不知道&#xff0c;以为只要申请一次&#xff0c;给国外手机发短信时给api传对应的国家码就行&#xff0c;后来才发现需要分别申请。 2、短信服务web api响应“手…

Hadoop入门(九)Mapreduce高级shuffle之Combiner

一、Combiner的出现 &#xff08;1&#xff09;为什么需要进行Map规约操 作 在上述过程中&#xff0c;我们看到至少两个性能瓶颈&#xff1a; &#xff08;1&#xff09;如果我们有10亿个数据&#xff0c;Mapper会生成10亿个键值对在网络间进行传输&#xff0c;但如果我们只…

欢乐纪中某A组赛【2019.1.19】

前言 因为BBB有一堆(两道)题都做过&#xff0c;于是就来做A组了。 成绩 RankRankRank是有算别人的 RankRankRankPersonPersonPersonScoreScoreScoreAAABBBCCC3332017myself2017myself2017myself2102102101001001001001001001010102222222017lrz2017lrz2017lrz1001001000001001…

使用Identity Server 4建立Authorization Server (2)

第一部分: 使用Identity Server 4建立Authorization Server (1) 第一部分主要是建立了一个简单的Identity Server. 接下来继续: 建立Web Api项目 如图可以在同一个解决方案下建立一个web api项目: (可选)然后修改webapi的launchSettings.json, 我习惯使用控制台, 所以把IISExpr…

建立Vue脚手架的必要性

首先所有文件都放到一个html&#xff0c;代码多了之后阅读体验非常差。 其次建立这样的文件夹后&#xff0c;发现竟然不能随时更新&#xff0c;有缓存的情况

【实验手册】使用Visual Studio Code 开发.NET Core应用程序

.NET Core with Visual Studio Code 目录 概述... 2 先决条件... 2 练习1&#xff1a; 安装和配置.NET Core以及Visual Studio Code 扩展... 2 任务1&#xff1a;安装Visual Studio Code和.NET Core. 2 任务2&#xff1a;安装插件... 4 练习2&#xff1a;使用命令行界面构建. N…