如何在ASP中创建自定义AuthorizeAttribute ?网络核心?

我试图使一个自定义授权属性在ASP。净的核心。在以前的版本中,可以覆盖bool AuthorizeCore(HttpContextBase httpContext)。但是在AuthorizeAttribute中已经不存在了。

当前制作自定义AuthorizeAttribute的方法是什么?

我想要完成的:我正在头授权中接收会话ID。通过该ID,我将知道特定操作是否有效。

460871 次浏览

当前制作自定义AuthorizeAttribute的方法是什么

对于纯授权场景(例如仅限制特定用户访问),建议使用新的授权块:https://github.com/aspnet/MusicStore/blob/1c0aeb08bb1ebd846726232226279bbe001782e1/samples/MusicStore/Startup.cs#L84-L92

public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.Configure<AuthorizationOptions>(options =>
{
options.AddPolicy("ManageStore", policy => policy.RequireClaim("Action", "ManageStore"));
});
}
}


public class StoreController : Controller
{
[Authorize(Policy = "ManageStore"), HttpGet]
public async Task<IActionResult> Manage() { ... }
}

对于身份验证,最好在中间件级别进行处理。

你到底想达到什么目的?

我是asp.net安全人员。详细的文档是在这里

我们不希望您编写自定义授权属性。如果你需要这样做,我们做错了什么。相反,您应该编写授权需求

授权作用于身份。身份通过认证创建。

你在注释中说你想检查头中的会话ID。您的会话ID将是标识的基础。如果您想使用Authorize属性,则需要编写一个身份验证中间件来获取该标头,并将其转换为经过身份验证的ClaimsPrincipal。然后在授权要求中检查。授权要求可以像您喜欢的那样复杂,例如,这里有一个在当前身份上接受出生日期声明,并在用户超过18岁时进行授权;

public class Over18Requirement : AuthorizationHandler<Over18Requirement>, IAuthorizationRequirement
{
public override void Handle(AuthorizationHandlerContext context, Over18Requirement requirement)
{
if (!context.User.HasClaim(c => c.Type == ClaimTypes.DateOfBirth))
{
context.Fail();
return;
}


var dobVal = context.User.FindFirst(c => c.Type == ClaimTypes.DateOfBirth).Value;
var dateOfBirth = Convert.ToDateTime(dobVal);
int age = DateTime.Today.Year - dateOfBirth.Year;
if (dateOfBirth > DateTime.Today.AddYears(-age))
{
age--;
}


if (age >= 18)
{
context.Succeed(requirement);
}
else
{
context.Fail();
}
}
}

然后在ConfigureServices()函数中,将其连接起来

services.AddAuthorization(options =>
{
options.AddPolicy("Over18",
policy => policy.Requirements.Add(new Authorization.Over18Requirement()));
});

最后,将它应用到控制器或动作方法

[Authorize(Policy = "Over18")]

您可以创建自己的AuthorizationHandler,它将在控制器和动作上找到自定义属性,并将它们传递给handlerrequirementasync方法。

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


var action = (context.Resource as AuthorizationFilterContext)?.ActionDescriptor as 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)
{
return memberInfo.GetCustomAttributes(typeof(TAttribute), false).Cast<TAttribute>();
}
}

然后你可以在你的控制器或动作上使用它的任何自定义属性。例如,添加权限要求。只需创建您的自定义属性。

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class PermissionAttribute : AuthorizeAttribute
{
public string Name { get; }


public PermissionAttribute(string name) : base("Permission")
{
Name = name;
}
}

然后创建一个需求,添加到您的策略

public class PermissionAuthorizationRequirement : IAuthorizationRequirement
{
//Add any custom requirement properties if you have them
}

然后为您的自定义属性创建AuthorizationHandler,继承我们之前创建的AttributeAuthorizationHandler。它将被传递一个IEnumerable,用于handlerrequirementsasync方法中的所有自定义属性,这些属性是由控制器和动作积累而来的。

public class PermissionAuthorizationHandler : AttributeAuthorizationHandler<PermissionAuthorizationRequirement, PermissionAttribute>
{
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionAuthorizationRequirement requirement, IEnumerable<PermissionAttribute> attributes)
{
foreach (var permissionAttribute in attributes)
{
if (!await AuthorizeAsync(context.User, permissionAttribute.Name))
{
return;
}
}


context.Succeed(requirement);
}


private Task<bool> AuthorizeAsync(ClaimsPrincipal user, string permission)
{
//Implement your custom user permission logic here
}
}

最后,在Startup.cs ConfigureServices方法中,将您的自定义AuthorizationHandler添加到服务中,并添加您的Policy。

        services.AddSingleton<IAuthorizationHandler, PermissionAuthorizationHandler>();


