.NET Core 集成JWT認證

JWT(Json web token)就不用過多的介紹了,在 .NET Core 開發中使用JWT進行認證也是比較常見的,而且接入過程也比較簡單,隨便配置配置就好了。

要想使用JWT,僅僅只需要在項目中引用微軟的一個認證組件。

Install-Package Microsoft.AspNetCore.Authentication.JwtBearer

然後將一些敏感數據可以放在配置文件appsettings.json中。

{
    "JWT": {
        "ClockSkew": 10,
        "ValidAudience": "https://meowv.com",
        "ValidIssuer": "阿星Plus",
        "IssuerSigningKey": "6Zi/5pifUGx1c+mYv+aYn1BsdXPpmL/mmJ9QbHVz6Zi/5pifUGx1c+mYv+aYn1BsdXPpmL/mmJ9QbHVz6Zi/5pifUGx1c+mYv+aYn1BsdXPpmL/mmJ9QbHVz6Zi/5pifUGx1cw==",
        "Expires": 30
    }
}

Startup中添加配置並且使用

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(options =>
        {
            options.TokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuer = true,
                ValidateAudience = true,
                ValidateLifetime = true,
                ClockSkew = TimeSpan.FromSeconds(Convert.ToInt32(Configuration.GetSection("JWT")["ClockSkew"])),
                ValidateIssuerSigningKey = true,
                ValidAudience = Configuration.GetSection("JWT")["ValidAudience"],
                ValidIssuer = Configuration.GetSection("JWT")["ValidIssuer"],
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration.GetSection("JWT")["IssuerSigningKey"]))
            };
        });

services.AddAuthorization();
app.UseAuthentication();
app.UseAuthorization();

這樣一個簡單的JWT配置就完成了,接下來新寫一個接口去生成token。

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;

namespace JsonWebTokenDemo.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class AuthController : ControllerBase
    {
        public AuthController(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        [HttpGet]
        [Route("Token")]
        public string GenerateTokenAsync(string username, string password)
        {
            if (username == "meowv" && password == "123")
            {
                var claims = new[] {
                    new Claim(ClaimTypes.Name, username),
                    new Claim(ClaimTypes.Email, "[email protected]"),
                    new Claim(JwtRegisteredClaimNames.Exp, $"{new DateTimeOffset(DateTime.Now.AddMinutes(Convert.ToInt32(Configuration.GetSection("JWT")["Expires"]))).ToUnixTimeSeconds()}"),
                    new Claim(JwtRegisteredClaimNames.Nbf, $"{new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}")
                };

                var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration.GetSection("JWT")["IssuerSigningKey"]));
                var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

                var securityToken = new JwtSecurityToken(
                    issuer: Configuration.GetSection("JWT")["ValidIssuer"],
                    audience: Configuration.GetSection("JWT")["ValidAudience"],
                    claims: claims,
                    expires: DateTime.Now.AddMinutes(Convert.ToInt32(Configuration.GetSection("JWT")["Expires"])),
                    signingCredentials: creds);

                var token = new JwtSecurityTokenHandler().WriteToken(securityToken);

                return token;
            }
            else
            {
                throw new Exception("賬號密碼錯誤");
            }
        }
    }
}

模擬用戶登錄,成功登錄則去生成token,在實際應用中還可以對接第三方登錄系統進行認證,調用接口看下效果。

可以看到第一個接口輸入正確的賬號密碼,成功返回了token,第二個接口會拋出一個異常。

接下來去寫兩個接口,去驗證一下token的使用是否正常,寫一個需要授權的接口和一個不需要授權的接口。

[HttpGet]
[Authorize]
[Route("AuthorizeTest")]
public string AuthorizeTest()
{
    return "我是返回結果";
}

[HttpGet]
[AllowAnonymous]
[Route("AllowAnonymousTest")]
public string AllowAnonymousTest()
{
    return "我是返回結果";
}

這兩個接口的唯一區別就是,[Authorize][AllowAnonymous]

添加了 [Authorize]特性的表明是需要進行授權纔可以訪問此接口,而添加了[AllowAnonymous]特性則表明不需要授權誰都可以訪問,同樣調用看一下效果。


第一個接口沒有返回出結果,可見生效了,此時調用的時候就需要帶上我們前面生成的token成功授權後才能返回數據。

有時候當我們沒有成功授權,會直接返回一個401的錯誤頁面,如果需要自定義返回信息需要怎麼做呢?

這個有好幾種做法,可以用中間件,攔截器等等,不過這裏推薦一種組件集成好的做法,直接上代碼。

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(options =>
        {
            ...

            options.Events = new JwtBearerEvents
            {
                OnChallenge = async context =>
                {
                    context.HandleResponse();

                    context.Response.ContentType = "application/json;charset=utf-8";
                    context.Response.StatusCode = StatusCodes.Status401Unauthorized;

                    await context.Response.WriteAsync("{\"message\":\"Unauthorized\",\"success\":false}");
                }
            };
        });

添加上面這段代碼即可,await context.Response.WriteAsync()可以返回你自定義的錯誤消息,這裏返回的是一個json字符串。

另外還有一種場景,默認我們拿到token進行授權訪問,是需要在請求頭中添加Authorization Bearer {token}這種方式的,如果我不想在請求頭中使用要怎麼做呢?比如我想將token放在URL參數中,或者cookie中?

同樣也是可以的,而且實現方式也超級簡單,看下面代碼。

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(options =>
        {
            ...

            options.Events = new JwtBearerEvents
            {
                ...
                OnMessageReceived = async context =>
                {
                    context.Token = context.Request.Query["token"];

                    await Task.CompletedTask;
                }
            };
        });

這裏演示了將token放在URL請求參數中,其它情況請根據實際開發場景進行修改即可。

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