c# - 如何在应用程序中实现组的安全性
问题描述
我正在开发一个具有身份安全性的 API,该应用程序将具有组/页面(实体),并且每个实体将具有超级管理员、管理员和其他角色。对于整个应用程序角色来说,实施安全性是直接的,但我如何为组实施安全性?我有如下检查用户角色的代码
**存储库类**
public class UserEntityRoleRepository : RepositoryBase<UserEntityRole>, IUserEntityRoleRepository
{
public UserEntityRoleRepository(ARMASDataContext aRMASDataContext) : base(aRMASDataContext)
{
}
public async Task<bool> isEntitySuperAdmin(string entityId, string userId)
{
var entityRole = await this.ARMASDataContext.EntityRoles.FirstOrDefaultAsync(c => c.Name == "Entity Super Admin");
return await this.hasRequiredRole(entityRole.Id, entityId, userId);
}
public async Task<bool> isEntityAdmin(string entityId, string userId)
{
var entityRole = await this.ARMASDataContext.EntityRoles.FirstOrDefaultAsync(c => c.Name == "Entity Admin");
return await this.hasRequiredRole(entityRole.Id, entityId, userId);
}
public async Task<bool> isEntityHROfficer(string entityId, string userId)
{
var entityRole = await this.ARMASDataContext.EntityRoles.FirstOrDefaultAsync(c => c.Name == "Entity HR Officer");
return await this.hasRequiredRole(entityRole.Id, entityId, userId);
}
public async Task<bool> isEntityAccountant(string entityId, string userId)
{
var entityRole = await this.ARMASDataContext.EntityRoles.FirstOrDefaultAsync(c => c.Name == "Entity Accounts");
return await this.hasRequiredRole(entityRole.Id, entityId, userId);
}
public async Task<bool> isEntityAuditor(string entityId, string userId)
{
var entityRole = await this.ARMASDataContext.EntityRoles.FirstOrDefaultAsync(c => c.Name == "Entity Auditor");
return await this.hasRequiredRole(entityRole.Id, entityId, userId);
}
public async Task<bool> isEntityConsultant(string entityId, string userId)
{
var entityRole = await this.ARMASDataContext.EntityRoles.FirstOrDefaultAsync(c => c.Name == "Entity Consultant");
return await this.hasRequiredRole(entityRole.Id, entityId, userId);
}
private async Task<bool> hasRequiredRole(string entityRoleId, string entityId, string userId)
{
/*The following code is used to check if the provided EntityRoleId maps to row with User's
Id and Entity's Id in UserEntity table through middle table UserEntityRole
*/
return await this.ARMASDataContext.UserEntityRoles.Where(c => c.EntityRoleId == entityRoleId)
.Include(c => c.UserEntity)
.Where(c => c.UserEntity.UserId == userId && c.UserEntity.EntityId == entityId)
.Select(c => new { c.Id })
.AsQueryable().CountAsync() > 0;
}
}
**服务等级**
public class EntityService : IEntityService
{
.........
public async Task<bool> TestEntityRoleCheckSuperAdmin(string entityId, string userId)
{
return await _unitOfWork.UserEntityRole.isEntitySuperAdmin(entityId, userId);
}
public async Task<bool> TestEntityRoleCheckAdmin(string entityId, string userId)
{
return await _unitOfWork.UserEntityRole.isEntityAdmin(entityId, userId);
}
........
}
和控制器
public class EntityController : ControllerBase
{
............
// POST api/<EntityController>
[Authorize]
[HttpPost("RoleTestSuperAdmin")]
public async Task<ActionResult<string>> RoleTestSuperAdmin()
{
string userId = User.FindFirstValue(ClaimTypes.NameIdentifier); // will give the user's userId
string entityId = "e90fb3ab-347d-450f-ab9b-0cb187a22ae1";
var hasRole = await _entityService.TestEntityRoleCheckSuperAdmin(entityId, userId);
if (hasRole)
{
string message = "User Has the required role";
var result = new OkObjectResult(new {message});
return result;
}
return Unauthorized();
}
[Authorize]
[HttpPost("RoleTestAdmin")]
public async Task<IActionResult> RoleTestAdmin()
{
string userId = User.FindFirstValue(ClaimTypes.NameIdentifier); // will give the user's userId
string entityId = "e90fb3ab-347d-450f-ab9b-0cb187a22ae1";
var hasRole = await _entityService.TestEntityRoleAdmin(entityId, userId);
if (hasRole)
{
return Ok(StatusCode(StatusCodes.Status200OK, new Response { Status = "Success", Message = "The account has The Requested Role" }));
}
return Unauthorized();
}
........
}
上面的代码工作得很好,但是有没有办法将它作为带有 UserId 和 EntityId 参数的策略来实现?
解决方案
您可以编写自定义策略处理程序并在应用程序中实现基于策略的授权。
例如:
创建一个 CustomUserEntityRoleClaim.cs 文件:它可以用来传输角色名称。
public class CustomUserEntityRoleClaim: IAuthorizationRequirement
{
public string RoleName { get; }
public CustomUserEntityRoleClaim(string roleName)
{
RoleName = roleName;
}
}
创建一个 CustomUserEntityRoleHandler:在这个处理程序中,您可以调用您的 UserEntityRoleRepository 服务并检查用户的角色。
public class CustomUserEntityRoleHandler : AuthorizationHandler<CustomUserEntityRoleClaim>
{
private readonly ApplicationDbContext _context;
public CustomUserEntityRoleHandler(ApplicationDbContext context)
{
_context = context;
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, CustomUserEntityRoleClaim requirement)
{
if (context.User == null || !context.User.Identity.IsAuthenticated)
{
context.Fail();
return Task.CompletedTask;
}
//get current user id
var userid = _context.ApplicationUsers.Where(c=>c.UserName == context.User.Identity.Name).FirstOrDefault().Id;
//according to the role name to find the role id.
var roleid = _context.EntityRoles.Where(c=>c.EntityRoleName== requirement.RoleName).Select(c=>c.EntityRoleId).FirstOrDefault();
var validRole = false;
//According to the userid and roleid to query the UserEntityRoles table, then based on the result to verify whether the user has the special roles.
var result = _context.UserEntityRoles.Where(c => c.UserId == userid && c.EntityRoleId == roleid).ToList();
if (result.Count>0)
{
validRole = true;
}
if (validRole)
{
context.Succeed(requirement);
}
else
{
context.Fail();
}
return Task.CompletedTask;
}
}
然后,在 Startup.ConfigureServices 方法中,注册 CustomUserEntityRoleHandler,并配置策略。
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDatabaseDeveloperPageExceptionFilter();
services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
services.ConfigureApplicationCookie(options =>
{
// Cookie settings
options.Cookie.HttpOnly = true;
options.ExpireTimeSpan = TimeSpan.FromMinutes(5);
options.LoginPath = "/Identity/Account/Login";
options.AccessDeniedPath = "/Login/UserAccessDenied"; // change the path to yours.
options.SlidingExpiration = true;
});
//register the CustomUserEntityRoleHandler
services.AddScoped<IAuthorizationHandler, CustomUserEntityRoleHandler>();
//configure the policy.
services.AddAuthorization(options =>
{
options.AddPolicy("AdminPolicy", policy =>
policy.Requirements.Add(new CustomUserEntityRoleClaim("Admin")));
});
services.AddControllersWithViews();
}
之后,在控制器中,您可以[Authorize]
使用策略添加属性:
[Authorize(Policy ="AdminPolicy")]
public IActionResult Privacy()
{
return View();
}
截图如下:
有关创建基于策略的授权的更多详细信息,请查看以下文章:
ASP.NET Core 3.0 中基于策略和基于角色的授权使用自定义处理程序
[更新]
要为应用程序和实体添加多个角色,您可以尝试更改 CustomUserEntityRoleClaim 类,如下所示:
public class CustomUserEntityRoleClaim: IAuthorizationRequirement
{
public string ApplicationRoleName { get; }
public string EntityRoleName { get; set; }
public CustomUserEntityRoleClaim(string approleName, string entityroleName)
{
ApplicationRoleName = approleName;
EntityRoleName = entityroleName;
}
}
然后,参考以下代码来配置策略:
services.AddAuthorization(options =>
{
options.AddPolicy("AdminPolicy", policy =>
policy.Requirements.Add(new CustomUserEntityRoleClaim("applicationRole", "entityrole")));
});
并且,在CustomUserEntityRoleHandler的方法中,你可以访问ApplicationRoleName
property和EntityRoleName
property,然后查询数据库,检查用户是否有权限访问资源。
推荐阅读
- reactjs - 如何在反应中加载图像(找不到错误模块)?
- c# - 在 Unity3D 中不使用 IDropHandler 触发 Drop 事件
- powershell - 如何将多行字符串导出为单行文本
- css - 查看编译的 CSS 时如何删除知道 CSS 来自哪个文件?
- enums - 如何键入其键只能是枚举成员的哈希表?
- slurm - slurmd 无法与 slurmctld 通信
- javascript - 无法读取未定义的属性“toFixed”。未捕获的类型错误
- git - “git push --force --follow-tags”在使用强制时不推送标签
- javascript - 用于突出显示文本和从文件重新加载突出显示的 React 组件
- php - 在 WAMP (WAMP64) 中 CLI PHP 的正确方法,同时在 Windows 10 上的多个 PHP 版本之间切换