JWT(Json web token) 就不用過(guò)多的介紹了,在 .NET Core 開(kāi)發(fā)中使用JWT進(jìn)行認(rèn)證也是比較常見(jiàn)的,而且接入過(guò)程也比較簡(jiǎn)單,隨便配置配置就好了。
要想使用JWT,僅僅只需要在項(xiàng)目中引用微軟的一個(gè)認(rèn)證組件。
Install-Package Microsoft.AspNetCore.Authentication.JwtBearer
然后將一些敏感數(shù)據(jù)可以放在配置文件appsettings.json 中。
{
"JWT": {
"ClockSkew": 10,
"ValidAudience": "https://",
"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();
這樣一個(gè)簡(jiǎn)單的JWT配置就完成了,接下來(lái)新寫一個(gè)接口去生成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, "123@"),
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("賬號(hào)密碼錯(cuò)誤");
}
}
}
}
模擬用戶登錄,成功登錄則去生成token,在實(shí)際應(yīng)用中還可以對(duì)接第三方登錄系統(tǒng)進(jìn)行認(rèn)證,調(diào)用接口看下效果。

可以看到第一個(gè)接口輸入正確的賬號(hào)密碼,成功返回了token,第二個(gè)接口會(huì)拋出一個(gè)異常。
接下來(lái)去寫兩個(gè)接口,去驗(yàn)證一下token的使用是否正常,寫一個(gè)需要授權(quán)的接口和一個(gè)不需要授權(quán)的接口。
[HttpGet]
[Authorize]
[Route("AuthorizeTest")]
public string AuthorizeTest()
{
return "我是返回結(jié)果";
}
[HttpGet]
[AllowAnonymous]
[Route("AllowAnonymousTest")]
public string AllowAnonymousTest()
{
return "我是返回結(jié)果";
}
這兩個(gè)接口的唯一區(qū)別就是,[Authorize] 、[AllowAnonymous] 。
添加了 [Authorize] 特性的表明是需要進(jìn)行授權(quán)才可以訪問(wèn)此接口,而添加了[AllowAnonymous] 特性則表明不需要授權(quán)誰(shuí)都可以訪問(wèn),同樣調(diào)用看一下效果。


第一個(gè)接口沒(méi)有返回出結(jié)果,可見(jiàn)生效了,此時(shí)調(diào)用的時(shí)候就需要帶上我們前面生成的token成功授權(quán)后才能返回?cái)?shù)據(jù)。
有時(shí)候當(dāng)我們沒(méi)有成功授權(quán),會(huì)直接返回一個(gè)401的錯(cuò)誤頁(yè)面,如果需要自定義返回信息需要怎么做呢?
這個(gè)有好幾種做法,可以用中間件,攔截器等等,不過(guò)這里推薦一種組件集成好的做法,直接上代碼。
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() 可以返回你自定義的錯(cuò)誤消息,這里返回的是一個(gè)json字符串。
另外還有一種場(chǎng)景,默認(rèn)我們拿到token進(jìn)行授權(quán)訪問(wèn),是需要在請(qǐng)求頭中添加Authorization Bearer {token} 這種方式的,如果我不想在請(qǐng)求頭中使用要怎么做呢?比如我想將token放在URL參數(shù)中,或者cookie中?
同樣也是可以的,而且實(shí)現(xiàn)方式也超級(jí)簡(jiǎn)單,看下面代碼。
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
...
options.Events = new JwtBearerEvents
{
...
OnMessageReceived = async context =>
{
context.Token = context.Request.Query["token"];
await Task.CompletedTask;
}
};
});
這里演示了將token放在URL請(qǐng)求參數(shù)中,其它情況請(qǐng)根據(jù)實(shí)際開(kāi)發(fā)場(chǎng)景進(jìn)行修改即可。

|