ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

c# – 密码更改后如何使令牌无效

2019-06-22 08:53:44  阅读:199  来源: 互联网

标签:c jwt asp-net-core-2-0 asp-net-core-webapi


我正在使用一个使用JWT令牌身份验证的API.我已经创建了一些逻辑来改变用户密码,并使用验证码&这样.

一切正常,密码就会改变.但这里有一个问题:
即使用户密码已更改,我在验证时获得了新的JWT令牌……旧令牌仍然有效.

有关密码更改后如何刷新/无效令牌的任何提示?

编辑:我已经知道如何做到这一点,因为我听说你实际上无法使JWT令牌无效.
我的想法是创建一个新的用户列,其中包含类似“accessCode”的内容,并将该访问代码存储在令牌中.每当我更改密码时,我也会更改accessCode(类似于6位数的随机数),并且在执行API调用时实现对该accessCode的检查(如果令牌中使用的访问代码与db中的访问代码不匹配 – >未经授权返回) .

你们认为这是一个好方法还是有其他方法?

解决方法:

撤销/无效的最简单方法可能只是删除客户端上的令牌,并祈祷没有人会劫持它并滥用它.

使用“accessCode”列的方法可行,但我会担心性能.

另一种可能更好的方法是在某些数据库中使用黑名单标记.我认为Redis是最好的,因为它通过EXPIRE支持超时,所以你可以将它设置为与JWT令牌中相同的值.当令牌过期时,它会自动删除.

您将需要快速响应时间,因为您必须检查令牌在每个需要授权的请求上是否仍然有效(不在黑名单或不同的accessCode中),这意味着在每个请求上使用无效的令牌调用您的数据库.

刷新令牌不是解决方案

有些人建议使用长期刷新令牌和短期访问令牌.您可以设置访问令牌,让我们说在10分钟后过期,当密码更改时,令牌仍然有效10分钟,但随后它将过期,您将不得不使用刷新令牌获取新的访问令牌.就个人而言,我对此有点怀疑,因为刷新令牌也可能被劫持:http://appetere.com/post/how-to-renew-access-tokens然后你需要一种方法来使它们无效,所以,最后,你无法避免将它们存储在某个地方.

使用StackExchange.Redis的ASP.NET Core实现

您正在使用ASP.NET Core,因此您需要找到一种方法来添加自定义JWT验证逻​​辑,以检查令牌是否无效.这可以在extending default JwtSecurityTokenHandler完成,你应该可以从那里打电话给Redis.

在ConfigureServices中添加:

services.AddSingleton<IConnectionMultiplexer>(ConnectionMultiplexer.Connect("yourConnectionString"));
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(opt =>
    {
        opt.SecurityTokenValidators.Clear();
        // or just pass connection multiplexer directly, it's a singleton anyway...
        opt.SecurityTokenValidators.Add(new RevokableJwtSecurityTokenHandler(services.BuildServiceProvider()));
    });

创建自己的例外:

public class SecurityTokenRevokedException : SecurityTokenException
{
    public SecurityTokenRevokedException()
    {
    }

    public SecurityTokenRevokedException(string message) : base(message)
    {
    }

    public SecurityTokenRevokedException(string message, Exception innerException) : base(message, innerException)
    {
    }
}

延长the default handler

public class RevokableJwtSecurityTokenHandler : JwtSecurityTokenHandler
{
    private readonly IConnectionMultiplexer _redis;

    public RevokableJwtSecurityTokenHandler(IServiceProvider serviceProvider)
    {
        _redis = serviceProvider.GetRequiredService<IConnectionMultiplexer>();
    }

    public override ClaimsPrincipal ValidateToken(string token, TokenValidationParameters validationParameters,
        out SecurityToken validatedToken)
    {
        // make sure everything is valid first to avoid unnecessary calls to DB
        // if it's not valid base.ValidateToken will throw an exception, we don't need to handle it because it's handled here: https://github.com/aspnet/Security/blob/beaa2b443d46ef8adaf5c2a89eb475e1893037c2/src/Microsoft.AspNetCore.Authentication.JwtBearer/JwtBearerHandler.cs#L107-L128
        // we have to throw our own exception if the token is revoked, it will cause validation to fail
        var claimsPrincipal = base.ValidateToken(token, validationParameters, out validatedToken); 
        var claim = claimsPrincipal.FindFirst(JwtRegisteredClaimNames.Jti);
        if (claim != null && claim.ValueType == ClaimValueTypes.String)
        {
            var db = _redis.GetDatabase();
            if (db.KeyExists(claim.Value)) // it's blacklisted! throw the exception
            {
                // there's a bunch of built-in token validation codes: https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/7692d12e49a947f68a44cd3abc040d0c241376e6/src/Microsoft.IdentityModel.Tokens/LogMessages.cs
                // but none of them is suitable for this
                throw LogHelper.LogExceptionMessage(new SecurityTokenRevokedException(LogHelper.FormatInvariant("The token has been revoked, securitytoken: '{0}'.", validatedToken)));
            }
        }

        return claimsPrincipal;
    }
}

然后在您的密码更改或任何设置密钥与令牌的jti使其无效.

限制!:JwtSecurityTokenHandler中的所有方法都是同步的,如果你想要一些IO绑定的调用,这是不好的,理想情况下,你可以在那里使用等待db.KeyExistsAsync(claim.Value).这里跟踪的问题是:https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/468很遗憾,自2016年以来没有更新:(

这很有趣,因为验证令牌的函数是异步的:https://github.com/aspnet/Security/blob/beaa2b443d46ef8adaf5c2a89eb475e1893037c2/src/Microsoft.AspNetCore.Authentication.JwtBearer/JwtBearerHandler.cs#L107-L128

一个临时的解决方法是扩展JwtBearerHandler并用覆盖替换HandleAuthenticateAsync的实现而不调用base,因此它会调用你的异步版本的validate.然后使用this logic添加它.

C#最受推荐和积极维护的Redis客户端:

> StackExchange.Redis (also used on stackoverflow)(Using StackExchange.Redis in a ASP.NET Core Controller)
> ServiceStack.Redis(商业有限)

可能会帮助你选择一个:Difference between StackExchange.Redis and ServiceStack.Redis

StackExchange.Redis has no limitations and is under the MIT license.

所以我会选择StackExchange的

标签:c,jwt,asp-net-core-2-0,asp-net-core-webapi
来源: https://codeday.me/bug/20190622/1261810.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有