ASP.NET Core 認證與授權[4]:JwtBearer認證

来源:http://www.cnblogs.com/RainingNight/archive/2017/11/03/jwtbearer-authentication-in-asp-net-core.html
-Advertisement-
Play Games

在現代Web應用程式中,通常會使用Web, WebApp, NativeApp等多種呈現方式,而後端也由以前的Razor渲染HTML,轉變為Stateless的RESTFulAPI,因此,我們需要一種標準的,通用的,無狀態的,與語言無關的認證方式,也就是本文要介紹的 JwtBearer 認證。 目錄 ...


在現代Web應用程式中,通常會使用Web, WebApp, NativeApp等多種呈現方式,而後端也由以前的Razor渲染HTML,轉變為Stateless的RESTFulAPI,因此,我們需要一種標準的,通用的,無狀態的,與語言無關的認證方式,也就是本文要介紹的JwtBearer認證。

目錄

  1. Bearer認證
  2. JWT(JSON WEB TOKEN)
  3. 示例
  4. 擴展
  5. 源碼探索

Bearer認證

HTTP提供了一套標準的身份驗證框架:伺服器可以用來針對客戶端的請求發送質詢(challenge),客戶端根據質詢提供身份驗證憑證。質詢與應答的工作流程如下:伺服器端向客戶端返回401(Unauthorized,未授權)狀態碼,併在WWW-Authenticate頭中添加如何進行驗證的信息,其中至少包含有一種質詢方式。然後客戶端可以在請求中添加Authorization頭進行驗證,其Value為身份驗證的憑證信息。

HTTPAuth

在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],直接在瀏覽器中訪問:

access_token_in_url

同樣的,我們也可以很容易的在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();

    // 配置IdentitryServer
    services.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服務的地址,然後便可以自動發現Issuer, IssuerSigningKey等配置,而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-urlencoded

client_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.Audience
        if (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事件,來獲取Token
    await 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時再來詳細介紹一下。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前面文章介紹瞭如何使用Identity在ASP.NET MVC中實現用戶的註冊、登錄以及身份驗證。這些功能都是與用戶信息安全相關的功能,數據安全的重要性永遠放在第一位。那麼對於註冊和登錄功能來說要把密碼及用戶其它信息通過表單的形式安全的提交到伺服器上,那麼最適合的方法就是使用HTTPS(如果有條件或 ...
  • 昨天分享的博文《angularjs呼叫Web API》http://www.cnblogs.com/insus/p/7772022.html,只是從Entity獲取數據,沒有進行參數POST。 今天分享一個例子,是傳遞參數至Web API來獲取數據的。而且數據是存儲在SQL中。數表結構是昨晚幫助網友 ...
  • 目前想做一個類似這樣的功能,通過win32 api 獲取到一個視窗程式中的Combox句柄,然後根據這個句柄設置Comboxd的選項同時觸發Comobox的selectIndexChange事件。目前僅實現了Combox中設置值,對於如何觸發selectIndexChange沒有頭緒,希望園中的大神 ...
  • //TransmitFile實現下載 protected void Button1_Click(object sender, EventArgs e) { /* 微軟為Response對象提供了一個新的方法TransmitFile來解決使用Response.BinaryWrite 下載超過400mb ...
  • 在對業務層進行單元測試時,因為業務層調用到數據處理層,所以要用Moq去模擬DbContext,這個很容易做到,但如果操作DbContext下的DbSet和DbSet下的擴展方法時,就會拋出一個System.NotSupportedException異常。這是因為我們沒辦法Mock DbSet,並助D... ...
  • 在前面介紹了框架中工作流的幾個開發過程,本篇隨筆重點介紹一下日常審批環節中的具體處理過程,從開始創建表單,以及各個審批、會簽過程的流轉過程,希望大家對其中流程的處理有一個大概的印象。 ...
  • 本文實例講述了C#對圖片文件的壓縮、裁剪操作方法,在C#項目開發中非常有實用價值。分享給大家供大家參考。具體如下: 一般在做項目時,對圖片的處理,以前都採用在上傳時,限制其大小的方式,這樣帶來諸多不便。畢竟網站運維人員不一定會對圖片做處理,經常超出大小限制,即使會使用圖片處理軟體的,也由於個人水平方 ...
  • 劇情介紹 在傳統的信息系統(比如小規模的ERP\MES系統),往往只是進行簡單的應用伺服器和資料庫伺服器的分散式部署,以此來提高應用系統的負載能力,而伴隨著訪問的增大,應用伺服器層面除了做硬體和網路的擴容,很難應對【套路式開頭】。 當然現在開源技術很多,不就是分散式麽,應用伺服器分散式、資料庫讀寫分 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...