asp.net core認證與授權:Bearer驗證與JWT

1、Bearer認證概念

Bearer驗證也屬於HTTP協議標準驗證。

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

2、JWT(Json web token)

上面介紹的Bearer認證,其核心便是BEARER_TOKEN,而最流行的Token編碼方式便是:JSON WEB TOKEN。

頭部(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 認證的使用方式。

DEMO

1、添加jwt包引用

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

 2、Startup類中添加如下配置

services.AddAuthentication(x =>
            {
                x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            }).AddJwtBearer(o =>
            {
                o.TokenValidationParameters = new TokenValidationParameters
                {
                    NameClaimType = JwtClaimTypes.Name,
                    RoleClaimType = JwtClaimTypes.Role,

                    ValidIssuer = "http://localhost:5200",
                    ValidAudience = "api",
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Consts.Secret))

                    /***********************************TokenValidationParameters的參數默認值***********************************/
                    // RequireSignedTokens = true,
                    // SaveSigninToken = false,
                    // ValidateActor = false,
                    // 將下面兩個參數設置爲false,可以不驗證Issuer和Audience,但是不建議這樣做。
                    // ValidateAudience = true,
                    // ValidateIssuer = true, 
                    // ValidateIssuerSigningKey = false,
                    // 是否要求Token的Claims中必須包含Expires
                    // RequireExpirationTime = true,
                    // 允許的服務器時間偏移量
                    // ClockSkew = TimeSpan.FromSeconds(300),
                    // 是否驗證Token有效期,使用當前時間與Token的Claims中的NotBefore和Expires對比
                    // ValidateLifetime = true
                };
                o.Events = new JwtBearerEvents() {
                    OnMessageReceived = context => {
                        //支持通過url傳token
                       context.Token =  context.Request.Query["access_token"];
                        return Task.CompletedTask;
                    }
                };
            });
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseSwagger();
                app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "JWTDemo v1"));
            }

            app.UseHttpsRedirection();

            app.UseRouting();

            app.UseAuthentication();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }

3、增加生成token的action

[HttpPost("authenticate")]
        public IActionResult Authenticate([FromBody] UserDto userDto)
        {
            //驗證
            var user = _store.FindUser(userDto.UserName, userDto.Password);
            if (user == null) return Unauthorized();

            //JWT載荷(Payload)
            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.UserName),
                    new Claim(JwtClaimTypes.Email, user.Email),
                    new Claim(JwtClaimTypes.PhoneNumber, user.PhoneNumber)
                }),
                //過期時間
                Expires = expiresAt,
                //簽證
                SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
            };

            var tokenHandler = new JwtSecurityTokenHandler();
            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.UserName,
                    auth_time = new DateTimeOffset(authTime).ToUnixTimeSeconds(),
                    expires_at = new DateTimeOffset(expiresAt).ToUnixTimeSeconds()
                }
            });
        }

 

4、添加受保護資源

 

5、運行測試

5.1直接訪問WeatherForecast接口,會返回401。

5.2先訪問Authenticate?username=a&pwd=123獲取token.

 

 

5.3帶上token重新請求WeatherForecast,請求成功。

 

 測試代碼地址:https://gitee.com/xiaoqingyao/jwt.git

源:https://www.cnblogs.com/RainingNight/p/jwtbearer-authentication-in-asp-net-core.html

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章