gpt4 book ai didi

c# - 如何在更改密码后使 token 失效

转载 作者:太空狗 更新时间:2023-10-29 20:19:13 27 4
gpt4 key购买 nike

我正在开发一个使用 JWT token 身份验证的 API。我在其背后创建了一些逻辑来使用验证码等更改用户密码。

一切正常,密码已更改。但这里有一个问题:即使用户密码已更改并且我在验证时获得了新的 JWT token ...旧 token 仍然有效。

关于如何在密码更改后刷新/使 token 无效的任何提示?

编辑:自从我听说您实际上无法使 JWT token 无效后,我就知道如何去做了。我的想法是创建一个新的用户列,其中包含类似“accessCode”的内容,并将该访问代码存储在 token 中。每当我更改密码时,我也会更改 accessCode(类似于 6 位随机数),并且在执行 API 调用时对该 accessCode 进行检查(如果 token 中使用的 accesscode 与数据库中的 accesscode 不匹配 -> 未授权返回)。

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

最佳答案

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

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

另一种可能是更好的方法是将某些数据库中的 token 列入黑名单。我认为 Redis 是最好的选择,因为它支持通过 EXPIRE 超时,因此您只需将其设置为与 JWT token 中相同的值即可。当 token 过期时,它将自动删除。

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


刷新 token 不是解决方案

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


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

您使用的是 ASP.NET Core,因此您需要找到一种方法来添加自定义 JWT 验证逻辑以检查 token 是否已失效。这可以通过 extending default JwtSecurityTokenHandler 来完成并且您应该能够从那里调用 Redis。

在配置服务中添加:

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;
}
}

然后在您更改密码或使用 token 的 jti 设置 key 以使其无效时。

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

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

临时解决方法是扩展 JwtBearerHandler 并将 HandleAuthenticateAsync 的实现替换为 override 而不调用基础,这样它会调用你的异步验证版本。然后使用 this logic添加它。

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

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

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

所以我会选择 StackExchange 的

关于c# - 如何在更改密码后使 token 失效,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52476325/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com