文章目錄
0、引言
若不清楚什么是JWT
的請先了解下什么是JWT。
1、關於Authentication與Authorization
我相信在aspnet core中剛接觸甚至用了段時間這兩個概念的時候都是一頭霧水的,傻傻分不清。
認證(Authentication)和授權(Authorization)在概念上比較的相似,且又有一定的聯系,因此很容易混淆。
認證(Authentication)
是指驗證用戶身份的過程,即當用戶要訪問受保護的資源時,將其信息(如用戶名和密碼)發送給服務器並由服務器驗證的過程。
授權(Authorization)
是驗證一個已通過身份認證的用戶是否有權限做某件事情的過程。
有過RBAC
的開發經驗者來說這里可以這么通俗的來理解:認證是驗證一個用戶是否“合法”(一般就是檢查數據庫中是否有這么個用戶),授權是驗證這個用戶是否有做事情的權限(簡單理解成RBAC中的用戶權限)。
2、整個認證流程是怎樣的?
從圖中可以看到整個認證、授權的流程,先進行身份驗證 ,驗證通過后將Token放回給客戶端,客戶端訪問資源的時候請求頭中添加Token信息,服務器進行驗證並於授權是否能夠訪問該資源。
3、開始JWT身份認證
3.1 安裝JwtBearer包
在.csproj項目中添加JWT包(這里添加有很多種方式,自行選擇)
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.3" />
3.2 安裝Swashbuckle.AspNetCore包
這里便於進行測試,引入Swagger工具。
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.3.1" />
3.3 添加身份認證相關服務到容器中
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = false,
ValidateIssuerSigningKey = true,
ValidIssuer = "jonny",
ValidAudience = "jonny",
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("secretsecretsecret"))
};
});
說明
配置項 | 類型 | 說明 |
---|---|---|
ValidateIssuerSigningKey | bool | 是否調用對簽名securityToken的SecurityKey進行驗證。 |
ValidIssuer | string | 將用於檢查令牌的發行者是否與此發行者相同。 |
ValidateIssuer | bool | 是否驗證發行者 |
ValidAudience | string | 檢查令牌的受眾群體是否與此受眾群體相同。 |
ValidateAudience | bool | 在令牌驗證期間驗證受眾 。 |
ValidateLifetime | bool | 驗證生命周期。 |
3.4 添加Swagger服務到容器中
services.AddSwaggerGen(options =>
{
options.SwaggerDoc("openapi", new Microsoft.OpenApi.Models.OpenApiInfo
{
Title = "統一身份認證API",
Description = "身份認證和授權詳解",
Version = "v1"
});
var scheme = new OpenApiSecurityScheme()
{
Scheme = JwtBearerDefaults.AuthenticationScheme,
BearerFormat = "JWT",
In = ParameterLocation.Header,
//頭名稱
Name = ApiKeyConstants.HeaderName,
Type = SecuritySchemeType.ApiKey,
Description = "Bearer Token"
};
options.AddSecurityDefinition(JwtBearerDefaults.AuthenticationScheme, scheme);
options.AddSecurityRequirement(new OpenApiSecurityRequirement()
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
new string[] {}
}
});
});
aspnet core 3.x swagger與2.x有細微的差別,例如swagger中加入jwt和以前就有一定的差別。
swagger加入身份認證后出現了認證按鈕。
3.5 將身份認證加入到管道中
//身份認證中間件(踩坑:授權中間件必須在認證中間件之前)
app.UseAuthentication();
3.x中身份認證一定要在UseRouting和UseEndpoints之間
3.6 將swagger加入到管道中
app.UseSwagger();
app.UseSwaggerUI(options =>
{
options.RoutePrefix = string.Empty;
//配置swagger端點
options.SwaggerEndpoint("swagger/openapi/swagger.json", "openapi v1");
});
3.7 在需要授權的資源上加入Authorize
例如:
[HttpGet("role")]
[Authorize(Roles = "admin")]
public IEnumerable<Claim> GetRole()
{
return HttpContext.User.FindAll(c => c.Type == ClaimTypes.Role);
}
這里默認使用
角色授權
機制
4 、測試
4.1 請求資源
這時會返回401,因為沒有進行身份認證
4.2 調用登錄獲取token
4.3 將token添加到Header中
4.4 再次請求
這時返回403,是因為使用的jonny賬戶登錄的沒有admin權限。
4.5 切換admin賬戶登錄
重復上面的4.3、4.4步驟 。再次測試。這時就能正常訪問。
5、登錄邏輯代碼
我這里就不做過多的解釋 ,直接將相關創建JTW
代碼等貼出來。
public interface ICustomAuthenticationManager
{
string Authenticate(string username, string password);
IDictionary<string, string> Tokens { get; }
}
public class CustomAuthenticationManager : ICustomAuthenticationManager
{
private readonly IDictionary<string, string> users = new Dictionary<string, string>
{
{ "admin", "admin" },
{ "jonny", "jonny" },
{ "xhl", "xhl" },
{ "james", "james" }
};
public IDictionary<string, string> Tokens { get; } = new Dictionary<string, string>();
public string Authenticate(string username, string password)
{
var claimsIdentity = new ClaimsIdentity(new[]{
new Claim(ClaimTypes.Name,username)
});
if (!users.Any(u => u.Key == username && u.Value == password))
{
return null;
}
if (username == "admin")
{
claimsIdentity.AddClaims(new[]
{
new Claim( ClaimTypes.Email, "xhl.jonny@gmail.com"),
new Claim( "ManageId", "admin"),
new Claim(ClaimTypes.Role,"admin")
});
}
var handler = new JwtSecurityTokenHandler();
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = claimsIdentity,
Expires = DateTime.Now.AddMinutes(3),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes("secretsecretsecret")), SecurityAlgorithms.HmacSha256),
};
var securityToken = handler.CreateToken(tokenDescriptor);
var token = handler.WriteToken(securityToken);
Tokens.Add(token, username);
return token;
}
}
上面使用內存數據進行邏輯驗證 ,實際中需要使用數據庫查詢驗證等。
今天的JWT身份認證就介紹完了 ,下一篇文章將介紹授權。角色授權、身份授權(Claim)、自定義策略授權。