La risposta accettata ( https://stackoverflow.com/a/41348219/4974715 ) non è realisticamente mantenibile o adatta perché "CanReadResource" viene utilizzato come reclamo (ma dovrebbe essere essenzialmente una politica nella realtà, IMO). L'approccio alla risposta non è OK nel modo in cui è stato utilizzato, perché se un metodo di azione richiede molte configurazioni di attestazioni diverse, quindi con quella risposta dovresti scrivere più volte qualcosa come ...
[ClaimRequirement(MyClaimTypes.Permission, "CanReadResource")]
[ClaimRequirement(MyClaimTypes.AnotherPermision, "AnotherClaimVaue")]
//and etc. on a single action.
Quindi, immagina quanta codifica ci vorrebbe. Idealmente, "CanReadResource" dovrebbe essere una politica che utilizza molte affermazioni per determinare se un utente può leggere una risorsa.
Quello che faccio è creare i miei criteri come enumerazione e quindi scorrere e impostare i requisiti in questo modo ...
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.
*/
}
});
La classe DefaultAuthorizationRequirement sembra ...
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);
}
}
}
Si noti che il codice sopra può anche abilitare la pre-mappatura di un utente su una politica nell'archivio dati. Pertanto, quando si compongono attestazioni per l'utente, sostanzialmente si recuperano le politiche che sono state pre-mappate all'utente direttamente o indirettamente (ad es. Perché l'utente ha un determinato valore di rivendicazione e tale valore di rivendicazione è stato identificato e mappato su una politica, ad esempio che fornisce la mappatura automatica per gli utenti che hanno anche quel valore di rivendicazione) ed elenca le politiche come attestazioni, in modo tale che nel gestore dell'autorizzazione, è possibile semplicemente verificare se le rivendicazioni dell'utente contengono requisiti. Polizza come valore di un elemento di rivendicazione nel loro reclami. Questo è per un modo statico di soddisfare un requisito politico, ad esempio il requisito "Nome" è di natura abbastanza statica. Così,
[Authorize(Policy = nameof(Enumerations.Security.Policy.ViewRecord))]
Un requisito dinamico può riguardare il controllo della fascia di età, ecc. E le politiche che utilizzano tali requisiti non possono essere pre-mappate agli utenti.
Un esempio di controllo dinamico delle dichiarazioni di polizza (ad es. Per verificare se un utente ha più di 18 anni) è già alla risposta fornita da @blowdart ( https://stackoverflow.com/a/31465227/4974715 ).
PS: ho digitato questo sul mio telefono. Perdonate qualsiasi errore di battitura e mancanza di formattazione.