services.AddAuthorization(options =>
{
options.AddPolicy("Permission", policyBuilder =>
{
policyBuilder.Requirements.Add(new PermissionAuthorizationRequirement());
});
});

现在你可以简单地用你的自定义属性装饰你的控制器和动作。

[Permission("AccessCustomers")]
public class CustomersController
{
[Permission("AddCustomer")]
IActionResult AddCustomer([FromBody] Customer customer)
{
//Add customer
}
}

ASP。Net Core团队将使用完整文档化的新策略设计在这里。新方法背后的基本思想是使用新的[Authorize]属性来指定“策略”;(例如,在[Authorize( Policy = "YouNeedToBe18ToDoThis")]中,策略被注册在应用程序的Startup.cs中,以执行一些代码块(即,确保用户在年龄为18岁或以上的情况下有年龄索赔)。

策略设计是对框架和ASP。网络安全核心团队应该为其引入而受到赞扬。也就是说,它并不适合所有情况。这种方法的缺点是,它不能为最常见的需求提供方便的解决方案,即简单地断言给定的控制器或动作需要给定的声明类型。在应用程序可能有数百个独立的权限来控制对单个REST资源的CRUD操作(“cancreateorder”,“canreadorder”,“canupdateorder”,“candeleteorder”等)的情况下,新方法要么需要策略名称和索赔名称之间重复的一对一映射(例如options.AddPolicy("CanUpdateOrder", policy => policy.RequireClaim(MyClaimTypes.Permission, "CanUpdateOrder));),或者编写一些代码在运行时执行这些注册(例如,从数据库读取所有索赔类型并在循环中执行上述调用)。在大多数情况下,这种方法的问题在于它是不必要的开销。

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

基于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());
}
}

似乎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;
}
}
}

如果有人只是想在授权阶段使用当前的安全实践验证一个承载令牌,

将此添加到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)]

为了在我们的应用程序中进行授权。我们必须根据在授权属性中传递的参数调用服务。

例如,如果我们想检查登录的医生是否可以查看病人的预约,我们将传递“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()
{


}

现代的方法是AuthenticationHandlers

在startup.cs中添加

services.AddAuthentication("BasicAuthentication").AddScheme<AuthenticationSchemeOptions, BasicAuthenticationHandler>("BasicAuthentication", null);


public class BasicAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
private readonly IUserService _userService;


public BasicAuthenticationHandler(
IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock,
IUserService userService)
: base(options, logger, encoder, clock)
{
_userService = userService;
}


protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
if (!Request.Headers.ContainsKey("Authorization"))
return AuthenticateResult.Fail("Missing Authorization Header");


User user = null;
try
{
var authHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]);
var credentialBytes = Convert.FromBase64String(authHeader.Parameter);
var credentials = Encoding.UTF8.GetString(credentialBytes).Split(new[] { ':' }, 2);
var username = credentials[0];
var password = credentials[1];
user = await _userService.Authenticate(username, password);
}
catch
{
return AuthenticateResult.Fail("Invalid Authorization Header");
}


if (user == null)
return AuthenticateResult.Fail("Invalid User-name or Password");


var claims = new[] {
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
new Claim(ClaimTypes.Name, user.Username),
};
var identity = new ClaimsIdentity(claims, Scheme.Name);
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, Scheme.Name);


return AuthenticateResult.Success(ticket);
}
}
IUserService是一个你有用户名和密码的服务。 基本上它返回一个用户类,你用它来映射你的声明
var claims = new[] {
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
new Claim(ClaimTypes.Name, user.Username),
};

然后你可以查询这些索赔和她的任何数据你映射,有相当多,看看ClaimTypes类

您可以在扩展方法中使用它来获取任何映射

public int? GetUserId()
{
if (context.User.Identity.IsAuthenticated)
{
var id=context.User.FindFirst(ClaimTypes.NameIdentifier);
if (!(id is null) && int.TryParse(id.Value, out var userId))
return userId;
}
return new Nullable<int>();
}

我认为这种新方法比这里展示的旧方法更好,两种方法都有效

public class BasicAuthenticationAttribute : AuthorizationFilterAttribute
{
public override void OnAuthorization(HttpActionContext actionContext)
{
if (actionContext.Request.Headers.Authorization != null)
{
var authToken = actionContext.Request.Headers.Authorization.Parameter;
// decoding authToken we get decode value in 'Username:Password' format
var decodeauthToken = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(authToken));
// spliting decodeauthToken using ':'
var arrUserNameandPassword = decodeauthToken.Split(':');
// at 0th postion of array we get username and at 1st we get password
if (IsAuthorizedUser(arrUserNameandPassword[0], arrUserNameandPassword[1]))
{
// setting current principle
Thread.CurrentPrincipal = new GenericPrincipal(new GenericIdentity(arrUserNameandPassword[0]), null);
}
else
{
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
}
}
else
{
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
}
}


