Пользовательская проверка подлинности политики в ASPNET Core 2 (проверка подлинности jwt) - PullRequest
0 голосов
/ 24 сентября 2018

У меня есть простое приложение (API), которое использует аутентификацию JWT.Я реализовал способ проверить, действителен ли токен, основываясь на некоторой информации в БД.Это выглядит примерно так ->

        //Check if the token is valid
        var tokenStamp = int.Parse(claimsIdentity.FindFirst(JwtRegisteredClaimNames.Iat)?.Value);
        if (userDto.PasswordChangedAt > tokenStamp)
        {
            ModelState.AddModelError("SessionExpired", "Please relog.");
            return BadRequest(ModelState);
        }
        //Done checking token

Я часами пытался перевести этот код в политику, чтобы вместо того, чтобы писать все, что у меня могло бы быть что-то вроде

[Authorize(Policy="MYPOLICY")]

Моя проблема здесь в том, что мне нужно получить информацию из базы данных SQL (как указано выше), чтобы выполнить проверку, и почти все, что я искал в Интернете, было просто «проверкой возраста» со статическими данными.

Я пробовал кое-что, но я, вероятно, только что сделал беспорядок (все еще немного нового для netcore).Здесь ничего не идет ->

public class TokenValidationAuthorizeAttribute : IAuthorizationRequirement
{
    public void TokenChangeValidation(AuthorizationHandlerContext context, UserService userService)
    {
        //var claimsIdentity = this.User.Identity as ClaimsIdentity;
        var tokenCreationDate = int.Parse(context.User.FindFirst(c => c.Type == JwtRegisteredClaimNames.Iat).Value);
        var userId = int.Parse(context.User.FindFirst(c => c.Type == ClaimTypes.Name).Value);
        var user = userService.GetById(userId, false);
        var userInfo = Mapper.Map<UserDto>(user);
        var tokenStamp = int.Parse(context.User.FindFirst(JwtRegisteredClaimNames.Iat)?.Value);

        TokenIssuedAt = tokenStamp;


    }

    public int TokenIssuedAt { get; set; }

    internal IAuthorizationRequirement TokenChangeValidation(AuthorizationHandlerContext context)
    {
        throw new NotImplementedException();
    }
}

public class TokenValidationHandler : AuthorizationHandler<TokenValidationAuthorizeAttribute>
{

    const string POLICY_PREFIX = "TokenValidation";
    private IUserService _userService;

    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,TokenValidationAuthorizeAttribute requirement)
    {
        var userService = _userService;

        //var claimsIdentity = this.User.Identity as ClaimsIdentity;
        var tokenCreationDate = int.Parse(context.User.FindFirst(c => c.Type == JwtRegisteredClaimNames.Iat).Value);
        var userId = int.Parse(context.User.FindFirst(c => c.Type == ClaimTypes.Name).Value);
        var user = userService.GetById(userId, false);
        var userInfo = Mapper.Map<UserDto>(user);
        var tokenStamp = int.Parse(context.User.FindFirst(JwtRegisteredClaimNames.Iat)?.Value);

        if (userInfo.PasswordChangedAt >= requirement.TokenIssuedAt)
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

Но я никоим образом не могу создать новый экземпляр TokenValidationAuthorizeAttribute, так как не могу передать контекст из ConfigureServices, поэтому следующий фрагмент кода, безусловно, не работает.

        services.AddAuthorization(options =>
        {
            options.AddPolicy("TokenOk", policy => policy.Requirements.Add(new TokenValidationAuthorizeAttribute()));
        });

Любой совет, чтобы подтолкнуть меня в правильном направлении?

1 Ответ

0 голосов
/ 25 сентября 2018

Нет необходимости иметь дублированную логику проверки токенов как в AuthorizationRequirement, так и в AuthorizationHandler.

Просто создайте фиктивное требование авторизации, в котором ничего нет:

public class TokenValidationAuthorizeAttribute : IAuthorizationRequirement
{
    // remove codes here
}

И используйте DependencyInjection, чтобы запросить IUserService:

public class TokenValidationHandler : AuthorizationHandler<TokenValidationAuthorizeAttribute>
{
    private IUserService _userService;

    public TokenValidationHandler(IUserService userService) {
        this._userService = userService;
    }

    // ...

    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, TokenValidationAuthorizeAttribute requirement)
    {
         // check the token-code of current user against the one from db here ...

         // the toekn-code of current user
         var claimsIdentity = context.User.Identity as ClaimsIdentity;
         var tokenCreationDate = int.Parse(context.User.FindFirst(c => c.Type == JwtRegisteredClaimNames.Iat).Value);

         // ...

    }
}

Ядро ASP.NET понимает, что должно проверить обработчик, нам также нужно добавить конфигурацию авторизации:

services.AddAuthorization(opts=>
{
    opts.AddPolicy("TokenOk", policy => policy.Requirements.Add(new TokenValidationAuthorizeAttribute()));
});

Наконец, не забудьте зарегистрировать обработчик авторизации и IUserService:

services.AddSingleton<IAuthorizationHandler,TokenValidationHandler>();
services.AddScoped<IUserService,YourUserServiceImplementation>();

Теперь защитите действие, которое вы хотите проверить TokenOk, с помощью [Authorize("TokenOk")], оно будет работать как положено.

...