接受的答案 (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);
}
}
}
请注意,上面的代码还可以将用户预映射到数据存储中的策略。因此,在为用户撰写声明时,您基本上会直接或间接检索已预先映射到用户的策略(例如,因为用户具有特定声明值并且该声明值已被识别并映射到策略,例如它为也具有该声明值的用户提供自动映射),并将策略作为声明登记,这样在授权处理程序中,您可以简单地检查用户的声明是否包含 require.Policy 作为其声明项的值索赔。那是为了满足策略要求的静态方式,例如“名字”要求本质上是静态的。因此,对于上面的示例(我在之前对此答案的更新中忘记提供有关 Authorize 属性的示例),使用具有 Authorize 属性的策略如下所示,其中 ViewRecord 是枚举成员:
[Authorize(Policy = nameof(Enumerations.Security.Policy.ViewRecord))]
动态要求可以是关于检查年龄范围等,并且使用这些要求的政策不能预先映射到用户。
@blowdart (https://stackoverflow.com/a/31465227/4974715) 已经给出了动态政策声明检查的示例(例如检查用户是否超过 18 岁)。
PS:这是我在手机上输入的。请原谅任何拼写错误和格式缺失。