public static bool IsAuthorizedUser(string Username, string Password)
{
// In this method we can handle our database logic here...
return Username.Equals("test") && Password == "test";
}
}

在写这篇文章时,我相信这可以用asp.net core 2及以上的iclaimstrtransform接口来完成。我刚刚实现了一个概念的证明,可以分享到这里。

public class PrivilegesToClaimsTransformer : IClaimsTransformation
{
private readonly IPrivilegeProvider privilegeProvider;
public const string DidItClaim = "http://foo.bar/privileges/resolved";


public PrivilegesToClaimsTransformer(IPrivilegeProvider privilegeProvider)
{
this.privilegeProvider = privilegeProvider;
}


public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
{
if (principal.Identity is ClaimsIdentity claimer)
{
if (claimer.HasClaim(DidItClaim, bool.TrueString))
{
return principal;
}


var privileges = await this.privilegeProvider.GetPrivileges( ... );
claimer.AddClaim(new Claim(DidItClaim, bool.TrueString));


foreach (var privilegeAsRole in privileges)
{
claimer.AddClaim(new Claim(ClaimTypes.Role /*"http://schemas.microsoft.com/ws/2008/06/identity/claims/role" */, privilegeAsRole));
}
}


return principal;
}
}

要在你的控制器中使用它,只需添加一个适当的[Authorize(Roles="whatever")]到你的方法。

[HttpGet]
[Route("poc")]
[Authorize(Roles = "plugh,blast")]
public JsonResult PocAuthorization()
{
var result = Json(new
{
when = DateTime.UtcNow,
});


result.StatusCode = (int)HttpStatusCode.OK;


return result;
}

在我们的例子中,每个请求都包含一个JWT的授权标头。这是原型,我相信下周我们会在我们的生产系统中做一些非常接近的东西。

未来的选民,在投票时,请考虑投票日期。到目前为止,您可能需要在实现上使用works on my machine.™进行更多的错误处理和日志记录。

