英文:
How to fully implement authorization in asp.net core web api with entity framework core?
问题
您的代码主要涉及.NET Core Web API的授权部分,您的问题似乎是关于如何动态地根据数据库中的策略进行授权。以下是您的代码的翻译部分:
// Program.cs:
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("UserListRead", policy => policy.Requirements.Add(new arkiva_be.Services.User_Service.UserListRequirement()));
});
builder.Services.AddScoped<Microsoft.AspNetCore.Authorization.IAuthorizationHandler, UserListRequirementHandler>();
// UserListReadRequirement.cs
public class UserListReadRequirement : IAuthorizationRequirement
{
public readonly DataContext _context;
}
public class UserListRequirementHandler : AuthorizationHandler<UserListReadRequirement>
{
// 构造函数
// ...
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, UserListReadRequirement requirement)
{
// 在这里处理授权逻辑
// ...
return Task.CompletedTask;
}
}
您的问题似乎是关于如何在 UserListRequirementHandler
中访问策略(例如 "UserListRead")。在您的代码中,策略名称是硬编码的,但您想要从数据库中动态读取策略。
要做到这一点,您可以在 UserListRequirementHandler
中注入 DbContext
,然后在 HandleRequirementAsync
方法中从数据库中检查策略是否存在。这里是一个示例:
public class UserListRequirementHandler : AuthorizationHandler<UserListReadRequirement>
{
private readonly DataContext _context;
// 其他依赖项...
public UserListRequirementHandler(DataContext context, /*其他依赖项...*/)
{
_context = context;
// 初始化其他依赖项...
}
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, UserListReadRequirement requirement)
{
// 获取当前策略名称
string policyName = "UserListRead"; // 从某处动态获取策略名称
// 检查策略是否存在于数据库
bool policyExists = await _context.Policies.AnyAsync(p => p.Name == policyName);
if (policyExists)
{
// 授权成功,返回result 200
context.Succeed(requirement);
}
else
{
// 授权失败,返回result 401
context.Fail();
}
}
}
这样,您可以根据数据库中的策略动态进行授权。希望这有助于解决您的问题。如果您还有其他问题,请随时提出。
英文:
I have implemented authentication for my .net core web api with entity framework core v6.0 but now I am stuck in the authorization part.
I want to implement it in that way that I will read some Policies from my mssql database and then in my RequirementHandler to compare those Policies if the assigned policy to a method i.e: [Authorize(Policy="UserListRead")]
it exists in my mssql database or not.
If yes, then return result 200
, if not then result 401
.
My code is like this:
Program.cs:
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("UserListRead", policy => policy.Requirements.Add(new arkiva_be.Services.User_Service.UserListRequirement()));
});
builder.Services.AddScoped<Microsoft.AspNetCore.Authorization.IAuthorizationHandler, UserListRequirementHandler>();
UserListReadRequirement.cs
public class UserListReadRequirement:IAuthorizationRequirement
{
public readonly DataContext _context;
}
public class UserListRequirementHandler : AuthorizationHandler<UserListReadRequirement>
{
private readonly DataContext _context;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IAuthService _authService;
private readonly Microsoft.AspNetCore.Identity.RoleManager<Roles> _roleManager;
private readonly IConfiguration _configuration;
private readonly Microsoft.AspNetCore.Identity.UserManager<UserArkiva> _userManager;
public UserListRequirementHandler(DataContext context, IHttpContextAccessor httpContextAccessor,
IAuthService authService,
Microsoft.AspNetCore.Identity.RoleManager<Roles> roleManager,
IConfiguration configuration,
Microsoft.AspNetCore.Identity.UserManager<UserArkiva> userManager
)
{
_context = context;
_httpContextAccessor = httpContextAccessor;
_authService = authService;
_roleManager = roleManager;
_configuration = configuration;
_userManager = userManager;
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, UserListRequirement requirement)
{
HttpContext httpContext = _httpContextAccessor.HttpContext;
string dto_username = _authService.GetUserNameFromToken(httpContext).Result.UserName;
List<CompanyWithPrivileges> TempList = new List<CompanyWithPrivileges>();
var cp = new CompanyWithPrivilegesMethod(_userManager,_roleManager, _configuration,_context);
TempList = cp.GetCompaniesWithPrivileges(dto_username);
return Task.CompletedTask;
}
}
Now in TempList I get to access all the privileges that the current logged in user has but I cannot compare it with the "UserListRead" policy because I don't know where to access it.
UserListRequirement requirement
parameter is always null.
I think I am missing something in program.cs because I have +50 privileges and I don't think it is a good idea to add them all like policies.
Also I am currently adding a claim in my user token authClaims.Add(new Claim("UserListRead", "UserListRead"));
but do I really need it? If I need this one, then I will need also 50+ other claims and then my token would be very long...
Any help would be much appreciated.
答案1
得分: 1
我同意这个观点。根据您的描述,您有50多个权限。如果为每个权限创建策略,您需要在Program.cs
中注册许多策略。所以在我看来,您可以自定义IAuthorizationFilter
并接收一个动态参数。在这种情况下,您不需要为每个权限注册那么多策略,只需将不同的权限传递给您自己的IAuthorizationFilter
。
例如,用户有权保存他的年龄。在我的控制器中,每个操作都有自己的年龄标准,只有当用户的年龄大于操作的年龄标准时,才能访问该操作。所以在这里,我不会为每个年龄标准创建策略,而是自定义IAuthorizationFilter
:
public class CustomAuthorizeAttribute : AuthorizeAttribute, IAuthorizationFilter
{
private readonly int Age;
public CustomAuthorizeAttribute(int age)
{
Age = age;
}
void IAuthorizationFilter.OnAuthorization(AuthorizationFilterContext context)
{
var userage = Int32.Parse(context.HttpContext.User.FindFirstValue("Age"));
// 这里您还可以获取您的 dbcontext。
// var dbcontext = context.HttpContext.RequestServices.GetService(typeof(DatabaseContext)) as DatabaseContext;
if (userage < Age) {
context.Result = new ForbidResult();
}
}
}
然后在操作中,我只需要将参数传递给这个属性:
[CustomAuthorize(xxx)]
您还可以参考这个使用IAuthorizationPolicyProvider在ASP.NET Core中创建自定义授权策略提供程序。
英文:
> I don't think it is a good idea to add them all like policies
I agree with this. From your description, You have 50+ privileges, If you create policy for each privilege, You need to register a lot of policies in your Program.cs
. So in my opinion, You can custom IAuthorizationFilter
and receive a dynamic parameter. In this case, You don't need to register so many policies for each privilege, You just need to pass different privilege to your own IAuthorizationFilter
.
For Example, User has a claim to save his age. In my controller, Each action has it's own age standard, Action can only be accessed when the user's age is greater than the age standard of the action. So here i don't create policies for each age standard, I custom IAuthorizationFilter
:
public class CustomAuthorizeAttribute : AuthorizeAttribute, IAuthorizationFilter
{
private readonly int Age;
public CustomAuthorizeAttribute(int age)
{
Age = age;
}
void IAuthorizationFilter.OnAuthorization(AuthorizationFilterContext context)
{
var userage = Int32.Parse(context.HttpContext.User.FindFirstValue("Age"));
//You can also get your dbcontext here.
//var dbcontext = context.HttpContext.RequestServices.GetService(typeof(DatabaseContext)) as DatabaseContext;
if (userage<Age) {
context.Result = new ForbidResult();
}
}
}
Then in action, I only need to pass parameter to this attribute
[CustomAuthorize(xxx)]
You can also refer to this Custom Authorization Policy Providers using IAuthorizationPolicyProvider in ASP.NET Core.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论