Для вашего первого случая, потому что в действии вообще нет параметров, будет более целесообразно возвращать учеников, которые доступны для учителя, или вообще не иметь учеников, если никто не изучает все предметы определенного учителя, поэтому403 не нужны в этом случае.Вы можете передать User
из контроллера или ввести HttpContextAssessor в StudentService и использовать его для фильтрации.
как и во втором случае, идеальная ситуация - вернуть 403, если SubjectId
не имеет отношения к Teacher
в контексте.Если вы не возражаете против получения данных из базы данных для каждого запроса, вы можете использовать Требование в сочетании AuthorizationHandler в авторизации на основе политик, получая любые данные, необходимые для авторизации из базы данных, таким образом, чтобы определить,учитель имеет доступ к определенным предметам.Шаги для достижения этой цели:
Сначала настройте политику для отношения Teachers-Subjects и обработчики в 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);
}
}
}
}
Что касается класса Requirement, это просто пустой класс:
public class TeacherSubjectRequirement: IAuthorizationRequirement
{
}
Поскольку мы делаем механизм авторизации в AuthorizationHandler, мы можем оставить этот класс пустым.Но это все еще будет необходимо для авторизации на основе политик.
А затем, чтобы политика вступила в силу, добавьте атрибут к контроллеру
[Authorize(Policy = "TeacherSubject")]
public ActionResult<Subject> GetSubjectDetails(int subjectId)
{
//existing code
}
Но, честно говоря, я не пробовал помещать атрибут на основе политики в действие.Если это не работает, размещение атрибута в контроллере, безусловно, будет работать
Надеюсь, это поможет.