接受的答案(https://stackoverflow.com/a/41348219/4974715)实际上是不可维护的或不合适的,因为“CanReadResource”被用作声明(但实际上应该是一个策略,IMO)。答案的方法在它被使用的方式上是不正确的,因为如果一个动作方法需要许多不同的声明设置,那么有了这个答案,你就必须重复写一些像……

[ClaimRequirement(MyClaimTypes.Permission, "CanReadResource")]
[ClaimRequirement(MyClaimTypes.AnotherPermision, "AnotherClaimVaue")]
//and etc. on a single action.

所以,想象一下这需要多少编码。理想情况下,“CanReadResource”应该是一种策略,它使用许多声明来确定用户是否可以读取资源。

我所做的是将策略创建为枚举,然后循环遍历并设置需求,就像这样……

services.AddAuthorization(authorizationOptions =>
{
foreach (var policyString in Enum.GetNames(typeof(Enumerations.Security.Policy)))
{
authorizationOptions.AddPolicy(
policyString,
authorizationPolicyBuilder => authorizationPolicyBuilder.Requirements.Add(new DefaultAuthorizationRequirement((Enumerations.Security.Policy)Enum.Parse(typeof(Enumerations.Security.Policy), policyWrtString), DateTime.UtcNow)));


/* Note that thisn does not stop you from
configuring policies directly against a username, claims, roles, etc. You can do the usual.
*/
}
});

DefaultAuthorizationRequirement类看起来像…

public class DefaultAuthorizationRequirement : IAuthorizationRequirement
{
public Enumerations.Security.Policy Policy {get; set;} //This is a mere enumeration whose code is not shown.
public DateTime DateTimeOfSetup {get; set;} //Just in case you have to know when the app started up. And you may want to log out a user if their profile was modified after this date-time, etc.
}


public class DefaultAuthorizationHandler : AuthorizationHandler<DefaultAuthorizationRequirement>
{
private IAServiceToUse _aServiceToUse;


public DefaultAuthorizationHandler(
IAServiceToUse aServiceToUse
)
{
_aServiceToUse = aServiceToUse;
}


protected async override Task HandleRequirementAsync(AuthorizationHandlerContext context, DefaultAuthorizationRequirement requirement)
{
/*Here, you can quickly check a data source or Web API or etc.
to know the latest date-time of the user's profile modification...
*/
if (_aServiceToUse.GetDateTimeOfLatestUserProfileModication > requirement.DateTimeOfSetup)
{
context.Fail(); /*Because any modifications to user information,
e.g. if the user used another browser or if by Admin modification,
the claims of the user in this session cannot be guaranteed to be reliable.
*/
return;
}


bool shouldSucceed = false; //This should first be false, because context.Succeed(...) has to only be called if the requirement specifically succeeds.


bool shouldFail = false; /*This should first be false, because context.Fail()
doesn't have to be called if there's no security breach.
*/


// You can do anything.
await doAnythingAsync();


/*You can get the user's claims...
ALSO, note that if you have a way to priorly map users or users with certain claims
to particular policies, add those policies as claims of the user for the sake of ease.
BUT policies that require dynamic code (e.g. checking for age range) would have to be
coded in the switch-case below to determine stuff.
*/


var claims = context.User.Claims;


// You can, of course, get the policy that was hit...
var policy = requirement.Policy


//You can use a switch case to determine what policy to deal with here...
switch (policy)
{
case Enumerations.Security.Policy.CanReadResource:
/*Do stuff with the claims and change the
value of shouldSucceed and/or shouldFail.
*/
break;
case Enumerations.Security.Policy.AnotherPolicy:
/*Do stuff with the claims and change the
value of shouldSucceed and/or shouldFail.
*/
break;
// Other policies too.


default:
throw new NotImplementedException();
}


/* Note that the following conditions are
so because failure and success in a requirement handler
are not mutually exclusive. They demand certainty.
*/


if (shouldFail)
{
context.Fail(); /*Check the docs on this method to
see its implications.
*/
}


if (shouldSucceed)
{
context.Succeed(requirement);
}
}
}

注意,上面的代码还可以将用户预映射到数据存储中的策略。因此,在为用户组合声明时,基本上检索直接或间接地预先映射到用户的策略(例如,因为用户具有某个声明值,并且该声明值已被标识并映射到策略,以便它为同样具有该声明值的用户提供自动映射),并将策略征调为声明,以便在授权处理程序中,您可以简单地检查用户的声明是否包含需求。策略作为索赔项在其索赔中的值。这是满足策略需求的静态方式,例如。“名字”要求本质上是静态的。所以,对于上面的例子(我忘记了在我之前更新这个答案的授权属性的例子),使用授权属性的策略如下所示,其中ViewRecord是一个enum成员:

[Authorize(Policy = nameof(Enumerations.Security.Policy.ViewRecord))]

动态需求可以是关于检查年龄范围等,使用这种需求的策略不能预先映射到用户。

一个动态策略索赔检查的例子(例如,检查用户是否超过18岁)已经在@blowdart (https://stackoverflow.com/a/31465227/4974715)给出的答案中。

PS:这是我用手机打出来的。请原谅任何拼写错误和缺乏格式。

只是给@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的方式已经改变。

我有不记名令牌,我可以阅读声明。 我在控制器和动作上使用该属性

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

下面的代码适合我在。net Core 5中使用

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class AccessAuthorizationAttribute : AuthorizeAttribute, IAuthorizationFilter
{
public string Module { get; set; } //Permission string to get from controller


public AccessAuthorizationAttribute(string module)
{
Module = module;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
//Validate if any permissions are passed when using attribute at controller or action level


if (string.IsNullOrEmpty(Module))
{
//Validation cannot take place without any permissions so returning unauthorized
context.Result = new UnauthorizedResult();
return;
}
       

if (hasAccess)
{
return;
}


context.Result = new UnauthorizedResult();
return;
}
}

什么? !

我决定再补充一个简单的答案。B/c我发现大多数答案都有点过度设计。也因为我需要一种授予授权的方法,而不仅仅是否认它。这里的大多数答案都提供了一种“收紧”的方法;安全感,但我想“放松”;它。例如:“如果配置了某些应用程序设置,则允许匿名用户访问”。

public class MyAuthAttribute : Attribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
//check access
if (CheckPermissions())
{
//all good, add optional code if you want. Or don't
}
else
{
//DENIED!
//return "ChallengeResult" to redirect to login page (for example)
context.Result = new ChallengeResult(CookieAuthenticationDefaults.AuthenticationScheme);
}
}
}

就是这样。没有必要与“政策”、“索赔”、“处理人”混淆;其他的[哔]

用法:

// GET api/Get/5
[MyAuth]
public ActionResult<string> Get(int id)
{
return "blahblah";
}

这里很多人已经说过了,但是有了策略处理程序,你就可以在。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;
}
}

注册我的处理器和需求: services.AddSingleton< IAuthorizationHandler EditUserRequirementHandler> () < / p >

        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>

我希望这对面临这个问题的人有用。

我一直在寻找解决一个非常类似的问题,并决定创建一个自定义ActionFilterAttribute(我将其称为AuthorizationFilterAttribute)而不是AuthorizeAttribute来实现此处的指导:https://learn.microsoft.com/en-us/aspnet/core/security/authorization/resourcebased?view=aspnetcore-6.0#challenge-and-forbid-with-an-operational-resource-handler

下面是一个简单的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
{
}