如果特定参数无法满足要求,则限制授权或减少结果

时间:2019-05-27 11:07:18

标签: c# asp.net-core .net-core

通过软件描述大学时,我遇到了身份验证问题。

以前,我只有一个Headmaster角色,可以执行任何操作。 但是就目前而言,我需要集成一个Teacher角色。

Teacher角色应该具有访问某些功能的选项,可以通过Authorize属性轻松地对其进行限制。但是在某些情况下,我想减少此角色允许访问的数据数量,例如并非宇宙的所有学生,而是学习Teacher's Subject的学生。

所有这些都已经在EF中进行了描述(例如,教师与受试者,学科与学生之间的关系)。但是,现在我很难拒绝(返回403)对Teacher不允许访问的学科或学生的请求。

我考虑过我的服务使用规范模式的原因,因此将使用规范的过滤器减少生成的数据,因为它有助于减少数据量,有时减少为无数据,但并不能完全拒绝请求

能否请您提供一个链接或体系结构的想法,以满足对上面指定的两个用例的期望?

// entity models
class Subject {
    ...
    public Teacher Teacher { get; set; }
    public List<Students> { get; set; }
    ...
}

class Teacher {
    ...
    public List<Subject> Subjects { get; set; }
    ...
}
class Student {
    ...
    public List<Subject> StudiedSubjects {get; set; }
    ...
}

// first use-case I want to solve
public ActionResult<List<Student>> GetStudent()
{
    // previously I just did
    return Ok(_studentsService.GetStudents());

    // but as for now in case of Teacher role accessed the method I want to
    // reduce the number of returned students
}

// second use-case I want to solve
public ActionResult<Subject> GetSubjectDetails(int subjectId)
{
    // previously I just did
    return Ok(_subjectService.GetSubject(subjectId);

    // but as for now in case of Teacher role I need to check whether its
    // allowed to get the subject and return Access denied in case its not
}

2 个答案:

答案 0 :(得分:2)

对于第一种情况,由于该操作根本没有任何参数,因此更有意义的是返回可被教师访问的学生,或者如果没有人接受某位老师的所有科目,则根本不返回任何学生,因此在这种情况下,不需要403。您可以将User从控制器或inject HttpContextAssessor传递给StudentService并将其用于过滤。

对于您的第二种情况,如果SubjectId与上下文中的Teacher不相关,则返回403的理想情况。如果您不介意为每个请求从数据库获取数据,则可以在基于策略的授权中使用RequirementAuthorizationHandler的组合,方法是从数据库中检索您需要的授权数据,从而确定教师是否有权访问某些主题。实现它的步骤:

首先在Startup.ConfigureServices中为教师-主题关系和处理程序设置策略:

services.AddAuthorization(options =>
{
    options.AddPolicy("TeacherSubject", policy => policy.Requirements.Add( new TeacherSubjectRequirement() ));
});
services.AddScoped<IAuthorizationHandler, TeacherSubjectHandler>();

接下来为该策略创建AuthorizationHandler:

public class TeacherSubjectHandler : AuthorizationHandler<TeacherSubjectRequirement>
{
    readonly IHttpContextAccessor _contextAccessor;
    readonly UserManager<AppUser> _usermanager;
    readonly UserToTeacherService _userToTeacherService;

    public ThePolicyAuthorizationHandler(IHttpContextAccessor c, UserManager<AppUser> u, _userToTeacherService s)
    {
        _contextAccessor = c;
        _userManager = u;
        _userToTeacherService = s;
    }

    protected override async Task HandleRequirementAsync(AuthorizationHandlerContext authHandlerContext, TeacherSubjectRequirement requirement)
    {
        var user = _userManager.GetUserAsync(_contextAccessor.HttpContext.User);
        var teacher = _userToTeacherService(user); //I assume this service will also retrieve teacher's subjects
        var subjectIds = teacher.Subjects.Select(s => s.SubjectId).ToList();

        if (context.Resource is AuthorizationFilterContext filterContext)
        {
            var subjectIdStr = filterContext.RouteData.Values["id"].ToString();
            if ( int.TryParse(subjectIdStr, out var subjectId) && subjectIds.Contains(subjectId) )
            {
                context.Succeed(requirement);
            }
            else
            {
                context.Fail();
            }               
        } 
        else
        {
            context.Fail();
        }       
    }
}

对于Requirement类,它只是一个空类:

public class TeacherSubjectRequirement: IAuthorizationRequirement
{

}

由于我们正在AuthorizationHandler中进行授权机制,因此可以将此类保留为空。但是,基于策略的授权仍然需要工作。

然后要使该策略生效,请向控制器添加属性

[Authorize(Policy = "TeacherSubject")]
public ActionResult<Subject> GetSubjectDetails(int subjectId)
{
    //existing code
}

但是,老实说,我没有尝试将基于策略的属性放入Action中。如果这不起作用,则将属性放入控制器肯定会起作用。

希望这会有所帮助。

答案 1 :(得分:0)

您的情况非常实际,这使问题变得非常有趣。但是,很高兴看看这个documentation

使用声明使教师可以访问他们必须能够访问的信息。您可以将索赔另存为。 “ TeacherName-TargetInforName他们可以访问”;您可以根据老师应获取的信息量来拥有尽可能多的主张。

这对学生也是一样。您可以为该讲师班级下的学生创建索赔。就像说:“ StudentName-LectureName”,然后您可以通过检查学生是否声称自己属于特定的讲师班来进行认证。