我试图使一个自定义授权属性在ASP。净的核心。在以前的版本中,可以重写bool AuthorizeCore(HttpContextBase httpContext)。但是这在AuthorizeAttribute中不再存在。
当前制作自定义AuthorizeAttribute的方法是什么?
我想要完成的:我正在头授权中接收会话ID。通过该ID,我将知道特定操作是否有效。
我试图使一个自定义授权属性在ASP。净的核心。在以前的版本中,可以重写bool AuthorizeCore(HttpContextBase httpContext)。但是这在AuthorizeAttribute中不再存在。
当前制作自定义AuthorizeAttribute的方法是什么?
我想要完成的:我正在头授权中接收会话ID。通过该ID,我将知道特定操作是否有效。
当前回答
这里很多人已经说过了,但是有了策略处理程序,你就可以在。net框架中使用旧方法实现的功能而言,你可以走得更远。
我在SO网站上快速写了一个答案:https://stackoverflow.com/a/61963465/7081176 对我来说,在制作了一些类后,它完美地工作了:
EditUserRequirement:
public class EditUserRequirement : IAuthorizationRequirement
{
public EditUserRequirement()
{
}
}
一个抽象处理程序,使我的生活更容易:
public abstract class AbstractRequirementHandler<T> : IAuthorizationHandler
where T : IAuthorizationRequirement
{
public async Task HandleAsync(AuthorizationHandlerContext context)
{
var pendingRequirements = context.PendingRequirements.ToList();
foreach (var requirement in pendingRequirements)
{
if (requirement is T typedRequirement)
{
await HandleRequirementAsync(context, typedRequirement);
}
}
}
protected abstract Task HandleRequirementAsync(AuthorizationHandlerContext context, T requirement);
}
抽象处理程序的实现:
public class EditUserRequirementHandler : AbstractRequirementHandler<EditUserRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, EditUserRequirement requirement)
{
// If the user is owner of the resource, allow it.
if (IsOwner(context.User, g))
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
private static bool IsOwner(ClaimsPrincipal user, Guid userIdentifier)
{
return user.GetUserIdentifier() == userIdentifier;
}
}
注册我的处理程序和需求: 服务。AddSingleton < IAuthorizationHandler, EditUserRequirementHandler > ();
services.AddAuthorization(options =>
{
options.AddPolicy(Policies.Policies.EditUser, policy =>
{
policy.Requirements.Add(new EditUserRequirement());
});
});
然后使用我在Blazor的策略:
<AuthorizeView Policy="@Policies.EditUser" Resource="@id">
<NotAuthorized>
<Unauthorized />
</NotAuthorized>
<Authorized Context="Auth">
...
</Authorized>
</AuthorizeView>
我希望这对面临这个问题的人有用。
其他回答
如果有人只是想在授权阶段使用当前的安全实践验证一个承载令牌,
将此添加到Startup/ConfigureServices中
services.AddSingleton<IAuthorizationHandler, BearerAuthorizationHandler>();
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer();
services.AddAuthorization(options => options.AddPolicy("Bearer",
policy => policy.AddRequirements(new BearerRequirement())
)
);
在你的代码库中,
public class BearerRequirement : IAuthorizationRequirement
{
public async Task<bool> IsTokenValid(SomeValidationContext context, string token)
{
// here you can check if the token received is valid
return true;
}
}
public class BearerAuthorizationHandler : AuthorizationHandler<BearerRequirement>
{
public BearerAuthorizationHandler(SomeValidationContext thatYouCanInject)
{
...
}
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, BearerRequirement requirement)
{
var authFilterCtx = (Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext)context.Resource;
string authHeader = authFilterCtx.HttpContext.Request.Headers["Authorization"];
if (authHeader != null && authHeader.Contains("Bearer"))
{
var token = authHeader.Replace("Bearer ", string.Empty);
if (await requirement.IsTokenValid(thatYouCanInject, token))
{
context.Succeed(requirement);
}
}
}
}
如果代码没有到达context.Succeed(…),那么无论如何都会失败(401)。
然后在你的控制器中你可以使用
[Authorize(Policy = "Bearer", AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
基于Derek Greer伟大的答案,我用枚举来做。
下面是我的代码示例:
public enum PermissionItem
{
User,
Product,
Contact,
Review,
Client
}
public enum PermissionAction
{
Read,
Create,
}
public class AuthorizeAttribute : TypeFilterAttribute
{
public AuthorizeAttribute(PermissionItem item, PermissionAction action)
: base(typeof(AuthorizeActionFilter))
{
Arguments = new object[] { item, action };
}
}
public class AuthorizeActionFilter : IAuthorizationFilter
{
private readonly PermissionItem _item;
private readonly PermissionAction _action;
public AuthorizeActionFilter(PermissionItem item, PermissionAction action)
{
_item = item;
_action = action;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
bool isAuthorized = MumboJumboFunction(context.HttpContext.User, _item, _action); // :)
if (!isAuthorized)
{
context.Result = new ForbidResult();
}
}
}
public class UserController : BaseController
{
private readonly DbContext _context;
public UserController( DbContext context) :
base()
{
_logger = logger;
}
[Authorize(PermissionItem.User, PermissionAction.Read)]
public async Task<IActionResult> Index()
{
return View(await _context.User.ToListAsync());
}
}
只是给@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的方式已经改变。
似乎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
{
}