La respuesta aceptada ( https://stackoverflow.com/a/41348219/4974715 ) no es mantenible o adecuada de manera realista porque "CanReadResource" se está utilizando como un reclamo (pero esencialmente debería ser una política en realidad, IMO). El enfoque en la respuesta no está bien en la forma en que se usó, porque si un método de acción requiere muchas configuraciones de reclamos diferentes, entonces con esa respuesta tendría que escribir repetidamente algo como ...
[ClaimRequirement(MyClaimTypes.Permission, "CanReadResource")]
[ClaimRequirement(MyClaimTypes.AnotherPermision, "AnotherClaimVaue")]
//and etc. on a single action.
Entonces, imagine la cantidad de codificación que tomaría. Idealmente, se supone que "CanReadResource" es una política que utiliza muchos reclamos para determinar si un usuario puede leer un recurso.
Lo que hago es crear mis políticas como una enumeración y luego recorrer y configurar los requisitos de esta manera ...
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 clase DefaultAuthorizationRequirement se parece a ...
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);
}
}
}
Tenga en cuenta que el código anterior también puede habilitar la asignación previa de un usuario a una política en su almacén de datos. Por lo tanto, al redactar reclamos para el usuario, básicamente recupera las políticas que se habían asignado previamente al usuario directa o indirectamente (por ejemplo, porque el usuario tiene un cierto valor de reclamo y ese valor de reclamo se ha identificado y asignado a una política, como que proporciona un mapeo automático para los usuarios que también tienen ese valor de reclamo), y alista las políticas como reclamos, de modo que en el controlador de autorización, simplemente puede verificar si los reclamos del usuario contienen requisitos. reclamación (es. Esto es para una forma estática de satisfacer un requisito de política, por ejemplo, el requisito de "nombre" es de naturaleza bastante estática. Entonces,
[Authorize(Policy = nameof(Enumerations.Security.Policy.ViewRecord))]
Un requisito dinámico puede ser verificar el rango de edad, etc. y las políticas que usan dichos requisitos no pueden asignarse previamente a los usuarios.
En la respuesta dada por @blowdart ( https://stackoverflow.com/a/31465227/4974715 ) ya se encuentra un ejemplo de verificación dinámica de reclamos de políticas (por ejemplo, para verificar si un usuario tiene más de 18 años ).
PD: escribí esto en mi teléfono. Disculpe cualquier error tipográfico y falta de formato.