Car*_*ens 5 c# authentication user-roles asp.net-core-mvc asp.net-core
ASP.NET Core 结合 Identity 已经提供了一种在登录后检查角色的简单方法,但我想在每次控制器操作之前查询当前用户的当前角色的数据库。
我已经阅读了 Microsoft 提供的基于角色、基于策略和基于声明的授权。( https://docs.microsoft.com/en-us/aspnet/core/security/authorization/introduction ) 这些解决方案似乎都没有检查每个操作的角色。这是我以某种基于策略的授权的形式实现预期结果的最新尝试:
在 Startup.cs 中:
DatabaseContext context = new DatabaseContext();
services.AddAuthorization(options =>
{
options.AddPolicy("IsManager",
policy => policy.Requirements.Add(new IsManagerRequirement(context)));
options.AddPolicy("IsAdmin",
policy => policy.Requirements.Add(new IsAdminRequirement(context)));
});
Run Code Online (Sandbox Code Playgroud)
在我的需求文件中:
public class IsAdminRequirement : IAuthorizationRequirement
{
public IsAdminRequirement(DatabaseContext context)
{
_context = context;
}
public DatabaseContext _context { get; set; }
}
public class IsAdminHandler : AuthorizationHandler<IsAdminRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, IsAdminRequirement requirement)
{
// Enumerate all current users roles
int userId = Int32.Parse(context.User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier).Value);
Roles adminRoles = requirement._context.Roles.FirstOrDefault(r => r.Name == "Administrator" && r.IsActive == true);
bool hasRole = requirement._context.UserRoles.Any(ur => ur.UserId == userId && adminRoles.Id == ur.RoleId && ur.IsActive == true);
// Check for the correct role
if (hasRole)
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
Run Code Online (Sandbox Code Playgroud)
并在控制器中:
[HttpGet]
[Authorize(Policy = "IsAdmin")]
public async Task<IActionResult> Location()
{
// do action here
}
Run Code Online (Sandbox Code Playgroud)
使用此代码,需求中间件永远不会被调用,因此永远不会检查数据库。
在执行每个控制器操作之前,我将如何正确查询数据库以检查当前用户的角色?
我通过处理 OnTokenValidated 事件在我的应用程序(SignalR + JwtBearer)中解决了这个问题。我只是将声明中的角色与我的数据库中的角色进行检查。如果它们不再有效,我将 TokenValidatedContext 设置为失败。
以下是我的 ASP.NET Core Startup.cs 的摘录:
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(x =>
{
x.Events = new JwtBearerEvents
{
OnTokenValidated = async context =>
{
var userService = context.HttpContext.RequestServices.GetRequiredService<IUserRoleStore<User>>();
var username = context.Principal.Identity.Name;
var user = await userService.FindByNameAsync(username, CancellationToken.None);
if (user == null)
{
// return unauthorized if user no longer exists
context.Fail("Unauthorized");
}
else
{
// Check if the roles are still valid.
var roles = await userService.GetRolesAsync(user, CancellationToken.None);
foreach (var roleClaim in context.Principal.Claims.Where(p => p.Type == ClaimTypes.Role))
{
if (roles.All(p => p != roleClaim.Value))
{
context.Fail("Unauthorized");
return;
}
}
context.Success();
}
},
OnMessageReceived = context =>
{
var accessToken = context.Request.Query["access_token"];
// If the request is for our hub...
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/hubs"))
{
// Read the token out of the query string
context.Token = accessToken;
}
return Task.CompletedTask;
}
};
x.RequireHttpsMetadata = false;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false
};
});
Run Code Online (Sandbox Code Playgroud)