A resposta aceita ( https://stackoverflow.com/a/41348219/4974715 ) não é realisticamente mantida ou adequada porque "CanReadResource" está sendo usado como uma reivindicação (mas deve ser essencialmente uma política na realidade, IMO). A abordagem na resposta não é boa na maneira como foi usada, porque se um método de ação exigir muitas configurações de declarações diferentes, com essa resposta, você deverá escrever repetidamente algo como ...
[ClaimRequirement(MyClaimTypes.Permission, "CanReadResource")]
[ClaimRequirement(MyClaimTypes.AnotherPermision, "AnotherClaimVaue")]
//and etc. on a single action.
Então, imagine quanta codificação seria necessária. Idealmente, "CanReadResource" deve ser uma política que use muitas declarações para determinar se um usuário pode ler um recurso.
O que faço é criar minhas políticas como uma enumeração e, em seguida, percorrer e configurar os requisitos dessa forma ...
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.
*/
}
});
A classe DefaultAuthorizationRequirement se parece com ...
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);
}
}
}
Observe que o código acima também pode ativar o pré-mapeamento de um usuário para uma política em seu armazenamento de dados. Portanto, ao compor declarações para o usuário, você basicamente recupera as políticas que foram pré-mapeadas para o usuário direta ou indiretamente (por exemplo, porque o usuário tem um determinado valor de declaração e esse valor de identificação foi identificado e mapeado para uma política, como que ele fornece mapeamento automático para usuários que também possuem esse valor de declaração) e inscreve as políticas como declarações, de modo que, no manipulador de autorização, você pode simplesmente verificar se as declarações do usuário contêm requisito. reivindicações. Isso é para uma maneira estática de atender a um requisito de política, por exemplo, o requisito "Primeiro nome" é de natureza bastante estática. Assim,
[Authorize(Policy = nameof(Enumerations.Security.Policy.ViewRecord))]
Um requisito dinâmico pode ser a verificação da faixa etária etc., e as políticas que usam esses requisitos não podem ser pré-mapeadas para os usuários.
Um exemplo de verificação dinâmica de declarações de política (por exemplo, para verificar se um usuário tem mais de 18 anos) já está na resposta dada por @blowdart ( https://stackoverflow.com/a/31465227/4974715 ).
PS: Eu digitei isso no meu telefone. Perdoe quaisquer erros de digitação e falta de formatação.