最近,團隊的小伙伴們在做專案時,需要用到JWT認證,遂根據自己的經驗,整理成了這篇文章,用來幫助理清JWT認證的原理和代碼撰寫操作,
一、JWT
JSON Web Token (JWT)是一個開放標準(RFC 7519),它定義了一種緊湊的、自包含的方式,用于作為JSON物件在各方之間安全地傳輸資訊,該資訊可以被驗證和信任,因為它是數字簽名的,
JWT是什么,看上面這段網上抄來的話,
關于JWT以及優缺點,網上有很多詳細的說法,我這兒就不重復了,
我們只需要知道以下的事實:
在一般的系統中,我們有時候會做個用戶登錄,用戶登錄完成進到系統后,需要根據用戶的權限,來控制一些功能可用,而另一些功能不可用,
在SOA/AOP架構中,做為最重要的API端,其實也需要有類似登錄或認證的內容,用來區分哪些用戶可以使用某個API,哪些用戶不行,
同時,我們希望這個登錄或類似登錄的程序,只發生在一個固定位置,這樣,在我們寫代碼時,建立好這樣一個程序后,在我們后邊寫代碼時,簡單參考即可,而不需要每個API程式都開發一次認證,這個需求,其實就是OAuth的由來,
最重要的是,這樣的代碼寫出來,顯得高大上,
下面進入正題,
認證這個操作,就像我們最近的日子,
首先,我們要有一個出入證,或者綠碼,這個證,我們稱作令牌(Token),我們去領這個證,這個操作稱為發行(Issue),
我們拿著這個證,去到一個地方,有專人會檢查這個證,這稱為用戶身份驗證(Authentication),驗證通過放行,稱為授權(Authorization),驗證不通過,叫作未授權錯誤(Unauthorized),
如果這個證過期了,你就需要去重新辦一個證,這個程序叫做重繪(RefreshToken),
簡言之,這就是認證的全部流程,
下面,我用一個Demo專案,來逐步完成這個程序,
二、開發環境&基礎專案
這個Demo的開發環境是:Mac + VS Code + Dotnet Core 3.1.2,
$ dotnet --info
.NET Core SDK (reflecting any global.json):
Version: 3.1.201
Commit: b1768b4ae7
Runtime Environment:
OS Name: Mac OS X
OS Version: 10.15
OS Platform: Darwin
RID: osx.10.15-x64
Base Path: /usr/local/share/dotnet/sdk/3.1.201/
Host (useful for support):
Version: 3.1.3
Commit: 4a9f85e9f8
.NET Core SDKs installed:
3.1.201 [/usr/local/share/dotnet/sdk]
.NET Core runtimes installed:
Microsoft.AspNetCore.App 3.1.3 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.NETCore.App 3.1.3 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]
首先,在這個環境下建立工程:
- 創建Solution
% dotnet new sln -o demo
The template "Solution File" was created successfully.
- 用Webapi模板創建專案
% cd demo
% dotnet new webapi -o demo
The template "ASP.NET Core Web API" was created successfully.
Processing post-creation actions...
Running 'dotnet restore' on demo/demo.csproj...
Restore completed in 179.13 ms for demo/demo.csproj.
Restore succeeded.
- 把Demo專案加到Solution中
% dotnet sln add demo/demo.csproj
Project `demo/demo.csproj` added to the solution.
- 安裝Swagger(這步非必須,我習慣用Swagger,不習慣用Postman)
% dotnet add package Swashbuckle.AspNetCore
log : Restore completed in 2.75 sec for demo/demo.csproj.
- 安裝JWT認證支持庫(必須引入)
% dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
log : Restore completed in 3.09 sec for demo/demo.csproj.
五步做完,基礎專案就建完了,
看一下整個的目錄結構:
% tree .
.
├── demo
│ ├── Controllers
│ │ └── WeatherForecastController.cs
│ ├── Program.cs
│ ├── Properties
│ │ └── launchSettings.json
│ ├── Startup.cs
│ ├── WeatherForecast.cs
│ ├── appsettings.Development.json
│ ├── appsettings.json
│ ├── demo.csproj
│ └── obj
│ ├── demo.csproj.nuget.dgspec.json
│ ├── demo.csproj.nuget.g.props
│ ├── demo.csproj.nuget.g.targets
│ ├── project.assets.json
│ └── project.nuget.cache
└── demo.sln
- 在Startup.cs中補充代碼,以啟用Swagger
在ConfigureServices方法中加入以下代碼:
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Demo", Version = "V1" });
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Name = "Authorization",
Type = SecuritySchemeType.ApiKey,
Scheme = "Bearer",
BearerFormat = "JWT",
In = ParameterLocation.Header,
Description = "",
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
new string[] {}
}
});
});
在Configure方法中加入以下代碼
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Demo V1");
});
關于Swagger的詳細配置,這里不做說明,留著以后寫,
三、簽發Token
簽發Token是認證的第一步,
用戶進到系統,在驗證用戶帳號密碼后,需要根據用戶的資料,把Token回傳給用戶,
這個程序其實跟認證沒什么關系,只是一個普通的API功能,
- 工程下加一個目錄DTOModels,創建一個LoginRequestDTO的類,用于定義API的輸入引數,
using System;
namespace demo.DTOModels
{
public class LoginRequestDTO
{
public string username { get; set; }
public string password { get; set; }
}
}
- 創建一個控制器AuthenticationController,并在控制器里創建一個API方法RequestToken,
using Microsoft.AspNetCore.Mvc;
using demo.DTOModels;
namespace demo.Controllers
{
public class AuthenticationController : ControllerBase
{
[HttpPost, Route("requesttoken")]
public ActionResult RequestToken([FromBody] LoginRequestDTO request)
{
//這兒待完善
return Ok();
}
}
}
- 生成JWT Token需要預設一些引數,我們在appsetting.json里先設定好,
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"tokenParameter": {
"secret": "123456123456123456",
"issuer": "WangPlus",
"accessExpiration": 120,
"refreshExpiration": 1440
}
}
這里,tokenParameter節是我們設定的引數,一般來說,是這幾個:
secret: JWT加密的密鑰,現在主流用SHA256加密,需要256位以上的密鑰,unicode是16個字符以上,盡量復雜一些,密鑰泄露,Token就會被破解,所以,你懂的,
issuer: 簽發人的名稱,如果沒人注意,你可以把大名寫在上面,
accessExpiration: Token的有效分鐘數,過了這個時間,這個Token會過期,
refreshExpiration: refreshToken的有效分鐘數,過了這個時間,用戶需要重新登錄,
Token過期后,可以讓用戶重新登錄認證拿Token,但這個方式會比較Low,高大上的方式是簽發Token的時候,同時也簽發一個refreshToken給用戶,用戶Token過期后,可以拿refreshToken去申請新的Token,同時重繪refreshToken,如果用戶長時間未使用系統,refreshToken也過期了,才讓用戶重新登錄認證,
refreshToken可以用JWT生成,也可以自己生成,不影響認證,
- 建一個Models目錄,創建一個映射tokenParameter的類,這個類不是必須,只是為了寫著方便,不想這樣寫,也可以直接讀配置,再轉成資料,
using System;
namespace demo.Models
{
public class tokenParameter
{
public string Secret { get; set; }
public string Issuer { get; set; }
public int AccessExpiration { get; set; }
public int RefreshExpiration { get; set; }
}
}
- 在前邊建好的API - RequestToken中,完成Token和refreshToken的生成和回傳,
using Microsoft.AspNetCore.Mvc;
using demo.DTOModels;
using Microsoft.Extensions.Configuration;
using System;
using System.Text;
using demo.Models;
using Microsoft.IdentityModel.Tokens;
using System.Security.Claims;
using System.IdentityModel.Tokens.Jwt;
namespace demo.Controllers
{
public class AuthenticationController : ControllerBase
{
private tokenParameter _tokenParameter = new tokenParameter();
public AuthenticationController()
{
var config = new ConfigurationBuilder()
.SetBasePath(AppContext.BaseDirectory)
.AddJsonFile("appsettings.json")
.Build();
_tokenParameter = config.GetSection("tokenParameter").Get<tokenParameter>();
}
[HttpPost, Route("requestToken")]
public ActionResult RequestToken([FromBody] LoginRequestDTO request)
{
//這兒在做用戶的帳號密碼校驗,我這兒略過了,
if (request.username == null && request.password == null)
return BadRequest("Invalid Request");
//生成Token和RefreshToken
var token = GenUserToken(request.username, "testUser");
var refreshToken = "123456";
return Ok(new[] { token, refreshToken });
}
//這兒是真正的生成Token代碼
private string GenUserToken(string username, string role)
{
var claims = new[]
{
new Claim(ClaimTypes.Name, username),
new Claim(ClaimTypes.Role, role),
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_tokenParameter.Secret));
var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var jwtToken = new JwtSecurityToken(_tokenParameter.Issuer, null, claims, expires: DateTime.UtcNow.AddMinutes(_tokenParameter.AccessExpiration), signingCredentials: credentials);
var token = new JwtSecurityTokenHandler().WriteToken(jwtToken);
return token;
}
}
}
這個類里,驗證帳號密碼的代碼我略過了,還有,refreshToken給了一個固定串,真實專案這兒就按需要做就好,
(未完待續)
![]() |
微信公眾號:老王Plus 掃描二維碼,關注個人公眾號,可以第一時間得到最新的個人文章和內容推送 本文著作權歸作者所有,轉載請保留此宣告和原文鏈接 |
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/30417.html
標籤:.NET Core

