我正在尝试在ASP.NET Core中创建自定义授权属性。在以前的版本中,可以覆盖bool AuthorizeCore(HttpContextBase httpContext)
。但这在中不再存在AuthorizeAttribute
。
制作自定义AuthorizeAttribute的当前方法是什么?
我要完成的工作:我在标题授权中收到一个会话ID。通过该ID,我将知道特定操作是否有效。
我正在尝试在ASP.NET Core中创建自定义授权属性。在以前的版本中,可以覆盖bool AuthorizeCore(HttpContextBase httpContext)
。但这在中不再存在AuthorizeAttribute
。
制作自定义AuthorizeAttribute的当前方法是什么?
我要完成的工作:我在标题授权中收到一个会话ID。通过该ID,我将知道特定操作是否有效。
Answers:
ASP.Net Core团队推荐的方法是使用新的策略设计,在此将其详细记录。新方法的基本思想是使用新的[Authorize]属性来指定“策略”(例如[Authorize( Policy = "YouNeedToBe18ToDoThis")]
,该策略在应用程序的Startup.cs中注册的位置,以执行某些代码块(即,确保用户拥有年龄限制)年满18岁)。
策略设计是对框架的重要补充,ASP.Net Security Core团队的引入值得赞扬。也就是说,它并不适合所有情况。这种方法的缺点在于,它无法为最简单的要求简单地断言给定的控制器或操作需要给定的索赔类型的最常见需求提供方便的解决方案。如果应用程序可能具有数百个离散的权限来管理单个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();
}
}
new ForbidResult()
不起作用(导致异常/ 500),因为它没有关联的授权方案。在这种情况下我将使用什么?
我是asp.net安全人员。首先,我很抱歉,在音乐商店样本或单元测试之外,没有任何文档被记录下来,并且仍然在公开的API方面对其进行了完善。详细的文档在这里。
我们不希望您编写自定义授权属性。如果您需要这样做,我们做错了。相反,您应该编写授权要求。
授权作用于身份。身份是通过身份验证创建的。
您在注释中说,您想检查标题中的会话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 dateOfBirth = Convert.ToDateTime(context.User.FindFirst(c => c.Type == ClaimTypes.DateOfBirth).Value);
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")]
ManageStore
音乐商店中的需求”示例。正如示例中那样,只有一种“全部允许或全部不允许”的方式来执行此操作。然后,我们是否必须为每个可能的排列创建新的策略?例如“用户/读取”,“用户/创建”,“用户/ AssignRole”,“用户/删除”,是否需要细粒度的声明?听起来好像要进行大量设置工作才能使其正常运行,并且有大量策略仅用于管理声明而不是[ClaimsAutzorization("User", "Read", "Create", "Delete", "Assign")]
属性?
[CustomAuthorize(Operator.And, Permission.GetUser, Permission.ModifyUser)]
。我可以通过修改构造函数参数以多种方式使用单个自定义属性。
IAuthorizeData.Policy
)和自定义策略提供程序来克服这种公然的监督,而不是在框架内解决它。我以为我们不应该创建自己的实现?除了让我们重新从头开始(再一次)实现授权之外,您几乎没有其他选择,而且这次甚至没有使用Web API的旧Authorize
属性。现在,我们必须在操作过滤器或中间件级别上执行此操作。
似乎在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;
}
}
}
OnAuthorization
实现需要等待异步方法,则应实现IAsyncAuthorizationFilter
而不是IAuthorizationFilter
其他方法,否则过滤器将同步执行,并且无论过滤器的结果如何,控制器动作都将执行。
基于Derek Greer GREAT的答案,我使用枚举来做到这一点。
这是我的代码示例:
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());
}
}
您可以创建自己的AuthorizationHandler,以在Controllers和Actions上找到自定义属性,并将它们传递给HandleRequirementAsync方法。
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。将为HandleRequirementsAsync方法中的所有自定义属性传递一个IEnumerable,该IEnumerable是从Controller和Action累积的。
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
}
}
制作自定义AuthorizeAttribute的当前方法是什么
简单:不要创建自己的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() { ... }
}
对于身份验证,最好在中间件级别进行处理。
您到底想达到什么目标?
如果有人只想使用当前的安全做法在授权阶段验证承载令牌,则可以,
将此添加到您的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)]
现代方式是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及更高版本中的IClaimsTransformation接口来完成此操作。我刚刚实施了一个概念证明,该证明足以在此处发布。
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;
}
}
要在Controller中使用此功能,只需[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的Authorization标头。这是原型,我相信下周我们将在生产系统中做得更好。
未来的选民,请在投票时考虑撰写日期。截止到今天,works on my machine.
您可能将需要更多的错误处理和实现登录信息。
在我们的应用程序中进行授权。我们必须基于授权属性中传递的参数调用服务。
例如,如果我们要检查登录的医生是否可以查看患者约会,则将“ View_Appointment”传递给自定义授权属性,并在数据库服务中检查该权限,并根据结果进行调整。这是此方案的代码:
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()
{
}
接受的答案(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);
}
}
}
请注意,上面的代码还可以使用户预先映射到数据存储中的策略。因此,在为用户编写索赔时,基本上可以直接或间接检索预先映射到用户的策略(例如,因为用户具有某个索赔值,并且该索赔值已被标识并映射到策略,例如(它也为具有该声明值的用户提供自动映射),并将策略注册为声明,以便在授权处理程序中,您可以简单地检查用户的声明中是否包含需求。索赔。那是为了满足策略要求的静态方式,例如,“名字”要求本质上是相当静态的。所以,
[Authorize(Policy = nameof(Enumerations.Security.Policy.ViewRecord))]
动态需求可能涉及检查年龄范围等,并且使用此类需求的策略无法预先映射到用户。
@blowdart(https://stackoverflow.com/a/31465227/4974715)给出了动态策略声明检查(例如,检查用户是否超过18岁)的示例。
PS:我在手机上打了这个。请原谅任何错别字和缺乏格式。