《ASP.NET Core 與 RESTful API 開發實戰》-- (第8章)-- 讀書筆記(上)
第 8 章 認證和安全
8.1 認證
認證(Authentication)是指驗證使用者身份的過程,授權(Authorization)是驗證一個已經通過認證的使用者是否有許可權做某些事的過程
常見的 HTTP 認證方式包括:
- Basic 認證:使用者名稱密碼
- Digest 認證:摘要認證
- Bearer 認證:token 認證
常見的 Token 有兩種型別,一種是引用型別,另一種是自包含型別。前者是伺服器根據自己定義的規則隨機生成的字串,後者是對使用者資訊以及 Token 元資料等資訊進行編碼、加密之後得到的結果。JWT 是最為常見的自包含型別的 Token
JWT 全名為 JSON Web Token,是一個開放標準,一種 Token 格式,它由3部分組成,分別是頭部、負載和簽名,各部分之間以.隔開,例如:header.payload.signature
頭部由兩部分組成,即 Token 型別和使用的演算法名稱
{
"alg": "HS256",
"type": "JWT"
}
負載部分包括要傳輸的資訊,通常由多個 Claim 構成,Claim 是與實體相關的資訊以及其他元資料
Claim 有3種類型:已註冊、公有、私有
公共型別的 Claim 主要是常見且通用的 Claim,如 name、email 和 gender 等
一個典型的 JWT 負載如下:
{ "sub": "1234567890", "name": "John Doe", "Admin": true }
簽名部分通過使用頭部指定的演算法以及一個金鑰,對 Base64 編碼後的頭部和負載加密而成
HMAC-SHA256(
encodeBase64Url(header) + '.' +
encodeBase64Url(payload),
secret)
簽名主要用於驗證訊息不會被篡改
最終,上述3個部分的內容均使用 Base64 編碼,並使用 "." 將各部分隔開,即為一個標準的 JWT
使用 JWT 能夠以緊湊的方式傳遞使用者資訊,並通過簽名保護其中的資訊不會被修改。需要注意到是,它很容易被解碼,因此不應該在 Token 中包含敏感資訊,如使用者密碼等
接下來,通過 JwtBearer 認證中介軟體實現基於 Token 的認證
新增nuget
Install-Package Microsoft.AspNetCore.Authentication.JwtBearer
在 Startup 類中註冊服務
// defaultScheme用於指定當未指定具體認證方案時將會使用的預設方案
services.AddAuthentication(defaultScheme: JwtBearerDefaults.AuthenticationScheme);
在 Startup 類中使用服務
app.UseAuthentication();
對於不同的認證方式(如 Cookie 或 JwtBearer),ASP.NET Core 均在其實現的包中包含相應的擴充套件方法,以便新增相應型別的認證方式,例如
services.AddAuthentication(defaultScheme: JwtBearerDefaults.AuthenticationScheme)
.AddCookie()
.AddJwtBearer();
可以新增多個相同型別的認證方式,但指定的名稱必須不同
services.AddAuthentication()
.AddCookie("cookie1")
.AddCookie("cookie2");
當新增 JwtBearer 認證方式時,JwtBearerOptions 物件能夠配置該認證的選項,它的 TokenValidationParameters 屬性用於指定驗證 Token 時的規則
var tokenSection = Configuration.GetSection("Security:Token");
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuer = true,
ValidateIssuerSigningKey = true,
ValidIssuer = tokenSection["Issuer"],// 合法的簽發者
ValidAudience = tokenSection["Audience"],// 合法的接受方
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(tokenSection["Key"])),// 簽名驗證的安全金鑰
ClockSkew = TimeSpan.Zero// 驗證時間的時間偏移值
};
});
上述程式碼會從配置檔案中讀取關於 Token 的資訊,因此還需要在 appsettings.json 中新增如下內容
"Security": {
"Token": {
"Issuer": "demo_issuer",
"Audience": "demo_audience",
"Key": "<your_secret_key>"
}
},
接下來,為了使用 ASP.NET Core 的認證功能來保護資源,應為 Controller 或 Action 新增 [Authorize] 特性
[Authorize]
public class AuthorController : ControllerBase
{}
如果使用了多個認證方式,可以使用 [Authorize] 特性的 AuthenticationSchemes 屬性指明當前使用哪一種認證方式
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class BookController : ControllerBase
{}
JwtBearer 中介軟體提供了對 JWT 的驗證功能,然而並未能提供生成 Token 的功能。要生成 Tokne,可以使用 JwtSecurityTokenHandler 類
接下來,我們建立一個 Controller,它將會根據使用者的認證資訊生成 JWT,並返回給客戶端
namespace Library.API.Controllers
{
[Route("auth")]
[ApiController]
public class AuthenticateController : ControllerBase
{
public IConfiguration Configuration { get; set; }
public AuthenticateController(IConfiguration configuration)
{
Configuration = configuration;
}
[HttpPost("token", Name = nameof(GenerateToken))]
public IActionResult GenerateToken(LoginUser loginUser)
{
if (loginUser.UserName != "demouser" || loginUser.Password != "demopassword")
{
return Unauthorized();
}
var claims = new List<Claim>
{
new Claim(JwtRegisteredClaimNames.Sub,loginUser.UserName)
};
var tokenConfigSection = Configuration.GetSection("Security:Token");
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(tokenConfigSection["Key"]));
var signCredential = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var jwtToken = new JwtSecurityToken(
issuer: tokenConfigSection["Issuer"],
audience: tokenConfigSection["Audience"],
claims: claims,
expires: DateTime.Now.AddMinutes(3),// 由於 JWT 不支援銷燬以及撤回功能,因此在設定它的有效時間時,應該設定一個較短的時間
signingCredentials: signCredential);
return Ok(new
{
token = new JwtSecurityTokenHandler().WriteToken(jwtToken),
expiration = TimeZoneInfo.ConvertTimeFromUtc(jwtToken.ValidTo, TimeZoneInfo.Local)
});
}
}
public class LoginUser
{
public string UserName { get; set; }
public string Password { get; set; }
}
}
對於受保護的資源,應在每一次請求時均攜帶 Authorization 訊息頭
如果不希望新增認證功能,則應為其新增 [AllowAnonymous] 特性
當伺服器驗證 Token 通過時,JwtBearer 認證處理器會通過 JwtSecurityTokenHandler 將 Token 轉換為 ClaimPrincipal 物件,並將他賦值給 HttpContext 物件的 User 屬性
ClaimPrincipal 類代表一個使用者,它包含一些重要的屬性(如 Identity 和 Identities),它們分別返回該物件中主要的 ClaimsIdentity 物件和所有的 ClaimsIdentity 物件集合
ClaimsIdentity 類代表使用者的一個身份,一個使用者可以有一個或多個身份;ClaimsIdentity 類則又由一個或多個 Claim 組成
Claim 類代表與使用者相關的具體資訊(如使用者名稱和出生日期等)
本作品採用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。
歡迎轉載、使用、重新發布,但務必保留文章署名 鄭子銘 (包含連結: http://www.cnblogs.com/MingsonZheng/ ),不得用於商業目的,基於本文修改後的作品務必以相同的許可釋出。
如有任何疑問,請與我聯絡 ([email protected]) 。