代码之家  ›  专栏  ›  技术社区  ›  Morasiu

如何在ASP.Net内核中对特定对象中的特定字段进行授权?

  •  1
  • Morasiu  · 技术社区  · 7 年前

    我需要检查数据库中特定对象中特定字段的权限。

    让我们做个例子。我叫了一个模特 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; }
    }
    

    我有几个案子:

    1. Salary 因为我不想让任何人看到彼此。但是HR可以看到任何人 然后编辑它。如果我是这个雇员,我可以看到我自己的 工资

    2. 每个人都可以看到对方的职称,但只有HR可以编辑。而且也是那个员工的老板,可以编辑,由员工自己不能。

    用例:

    • 我是RoleID 4的经理。我想见你 工资 我的 雇员 EmployeeID

    • 工资 员工的 named Mark Twain with

    • 我是公司的员工 员工编号 我想看看我的孩子 . 这是允许的。

    • 员工编号 工资 . 这是禁止的。我得到HTTP错误401。

    • 我是人力资源部的。我可以查看和编辑 工资 在公司所有员工中。

    我想到了这样的事情:

    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或其他角色)有权访问特定字段(例如 工资 雇员 id为22)。顺便说一句,这是非常“具体”的。

    我该怎么做?我的主意行吗?

    1 回复  |  直到 7 年前
        1
  •  4
  •   DirtyNative    7 年前

    您应该实现两种不同的方法。一个用于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; }
    }
    

    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);
        }
    }
    

    我敢打赌有很多更好的解决方案,但对我来说,这非常简单,很容易在其他应用程序中重新实现,而且没有明显的性能损失

        2
  •  6
  •   ASpirin    6 年前

    如果逻辑不那么复杂或更通用,可以设置 custom output formatter 防止某些字段写入响应。

    1. 替换默认序列化。因此,如果在中设置了特定的序列化设置 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;
        }
    }
    

    注册:

    services.AddMvc(options =>
    {
        options.OutputFormatters.Insert(0, new AuthFormatter());
    });
    

    在这种情况下,简单用户的响应将缺少Salary字段 {"Id":1,"Name":"John"} 同时,经理将看到完整的答复 {"Id":1,"Name":"John","Salary":100000} ,当然属性“Salary”应该设置属性

    [AuthorizeProperty("Boss")]
    public double Salary { get; set; }