我需要检查数据库中特定对象中特定字段的权限。
让我们举个例子。我有模特叫Employee
public class Employee {
[Key]
public int EmployeeID { get; set; }
public string JobTitle { get; set; }
public string Description { get; set; }
public int Salary { get; set; } // <---- Restricted
public int BossID { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
我有几个案例:
我需要限制对特定领域的访问,Salary因为我不希望任何人看到彼此的薪水。但是 HR 可以看到任何人Salary并对其进行编辑。如果我是这个员工,我可以看到我自己的Salary,但不能编辑它。
每个人都可以看到彼此的职位名称,但只有 HR 可以编辑它。还有那个员工的老板,可以编辑,员工自己不能。
用例:
我是 RoleID 为 4 的经理。我想看看Salary我的Employee名字为 John Smith 的角色 ID为EmployeeID5。我可以做到。
我是 RoleID 4 的经理。我想看看Salary“Employee named Mark Twain withEmployeeID ” 8。Mark不是我的直接下属。他来自不同的分支。我不能这样做。
我是EmployeeID5 的员工,我想看看我的Salary. 这是允许的。
我是EmployeeID5名员工,我想编辑自己的Salary. 这是禁止的。我收到 HTTP 错误 401。
我是人力资源的。我可以查看和编辑Salary公司中的所有员工。
我想到了这样的事情:
public class Access {
[Required]
public int RoleID { get; set; }
[Required]
public string TableName { get; set; }
[Required]
public string ColumnName { get; set; }
[Required]
public int RowID { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
然后(按Authorize属性)检查特定角色(老板、HR 或其他角色)是否可以访问特定字段(例如Salary)以获取特定数据(例如Employeeid 22)。顺便说一下,这是很多“具体”。
我该怎么做?我的想法“OK”吗?
如果逻辑不太复杂或更通用,则可以设置自定义输出格式化程序以防止将某些字段写入响应。
该方法有以下问题:
Startup,那么就应该调用让我们看一个例子。可能有一个自定义属性,如
public class AuthorizePropertyAttribute : Attribute
{
public AuthorizePropertyAttribute(string role) => Role = role;
public string Role { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
然后输出格式化程序可能是这样的:
public class AuthFormatter : TextOutputFormatter
{
public AuthFormatter()
{
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/json"));
SupportedEncodings.Add(Encoding.UTF8);
}
public override async Task WriteResponseBodyAsync(OutputFormatterWriteContext context,
Encoding selectedEncoding)
{
var settings = new JsonSerializerSettings
{
ContractResolver = new AuthorizedPropertyContractResolver(context.HttpContext.User)
};
await context.HttpContext.Response.WriteAsync(
JsonConvert.SerializeObject(context.Object, settings));
}
}
Run Code Online (Sandbox Code Playgroud)
那将需要
public class AuthorizedPropertyContractResolver : DefaultContractResolver
{
public AuthorizedPropertyContractResolver(ClaimsPrincipal user)
{
User = user;
}
public ClaimsPrincipal User { get; }
protected override JsonProperty CreateProperty(MemberInfo member,
MemberSerialization memberSerialization)
{
var result = base.CreateProperty(member, memberSerialization);
result.ShouldSerialize = e =>
{
var role = member.GetCustomAttribute<AuthorizePropertyAttribute>()?.Role;
return string.IsNullOrWhiteSpace(role) ? true : User.IsInRole(role);
};
return result;
}
}
Run Code Online (Sandbox Code Playgroud)
登记:
services.AddMvc(options =>
{
options.OutputFormatters.Insert(0, new AuthFormatter());
});
Run Code Online (Sandbox Code Playgroud)
在这种情况下,简单用户的 Response 将缺少 Salary 字段{"Id":1,"Name":"John"},同时经理将看到完整的响应
{"Id":1,"Name":"John","Salary":100000},当然属性“Salary”应该设置属性
[AuthorizeProperty("Boss")]
public double Salary { get; set; }
Run Code Online (Sandbox Code Playgroud)
您应该实施两种不同的方法。一种是供HR索取数据时使用,另一种是供简单用户使用。那么您永远不应该返回整个对象 (json),而是创建一些保存所需数据的 DTO(数据传输对象)。那么让我们举个例子:
public class DTOGetEmployeeByEmployee {
public int EmployeeID { get; set; }
public string JobTitle { get; set; }
public string Description { get; set; }
public int BossID { get; set; }
}
public class DTOGetEmployeeByHR {
public int EmployeeID { get; set; }
public string JobTitle { get; set; }
public string Description { get; set; }
public int Salary { get; set; }
public int BossID { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
一旦用户请求该员工,就从数据库中获取它,然后将其转换为所需的 DTO。到目前为止我看到的最好的方法是使用 AutoMapper 来做到这一点:
Mapper.Map<DTOxxxx>(yourObject);
Run Code Online (Sandbox Code Playgroud)
您还可以使用[授权]属性来检查用户是HR还是员工。我结合 JWT-Token 多次执行此操作。
public class EmployeeController
{
[Authorize("HR")]
[HttpGet, Route("GetForHR")]
public IActionResult Get(int employeeID)
{
// Note: this is just a sample out of my head, so there will be adjustments needed in order to run that
// Check if the HR is allowed to access the Employees data
// Get the Employee by its ID
var emp = ...;
// Convert it to the DTO
var dto = Mapper.Map<DTOGetEmployee>(emp);
// return the dto
return Ok(dto);
}
}
Run Code Online (Sandbox Code Playgroud)
我打赌有很多更好的解决方案,但对我来说,这非常简单,很容易在其他应用程序中重新实现,并且没有明显的性能损失
| 归档时间: |
|
| 查看次数: |
1378 次 |
| 最近记录: |