繁体   English   中英

在 angular 6 的 asp.net 核心中一次性使用 Jwt 令牌进行电子邮件验证

[英]Single time use Jwt Token for email verification in asp.net core with angular 6

我,正在使用 jwt 令牌进行一次电子邮件验证。

这是生成jwt令牌的c#代码

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 导航时,它会第一次验证电子邮件。 这对我有用。

现在,当用户再次导航时,链接应该不起作用。 我如何才能限制用户进行一次电子邮件验证,或者他们是否可以通过任何方式从 asp.net 核心创建一次使用 jwt 令牌。

嗯,这是可能的。 但不建议这样做。

普通的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事件也有类似的问题。

最后,虽然可以这样做,但我建议您应该简单地将带有expiredAtinvalid的令牌存储在数据库中,并通过过滤器、模型绑定或什至手动对其进行验证。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM