我需要检查对数据库中特定对象中特定字段的特权。
让我们做例子。我有一个名为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; }
}
我有几种情况:
我需要限制对特定字段Salary
的访问,因为我不希望任何人看到彼此的薪水。但是,HR可以看到任何人Salary
并对其进行编辑。如果我是此员工,我可以看到自己的Salary
,但无法对其进行编辑。
每个人都可以看到彼此的职务,但只有HR可以编辑。而且该员工的老板可以编辑,而员工本人则不能。
用例:
我是RoleID 4的管理员。我想看到我的Salary
中名为Employee
5的约翰·史密斯(John Smith)中的EmployeeID
。
我是RoleID 4的经理。我想查看'Employee Salary
EmployeeID`的named Mark Twain with
。8. Mark不是我的直接下属。他来自不同的部门。我不能那样做。
我是EmployeeID
5的员工,我想看看我的Salary
。允许。
我是EmployeeID
5的员工,我想编辑自己的Salary
。这是禁止的。我收到HTTP错误401。
我来自HR。我可以查看和编辑公司中所有员工的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; }
}
然后检查(通过Authorize
属性),以了解特定角色(老板,HR或其他人员)是否可以访问特定字段(例如Salary
)的特定数据(例如Employee
) ID 22)。顺便说一下,这是很多“特定的”。
我应该怎么做?我的想法好吗?
答案 0 :(得分:5)
如果逻辑不太复杂或更通用,可以设置custom output formatter来防止某些字段写入Respose。
该方法还有下一个问题:
mvn dependency:tree
中设置了特定的序列化设置,则应该进行传输让我们看一个例子。 可能有一个自定义属性,如
Startup
然后输出格式化程序可能像:
public class AuthorizePropertyAttribute : Attribute
{
public AuthorizePropertyAttribute(string role) => Role = role;
public string Role { get; set; }
}
那需要
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));
}
}
注册:
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;
}
}
在这种情况下,对简单用户的响应将缺少Salary字段services.AddMvc(options =>
{
options.OutputFormatters.Insert(0, new AuthFormatter());
});
,同时经理将看到完整的响应
{"Id":1,"Name":"John"}
,当然,“工资”属性应设置属性
{"Id":1,"Name":"John","Salary":100000}
答案 1 :(得分:1)
您应该实现2种不同的方法。一个用于请求数据时的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; }
}
一旦用户请求该员工,请从数据库中获取该员工,然后将其转换为所需的DTO。到目前为止,我看到的最好的方法是使用AutoMapper来做到这一点:
Mapper.Map<DTOxxxx>(yourObject);
您还可以使用[Authorize]属性来检查用户是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);
}
}
我敢肯定有很多更好的解决方案,但是对我来说,这非常简单,在其他应用程序中难以实现,并且不会出现明显的性能损失