在 .NET Core 中实现基于策略和基于角色的授权
本文主要介绍如何使用 .NET Core 实现基于策略的授权。 如果您需要了解授权技术的基础知识,请参阅链接。让我们来分解一下关键组件:
1、身份验证设置
• 首先,在您的应用程序中设置身份验证。这可确保正确识别和验证用户身份。
• 您通常会使用第三方身份验证提供程序(如 Microsoft Identity Platform)来处理用户登录和颁发令牌。
2、授权策略
• 接下来,定义授权策略。这些策略决定谁可以访问应用程序的特定部分。
• 策略可以很简单(例如“仅限经过身份验证的用户”),也可以更复杂(基于声明、角色或自定义要求)。
3、默认策略
• 创建一个适用于所有端点的默认策略,除非被覆盖。
• 例如,您可能要求用户经过身份验证并具有特定声明(如首选用户名)。
4、自定义策略
• 为特定场景添加自定义策略。这些策略允许对访问进行细粒度的控制。
• 例如,您可以根据权限创建策略(例如“创建/编辑用户”或“查看用户”)。
5、权限要求
• 定义权限要求(例如 PermissionAuthorizationRequirement)。这些代表特定的操作或功能。
• 对于每个要求,检查用户是否具有必要的权限(基于其角色或其他标准)。
6、基于角色的授权
• 可选地,合并基于角色的授权。
• 角色将具有相似访问级别的用户分组(例如,“管理员”、“用户”等)。您可以将角色分配给用户。
7、授权处理程序
• 实施自定义授权处理程序(例如 AuthorizationHandler)。
• 这些处理程序评估用户是否满足要求(例如,具有正确的权限或角色)。
8、控制器动作
• 在控制器操作中,应用授权规则。
• 使用 [Authorize] 具有策略或角色的属性。
9、中间件结果处理
• 自定义如何处理授权结果(例如,401 未授权或 403 禁止响应)。
• 您可以创建一个 AuthorizationMiddlewareResultHandler 来管理此行为
让我们继续进行 Web API 的实际编码:
Program.cs: 按原样使用 Azure AD 身份验证。确定授权的权限密钥。
AddPolicy 方法中只有一个策略
//In Program.cs file, add the below code
var builder = WebApplication.CreateBuilder(args);
// Authentication using Microsoft Identity Platform
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));
// Default policy-based authorization
services.AddAuthorizationCore(options =>
{
options.DefaultPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.RequireClaim("preferred_username")
.RequireScope("user_impersonation")
.Build();
options.AddPolicy("Permission1", policy =>
policy.Requirements.Add(new PermissionAuthorizationRequirement("Permission1")));
options.AddPolicy("Permission2", policy =>
policy.Requirements.Add(new PermissionAuthorizationRequirement("Permission2")));
});
// Authorization handler
services.AddScoped<IAuthorizationHandler, AuthorizationHandler>();
// Middleware result handler for response errors (401 or 403)
services.AddScoped<IAuthorizationMiddlewareResultHandler, AuthorizationMiddlewareResultHandler>();
// Other services and configurations...
PermissionAuthorizationRequirement .cs: 只需复制并粘贴要求
//Create new PermissionAuthorizationRequirement.cs file for Custom requirement for permission-based authorization
public class PermissionAuthorizationRequirement : IAuthorizationRequirement
{
public PermissionAuthorizationRequirement(string allowedPermission)
{
AllowedPermission = allowedPermission;
}
public string AllowedPermission { get; }
}
AuthorizationHandler.cs:( 只需复制并粘贴代码。确保使用应用程序管理器点击 DB 调用以获取权限列表)
// Custom authorization handler to check user permissions
public class AuthorizationHandler : AuthorizationHandler<PermissionAuthorizationRequirement>
{
// Business layer service for user-related operations
private readonly IAppManager _appManager;
public AuthorizationHandler(IAppManager appManager)
{
_appManager= appManager;
}
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionAuthorizationRequirement requirement)
{
// Find the preferred_username claim
var preferredUsernameClaim = context.User.Claims.FirstOrDefault(c => c.Type == "preferred_username");
if (preferredUsernameClaim is null)
{
// User is not authenticated
context.Fail(new AuthorizationFailureReason(this, "UnAuthenticated"));
return;
}
// Call the business layer method to check if the user exists
var user = await _appManager.GetUserRolesAndPermissions(preferredUsernameClaim);
if (user is null || !user.IsActive)
{
// User does not exist or is inactive
context.Fail(new AuthorizationFailureReason(this, "UnAuthenticated"));
return;
}
// Select the list of permissions that the user is assigned
// Here you will fetch the Permission1 and Permission2
var userPermissions = user.UserPermissions?.Select(k => k.PermissionKey);
// Get the current permission key from the controller's action method
string allowedPermission = requirement.AllowedPermission;
// Check if the current request carries this permission
if (userPermissions.Any(permission => permission == allowedPermission))
{
// Permission granted
context.Succeed(requirement);
return;
}
// Permission denied
context.Fail();
}
}
AuthorizationMiddlewareResultHandler.cs: 只需复制粘贴即可。无需任何更改。
// AuthorizationMiddlewareResultHandler to decide response code (401 or success)
public class AuthorizationMiddlewareResultHandler : IAuthorizationMiddlewareResultHandler
{
private readonly ILogger<AuthorizationMiddlewareResultHandler> _logger;
private readonly Microsoft.AspNetCore.Authorization.Policy.AuthorizationMiddlewareResultHandler _defaultHandler = new();
public AuthorizationMiddlewareResultHandler(ILogger<AuthorizationMiddlewareResultHandler> logger)
{
_logger = logger;
}
public async Task HandleAsync(
RequestDelegate next,
HttpContext context,
AuthorizationPolicy policy,
PolicyAuthorizationResult authorizeResult)
{
var authorizationFailureReason = authorizeResult.AuthorizationFailure?.FailureReasons.FirstOrDefault();
var message = authorizationFailureReason?.Message;
if (string.Equals(message, "UnAuthenticated", StringComparison.CurrentCultureIgnoreCase))
{
// Set response status code to 401 (Unauthorized)
context.Response.StatusCode = "401";
_logger.LogInformation("401 failed authentication");
return;
}
// If not unauthorized, continue with default handler
await _defaultHandler.HandleAsync(next, context, policy, authorizeResult);
}
}
Controller.cs: 最后,在仪表板控制器中添加 [Authorize] 和 [Policies] 属性。这里定义Permission1和Permission2。
// Dashboard controller
[Authorize]
[Route("api/[controller]")]
[ApiController]
public class DashboardController : ControllerBase
{
[HttpGet]
[Route("get-dashboardDetails")]
[Authorize(Policy = "Permission1")]
public async Task GetAllDashboardDetailsAsync()
{
// Your logic for fetching all user details
return await GetAllDashboardDetailsAsync();
}
[HttpGet]
[Route("create-product")]
[Authorize(Policy = "Permission2")]
//[Authorize(Policy = nameof(Read string from ENUM))]
public async Task CreateProductAsync([FromBody] Product product)
{
// Your logic for creating a new product
}
}
现在,如果您想在 Web API 代码中结合使用基于角色的授权,请按照以下步骤操作,或者您也可以跳过此步骤。它与我们基于策略的授权的步骤几乎相同,只有一些细微的差别,您可以在代码中发现。
一. 注册 RoleAuthorizationHandler
在您的 Program.cs 文件中,只需添加以下行即可注册 RoleAuthorizationHandler
builder.Services.AddScoped<IAuthorizationHandler, RoleAuthorizationHandler>();
二. RoleAuthorizationHandler
下面是 RoleAuthorizationHandler.cs 检查用户是否具有所需角色的处理器。再创建一个处理器
public class RoleAuthorizationHandler : AuthorizationHandler<RolesAuthorizationRequirement>
{
private readonly IAppManager _appManager;
public RoleAuthorizationHandler(IAppManager appManager)
{
_appManager= appManager;
}
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, RolesAuthorizationRequirement requirement)
{
// Find the preferred_username claim
Claim claim = context.User.Claims.FirstOrDefault(c => c.Type == "preferred_username");
if (claim is not null)
{
// Get user details
var userRoles = await _appManager.GetUserRolesAndPermissions(claim.Value);
// Check if the user's roles match the allowed roles
var roles = requirement.AllowedRoles;
if (userRoles .Any(x => roles.Contains(x)))
{
context.Succeed(requirement); // User has the required roles
}
else
{
context.Fail(); // User does not have the required roles
return;
}
}
await Task.CompletedTask;
}
}
三. 在 DashboardController 中的使用
在你的 DashboardController 中DashboardController,你可以同时使用角色和策略进行授权。例如:
[HttpGet]
[Route("get-dashboardDetails")]
[Authorize(Roles = "Super_Admin, Role_Administrator")] // Can have multiple roles. You can choose either Roles or Policy or both
[Authorize(Policy = "Permission1")] // Can have only one policy per action to accept.
public async Task GetAllDashboardDetailsAsync()
{
// Your logic for fetching all user details
return await GetAllDashboardDetailsAsync();
}
就这样。你的 Web API 将会像魔法一样运行。:-)。
如果您喜欢此文章,请收藏、点赞、评论,谢谢,祝您快乐每一天。