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請求參數中,其它情況請根據實際開發場景進行修改即可。