我试图使一个自定义授权属性在ASP。净的核心。在以前的版本中,可以重写bool AuthorizeCore(HttpContextBase httpContext)。但是这在AuthorizeAttribute中不再存在。
当前制作自定义AuthorizeAttribute的方法是什么?
我想要完成的:我正在头授权中接收会话ID。通过该ID,我将知道特定操作是否有效。
我试图使一个自定义授权属性在ASP。净的核心。在以前的版本中,可以重写bool AuthorizeCore(HttpContextBase httpContext)。但是这在AuthorizeAttribute中不再存在。
当前制作自定义AuthorizeAttribute的方法是什么?
我想要完成的:我正在头授权中接收会话ID。通过该ID,我将知道特定操作是否有效。
当前回答
为了在我们的应用程序中进行授权。我们必须根据在授权属性中传递的参数调用服务。
例如,如果我们想检查登录的医生是否可以查看病人的预约,我们将传递“View_Appointment”到自定义授权属性,并在DB服务中检查该权利,并根据结果进行审查。下面是这个场景的代码:
public class PatientAuthorizeAttribute : TypeFilterAttribute
{
public PatientAuthorizeAttribute(params PatientAccessRights[] right) : base(typeof(AuthFilter)) //PatientAccessRights is an enum
{
Arguments = new object[] { right };
}
private class AuthFilter : IActionFilter
{
PatientAccessRights[] right;
IAuthService authService;
public AuthFilter(IAuthService authService, PatientAccessRights[] right)
{
this.right = right;
this.authService = authService;
}
public void OnActionExecuted(ActionExecutedContext context)
{
}
public void OnActionExecuting(ActionExecutingContext context)
{
var allparameters = context.ActionArguments.Values;
if (allparameters.Count() == 1)
{
var param = allparameters.First();
if (typeof(IPatientRequest).IsAssignableFrom(param.GetType()))
{
IPatientRequest patientRequestInfo = (IPatientRequest)param;
PatientAccessRequest userAccessRequest = new PatientAccessRequest();
userAccessRequest.Rights = right;
userAccessRequest.MemberID = patientRequestInfo.PatientID;
var result = authService.CheckUserPatientAccess(userAccessRequest).Result; //this calls DB service to check from DB
if (result.Status == ReturnType.Failure)
{
//TODO: return apirepsonse
context.Result = new StatusCodeResult((int)System.Net.HttpStatusCode.Forbidden);
}
}
else
{
throw new AppSystemException("PatientAuthorizeAttribute not supported");
}
}
else
{
throw new AppSystemException("PatientAuthorizeAttribute not supported");
}
}
}
}
在API操作中,我们像这样使用它:
[PatientAuthorize(PatientAccessRights.PATIENT_VIEW_APPOINTMENTS)] //this is enum, we can pass multiple
[HttpPost]
public SomeReturnType ViewAppointments()
{
}
其他回答
似乎ASP。NET Core 2,你可以继承AuthorizeAttribute,你只需要实现IAuthorizationFilter(或IAsyncAuthorizationFilter):
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class CustomAuthorizeAttribute : AuthorizeAttribute, IAuthorizationFilter
{
private readonly string _someFilterParameter;
public CustomAuthorizeAttribute(string someFilterParameter)
{
_someFilterParameter = someFilterParameter;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
var user = context.HttpContext.User;
if (!user.Identity.IsAuthenticated)
{
// it isn't needed to set unauthorized result
// as the base class already requires the user to be authenticated
// this also makes redirect to a login page work properly
// context.Result = new UnauthorizedResult();
return;
}
// you can also use registered services
var someService = context.HttpContext.RequestServices.GetService<ISomeService>();
var isAuthorized = someService.IsUserAuthorized(user.Identity.Name, _someFilterParameter);
if (!isAuthorized)
{
context.Result = new StatusCodeResult((int)System.Net.HttpStatusCode.Forbidden);
return;
}
}
}
下面是一个简单的5步指南,教你如何使用策略来实现自定义角色授权:)。我使用了这些文档。
创建需求:
public class RoleRequirement : IAuthorizationRequirement
{
public string Role { get; set; }
}
创建一个处理器:
public class RoleHandler : AuthorizationHandler<RoleRequirement>
{
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, RoleRequirement requirement)
{
var requiredRole = requirement.Role;
//custom auth logic
// you can use context to access authenticated user,
// you can use dependecy injection to call custom services
var hasRole = true;
if (hasRole)
{
context.Succeed(requirement);
}
else
{
context.Fail(new AuthorizationFailureReason(this, $"Role {requirement.Role} missing"));
}
}
}
在Program.cs中添加处理器:
builder.Services.AddSingleton<IAuthorizationHandler, RoleHandler>();
在program.cs中添加带有角色需求的策略:
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("Read", policy => policy.Requirements.Add(new RoleRequirement{Role = "ReadAccess_Custom_System"}));
});
使用你的策略:
[Authorize("Read")]
public class ExampleController : ControllerBase
{
}
我有不记名令牌,我可以阅读索赔。 我在控制器和动作上使用该属性
public class CustomAuthorizationAttribute : ActionFilterAttribute
{
public string[] Claims;
public override void OnActionExecuting(ActionExecutingContext context)
{
// check user
var contextUser = context?.HttpContext?.User;
if (contextUser == null)
{
throw new BusinessException("Forbidden");
}
// check roles
var roles = contextUser.FindAll("http://schemas.microsoft.com/ws/2008/06/identity/claims/role").Select(c => c.Value).ToList();
if (!roles.Any(s => Claims.Contains(s)))
{
throw new BusinessException("Forbidden");
}
base.OnActionExecuting(context);
}
}
例子
[CustomAuthorization(Claims = new string[]
{
nameof(AuthorizationRole.HR_ADMIN),
nameof(AuthorizationRole.HR_SETTING)
})]
[Route("api/[controller]")]
[ApiController]
public class SomeAdminController : ControllerBase
{
private readonly IMediator _mediator;
public SomeAdminController(IMediator mediator)
{
_mediator = mediator;
}
[HttpGet("list/SomeList")]
public async Task<IActionResult> SomeList()
=> Ok(await _mediator.Send(new SomeListQuery()));
}
这就是角色
public struct AuthorizationRole
{
public static string HR_ADMIN;
public static string HR_SETTING;
}
ASP。Net Core团队将使用这里完整记录的新策略设计。新方法背后的基本思想是使用新的[Authorize]属性来指定一个“策略”(例如[Authorize(policy = "YouNeedToBe18ToDoThis")],其中策略在应用程序的Startup.cs中注册,以执行一些代码块(即确保用户在年龄为18岁或以上时有年龄声明)。
The policy design is a great addition to the framework and the ASP.Net Security Core team should be commended for its introduction. That said, it isn't well-suited for all cases. The shortcoming of this approach is that it fails to provide a convenient solution for the most common need of simply asserting that a given controller or action requires a given claim type. In the case where an application may have hundreds of discrete permissions governing CRUD operations on individual REST resources ("CanCreateOrder", "CanReadOrder", "CanUpdateOrder", "CanDeleteOrder", etc.), the new approach either requires repetitive one-to-one mappings between a policy name and a claim name (e.g. options.AddPolicy("CanUpdateOrder", policy => policy.RequireClaim(MyClaimTypes.Permission, "CanUpdateOrder));), or writing some code to perform these registrations at run time (e.g. read all claim types from a database and perform the aforementioned call in a loop). The problem with this approach for the majority of cases is that it's unnecessary overhead.
而ASP。Net Core Security团队建议永远不要创建自己的解决方案,在某些情况下,这可能是最谨慎的选择。
下面是一个实现,它使用IAuthorizationFilter提供了一种简单的方式来表达给定控制器或动作的索赔要求:
public class ClaimRequirementAttribute : TypeFilterAttribute
{
public ClaimRequirementAttribute(string claimType, string claimValue) : base(typeof(ClaimRequirementFilter))
{
Arguments = new object[] {new Claim(claimType, claimValue) };
}
}
public class ClaimRequirementFilter : IAuthorizationFilter
{
readonly Claim _claim;
public ClaimRequirementFilter(Claim claim)
{
_claim = claim;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
var hasClaim = context.HttpContext.User.Claims.Any(c => c.Type == _claim.Type && c.Value == _claim.Value);
if (!hasClaim)
{
context.Result = new ForbidResult();
}
}
}
[Route("api/resource")]
public class MyController : Controller
{
[ClaimRequirement(MyClaimTypes.Permission, "CanReadResource")]
[HttpGet]
public IActionResult GetResource()
{
return Ok();
}
}
只是给@Shawn的回答补充了一点。如果你正在使用dotnet 5,你需要更新类为:
public abstract class AttributeAuthorizationHandler<TRequirement, TAttribute> : AuthorizationHandler<TRequirement> where TRequirement : IAuthorizationRequirement where TAttribute : Attribute
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, TRequirement requirement)
{
var attributes = new List<TAttribute>();
if (context.Resource is HttpContext httpContext)
{
var endPoint = httpContext.GetEndpoint();
var action = endPoint?.Metadata.GetMetadata<ControllerActionDescriptor>();
if(action != null)
{
attributes.AddRange(GetAttributes(action.ControllerTypeInfo.UnderlyingSystemType));
attributes.AddRange(GetAttributes(action.MethodInfo));
}
}
return HandleRequirementAsync(context, requirement, attributes);
}
protected abstract Task HandleRequirementAsync(AuthorizationHandlerContext context, TRequirement requirement, IEnumerable<TAttribute> attributes);
private static IEnumerable<TAttribute> GetAttributes(MemberInfo memberInfo) => memberInfo.GetCustomAttributes(typeof(TAttribute), false).Cast<TAttribute>();
}
注意获取ControllerActionDescriptor的方式已经改变。