Die akzeptierte Antwort ( https://stackoverflow.com/a/41348219/4974715 ) ist nicht realistisch wartbar oder geeignet, da "CanReadResource" als Anspruch verwendet wird (sollte aber im Wesentlichen eine Richtlinie in der Realität sein, IMO). Der Ansatz bei der Antwort ist in der Art und Weise, wie er verwendet wurde, nicht in Ordnung. Wenn für eine Aktionsmethode viele verschiedene Anspruchseinstellungen erforderlich sind, müssten Sie mit dieser Antwort wiederholt so etwas wie ...
[ClaimRequirement(MyClaimTypes.Permission, "CanReadResource")]
[ClaimRequirement(MyClaimTypes.AnotherPermision, "AnotherClaimVaue")]
//and etc. on a single action.
Stellen Sie sich also vor, wie viel Codierung das kosten würde. Im Idealfall sollte "CanReadResource" eine Richtlinie sein, die viele Ansprüche verwendet, um zu bestimmen, ob ein Benutzer eine Ressource lesen kann.
Was ich tue, ist, dass ich meine Richtlinien als Aufzählung erstelle und dann die Anforderungen wie folgt durchlaufe und einrichte ...
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.
*/
}
});
Die DefaultAuthorizationRequirement-Klasse sieht aus wie ...
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);
}
}
}
Beachten Sie, dass der obige Code auch die Vorabzuordnung eines Benutzers zu einer Richtlinie in Ihrem Datenspeicher ermöglichen kann. Wenn Sie also Ansprüche für den Benutzer erstellen, rufen Sie im Grunde genommen die Richtlinien ab, die dem Benutzer direkt oder indirekt vorab zugeordnet wurden (z. B. weil der Benutzer einen bestimmten Anspruchswert hat und dieser Anspruchswert identifiziert und einer Richtlinie zugeordnet wurde, z dass es eine automatische Zuordnung für Benutzer bietet, die auch diesen Anspruchswert haben) und die Richtlinien als Ansprüche eintragen, sodass Sie im Autorisierungshandler einfach überprüfen können, ob die Ansprüche des Benutzers eine Anforderung enthalten. Richtlinie als Wert eines Anspruchselements in ihrem Ansprüche. Dies dient dazu, eine Richtlinienanforderung statisch zu erfüllen, z. B. ist die Anforderung "Vorname" recht statisch. Damit,
[Authorize(Policy = nameof(Enumerations.Security.Policy.ViewRecord))]
Bei einer dynamischen Anforderung kann es sich um die Überprüfung des Altersbereichs usw. handeln, und Richtlinien, die solche Anforderungen verwenden, können Benutzern nicht vorab zugeordnet werden.
Ein Beispiel für die dynamische Überprüfung von Richtlinienansprüchen (z. B. um zu überprüfen, ob ein Benutzer älter als 18 Jahre ist) finden Sie bereits in der Antwort von @blowdart ( https://stackoverflow.com/a/31465227/4974715 ).
PS: Ich habe das auf meinem Handy eingegeben. Verzeihen Sie Tippfehler und fehlende Formatierung.