Однократное использование токена Jwt для проверки электронной почты в ядре asp.net с angular 6 - PullRequest
0 голосов
/ 10 октября 2018

Я использую токен jwt для одноразовой проверки электронной почты.

Вот код c # для генерации токена jwt

public string OneTimeTokenGenerationForVerification(string userName, int expireTime, string secretToken)
        {
            var tokenHandler = new JwtSecurityTokenHandler();
            var key = Encoding.ASCII.GetBytes(secretToken);
            var tokenDescriptor = new SecurityTokenDescriptor
            {
                Subject = new ClaimsIdentity(new Claim[]
                {
                    new Claim(ClaimTypes.Name, userName)
                }),
                Expires = DateTime.UtcNow.AddHours(expireTime),
                SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
            };
            var token = tokenHandler.CreateToken(tokenDescriptor);
            return tokenHandler.WriteToken(token);
        }

На угловой стороне.Я проверяю, истек ли токен, или не использует приведенный ниже код.

public jwtTokenValidation(token : string) : boolean {
        let jwtHelper: JwtHelperService = new JwtHelperService();
        if (token != null) {
            // Check whether the token is expired and return true or false
            return !jwtHelper.isTokenExpired(token);
        }
        return false;
    }

Приведенный выше метод вернет false, если срок действия токена истек.С тех пор, как я установил 2 часа истечения срока действия, значит, токен истекает через 2 часа.Но когда пользователь получает навигацию по URL-адресу электронной почты, он впервые проверяет электронную почту.Это работает для меня.

Теперь, когда пользователь переходит снова, ссылка не должна работать.Как, я могу ограничить одноразовую проверку пользователей электронной почтой или это один из способов создания одноразового использования токена jwt из ядра asp.net.

1 Ответ

0 голосов
/ 11 октября 2018

Ну, это возможно.Но это не рекомендуется.

Простой токен JWT обычно хранится на стороне клиента, и сервер просто проверяет его, когда получает токен JWT.При проверке токена JWT сервер считается каким-то образом «не имеющим состояния».Они просто проверяют подпись, издателя, аудиторию, время жизни и т. Д.

Поэтому, если мы хотим получить одноразовый токен, нам нужно поддерживать состояние на стороне сервера , чтобы предотвратить Повторить атаку на стороне клиента.Другими словами, у нас будет состояние, связанное с токеном JWT, поэтому мы можем вызвать делегата, чтобы определить, был ли токен использован при получении или проверке токена JWT.Например, мы можем использовать OnMessageReceived, чтобы сделать это:

services.AddAuthentication()
.AddJwtBearer(options =>{
    // options.TokenValidationParameters=  ...

    options.Events = new JwtBearerEvents() {
        OnMessageReceived= async (context) => {
            var tokenService = context.HttpContext.RequestServices.GetRequiredService<IOneTimeOnlyJwtTokenService>();
            if (context.Request.Headers.ContainsKey("Authorization") ){
                var header = context.Request.Headers["Authorization"].FirstOrDefault();
                if (header.StartsWith("Bearer",StringComparison.OrdinalIgnoreCase)){
                    var token = header.Substring("Bearer".Length).Trim();
                    context.Token = token;
                }
            }
            if (context.Token == null) {
                return;
            }
            if (await tokenService.HasBeenConsumed(context.Token))
            {
                context.Fail("not a valid token");
            }
            else {
                await tokenService.InvalidateToken(context.Token);
            }
        }
    };
});

IOneTimeOnlyJwtTokenService - фиктивная служба, помогающая справиться с генерацией токена и сделать токен недействительным:

public interface IOneTimeOnlyJwtTokenService
{
    Task<string> BuildToken(string name, int? expires);
    Task<bool> HasBeenConsumed(string token);
    Task InvalidateToken(string token);
}

Вот реализация:

public class JwtTokenService : IOneTimeOnlyJwtTokenService
{
    private readonly IConfiguration _config;


    public JwtTokenService(IConfiguration config )
    {
        _config = config;

    }

    /// <summary>
    /// Builds the token used for authentication
    /// </summary>
    /// <param name="email"></param>
    /// <returns></returns>
    public async Task<string> BuildToken(string userName,int? expireTime)
    {
        var claims = new[] {
            new Claim(ClaimTypes.Name, userName),
        };

        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
        var sign= new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

        var expiredAt = expireTime != null ?
            DateTime.UtcNow.AddHours((int)expireTime) :
            DateTime.Now.AddHours(int.Parse(_config["Jwt:ExpireTime"]));

        var tokenDescriptor = new SecurityTokenDescriptor
        {
            Subject = new ClaimsIdentity(claims) ,
            Expires = expiredAt,
            Issuer = _config["Jwt:Issuer"],
            Audience = _config["Jwt:Audience"],
            SigningCredentials = sign,
        };

        var tokenHandler = new JwtSecurityTokenHandler();
        var token= tokenHandler.CreateToken(tokenDescriptor);
        var tokenString=tokenHandler.WriteToken(token);
        await this.RegisterToken(tokenString);
        return tokenString;
    }

    private async Task RegisterToken(string token) 
    {
        // register token 
    }

    public async Task<bool> HasBeenConsumed(string token) 
    {
        // check the state of token
    }

    public async Task InvalidateToken(string token) 
    {
         // persist the invalid state of token
    }

}

Хорошо, наверняка, это работает.Но если в этом случае делегат будет вызываться каждый раз, когда мы получим сообщение.Аналогичная проблема возникает с событием OnTokenValidated.

Наконец, хотя это и возможно, я предлагаю вам просто сохранить токен с expiredAt и invalidв базе данных и проверьте его с помощью фильтра, привязки модели или даже вручную.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...