本文主要介紹?Sang.AspNetCore.RoleBasedAuthorization[1]?庫如何通過中間件實現對用戶?
Claim
?的添加。
背景
前面我們介紹了通過對自定義授權策略和自定義授權處理程序的使用實現了基本的RBAC權限設計,將大量的用戶可訪問資源及操作的標識直接放到用戶的 JWT Token 中顯然并不合適,這篇文章我們主要介紹通過中間件如何根據用戶的角色添加用戶的?Claim
。
實現
角色獲取
首先我們需要提供一個接口?IRolePermission
?,需要用戶自行實現?GetRolePermissionClaimsByName
?通過角色名獲取用戶的?List<Claim>
。這里當然也可將用戶自身擁有的特定?Claim
?也加入進去。
public interface IRolePermission
{/// <summary>/// 獲取角色的所有 Permission /// </summary>/// <param name="roleName"></param>/// <returns></returns>Task<List<Claim>> GetRolePermissionClaimsByName(string roleName);
}
中間件核心邏輯
創建中間件?RolePermissionMiddleware
?,通過 DI 注入?IRolePermission rolePermission
。核心的執行邏輯為:
/// <summary>
/// 自定義中間件要執行的邏輯
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public async Task Invoke(HttpContext context)
{
}
要確保用戶信息存在
if (context.User is null)
{await _next(context);return;
}
這里我們提供了一個可選的參數,使中間件可以單獨使用,也可以僅在含有ResourceAttribute
標記時執行。
var endpoint = context.Features.Get<IEndpointFeature>()?.Endpoint;
if (endpoint is null)
{await _next(context);return;
}
var endpointMetaData = endpoint!.Metadata;
bool hasResourceAttribute = endpointMetaData.Any(x => x is ResourceAttribute);
if (!hasResourceAttribute)
{await _next(context);return;
}
該中間件主要的核心邏輯為讀取用戶所有的角色,然后查詢角色對應的權限將其放入。
// 獲取用戶的所有角色
var roles = context.User.FindAll(ClaimTypes.Role);
// 逐個獲取角色的 claims 并添加給 User
foreach (var role in roles.ToList())
{var roleclaims = await _rolePermission.GetRolePermissionClaimsByName(role.Value);if (roleclaims.Count() > 0){context.User.AddIdentity(new ClaimsIdentity(roleclaims));}
}
中間件注冊
中間件的注冊提供了可選的參數,同時需要添加用戶角色查詢服務。添加RolePermissionExtensions
:
/// <summary>
/// 添加根據角色名為 User 加入角色 Permission 的中間件
/// </summary>
/// <param name="app"></param>
/// <param name="configureOptions"></param>
/// <returns></returns>
public static IApplicationBuilder UseRolePermission(this IApplicationBuilder app, Action<RolePermissionOptions> configureOptions)
{var options = new RolePermissionOptions();configureOptions(options);return app.UseMiddleware<RolePermissionMiddleware>(options);
}/// <summary>
/// 添加根據角色名為 User 加入角色 Permission 的中間件
/// </summary>
/// <param name="app"></param>
/// <returns></returns>
public static IApplicationBuilder UseRolePermission(this IApplicationBuilder app)
{return app.UseMiddleware<RolePermissionMiddleware>(new RolePermissionOptions());
}/// <summary>
/// 添加角色權限查詢服務
/// </summary>
/// <typeparam name="RolePermission">獲取角色權限的實現</typeparam>
/// <param name="Services"></param>
public static void AddRolePermission<RolePermission>(this IServiceCollection Services) where RolePermission : class, IRolePermission
{Services.AddSingleton<IRolePermission, RolePermission>();
}
最后
需要特別注意的是,這個中間件啟用的位置。需要在?UseAuthentication
?之后?UseAuthorization
?之前,也就是說要在驗證了用戶后,開始檢查用戶權限前將用戶的角色權限賦予給?context.User
。
本文介紹的相關代碼已經提供 Nuget 包,并開源了代碼,感興趣的同學可以查閱: https://github.com/sangyuxiaowu/Sang.AspNetCore.RoleBasedAuthorization
如有錯漏之處,敬請指正。
References
[1]
?Sang.AspNetCore.RoleBasedAuthorization:?https://www.nuget.org/packages/Sang.AspNetCore.RoleBasedAuthorization