概述
認證授權是很多系統的基本功能 , 在以前PC的時代 , 通常是基於cookies-session這樣的方式實現認證授權 , 在那個時候通常系統的使用者量都不會很大, 所以這種方式也一直很好執行, 隨著現在都軟體使用者量越來越大, 系統架構也從以前垂直擴充套件(增加伺服器效能) -> 水平擴充套件(增加伺服器數量)
cookies-session 工作方式
客戶端提交使用者資訊 -> 伺服器識別使用者 -> 服務端儲存使用者資訊 -> 返回session-id客戶端 -> 客戶端儲存session-id -> 每次請求cookies帶上session-id
這種方式也不是不能水平擴充套件 , 例如 , session複製/第三方儲存session(資料庫 , Redis)
名詞解析
認證 : 識別使用者是否合法
授權: 賦予使用者許可權 (能訪問哪些資源)
鑑權: 鑑定許可權是否合法
Jwt優勢與劣勢
優勢
- 無狀態
token 儲存身份驗證所有資訊 , 服務端不需要儲存使用者身份驗證資訊, 減少服務端壓力 , 服務端更容易水平擴充套件, 由於無狀態, 又會導致它最大缺點 , 很難登出
- 支援跨域訪問
Cookie是不允許垮域訪問的,token支援
- 跨語言
基於標準化的 JSON Web Token (JWT) , 不依賴特定某一個語言 , 例如生成對Token可以對多個語言使用(Net , Java , PHP ...)
劣勢
- Token有效性問題
後臺很難登出已經發布的Token , 通常需要藉助第三方儲存(資料庫/快取) 實現登出, 這樣就會失去JWT最大的優勢
- 佔頻寬
Token長度(取決存放內容) 比session_id大 , 每次請求多消耗頻寬 , token只存必要資訊 , 避免token過長
- 需要實現續簽
cookies - session 通常是框架已經實現續簽功能, 每次訪問把過期時間更新, JWT需要自己實現, 參考OAuth2重新整理Token機制實現重新整理Token
- 消耗更多CPU
每次請求需要對內容解密和驗證簽名這兩步操作,典型用時間換空間
只能根據自身使用場景決定使用哪一種身份驗證方案 , 沒有一種方案是通用的,完美的
AspNetCore整合Jwt認證
- 新增包
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
- 新增配置
"JwtOptions": {
"Issuer": "https://localhost:5001",
"Audience": "https://localhost:5001",
"SecurityKey": "1G3l0yYGbOINId3A*ioEi4iyxR7$SPzm"
}
- Jwt Bearer 擴充套件(選項)
public static AuthenticationBuilder AddJwtBearer(this IServiceCollection services, Action<JwtOptions> configureOptions)
{
if (configureOptions == null) throw new ArgumentNullException(nameof(configureOptions));
var jwtOptions = new JwtOptions()
{
Issuer = "Jwt Authentication",
Audience = "Wilson Pan Web Api",
};
// set customs optoins
configureOptions(jwtOptions);
// update Options
services.PostConfigure<JwtOptions>(options =>
{
options.Issuer = jwtOptions.Issuer;
options.Audience = jwtOptions.Audience;
options.SecurityKey = jwtOptions.SecurityKey;
});
return services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters()
{
ValidIssuer = jwtOptions.Issuer,
ValidAudience = jwtOptions.Audience,
ValidateIssuer = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
IssuerSigningKey = jwtOptions.SymmetricSecurityKey
};
});
}
- ConfigureServices
services.AddJwtBearer(options =>
{
options.Issuer = Configuration.GetValue<string>("JwtOptions:Issuer");
options.Audience = Configuration.GetValue<string>("JwtOptions:Audience");
options.SecurityKey = Configuration.GetValue<string>("JwtOptions:SecurityKey");
});
- Configure
app.UseAuthentication();
app.UseAuthorization();
- add AuthorizeController
//define claim
var claims = new Claim[]
{
new Claim(ClaimTypes.Name, username),
new Claim(ClaimTypes.Email, $"{username}@github.com"),
new Claim(ClaimTypes.Role, username == "WilsonPan" ? "Admin" : "Reader"),
new Claim(ClaimTypes.Hash, JwtHashHelper.GetHashString($"{username}:{password}:{System.DateTime.Now.Ticks}")),
};
//define JwtSecurityToken
var token = new JwtSecurityToken(
issuer: _jwtOptions.Issuer,
audience: _jwtOptions.Audience,
claims: claims,
expires: System.DateTime.Now.AddMinutes(5),
signingCredentials: _jwtOptions.SigningCredentials
);
// generate token
var result = new JwtSecurityTokenHandler().WriteToken(token);
- Contrller/Action 新增認證授權
[ApiController]
[Authorize]
[Route("[controller]")]
public class ApiController : ControllerBase
{
...
}
[HttpPost]
[Authorize(Roles = "Admin")]
public IActionResult Post()
{
return Ok();
}
Rest Client
dotnet run
- 認證介面
@host = https://localhost:5001
# @name token
POST {{host}}/Authorize HTTP/1.1
Content-Type: application/x-www-form-urlencoded
#username=Wilson&password=123456
# admin
username=WilsonPan&password=123456
- 需要授權介面
### required authorize
GET {{host}}/api HTTP/1.1
Authorization: Bearer {{token.response.body.*}}
- 需要管理員角色介面
### required authorize
POST {{host}}/api HTTP/1.1
Authorization: Bearer {{token.response.body.*}}
轉發請標明出處:https://www.cnblogs.com/WilsonPan/p/13495936.html
示例程式碼