我希望对新的REST API实现基于jwt的身份验证。但是由于到期时间是在令牌中设置的,那么是否可以自动延长它呢?我不希望用户每隔X分钟就需要登录一次,如果他们在这段时间内积极使用应用程序的话。这将是一个巨大的用户体验失败。

但是延长过期时间会创建一个新的令牌(旧的令牌在过期前仍然有效)。在每个请求后生成一个新的令牌对我来说听起来很傻。当多个令牌同时有效时,听起来像是一个安全问题。当然,我可以使用黑名单使旧的使用无效,但我需要存储令牌。JWT的好处之一是没有存储空间。

我发现Auth0是如何解决这个问题的。他们不仅使用JWT令牌,还使用refresh令牌: https://auth0.com/docs/tokens/refresh-tokens

但是,要实现这一点(没有Auth0),我需要存储刷新令牌并维护它们的过期。那么真正的好处是什么呢?为什么不只有一个令牌(不是JWT)并将过期时间保存在服务器上呢?

还有其他选择吗?使用JWT不适合这种情况吗?


当前回答

另一种使jwt失效的解决方案是在用户表上实现一个新的jwt_version整数列,而不需要在后端进行任何额外的安全存储。如果用户希望注销或过期现有令牌,他们只需增加jwt_version字段。

在生成一个新的JWT时,将jwt_version编码到JWT有效负载中,如果新的JWT应该替换所有其他JWT,则可以选择提前增加该值。

在验证JWT时,jwt_version字段将与user_id进行比较,只有匹配时才授予授权。

其他回答

services.Configure (Configuration.GetSection(“ApplicationSettings”));

        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); 

        services.AddDbContext<AuthenticationContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("IdentityConnection")));

        services.AddDefaultIdentity<ApplicationUser>()
            .AddEntityFrameworkStores<AuthenticationContext>();

        services.Configure<IdentityOptions>(options =>
        {
            options.Password.RequireDigit = false;
            options.Password.RequireNonAlphanumeric = false;
            options.Password.RequireLowercase = false;
            options.Password.RequireUppercase = false;
            options.Password.RequiredLength = 4;
        }
        );

        services.AddCors();

        //Jwt Authentication

        var key = Encoding.UTF8.GetBytes(Configuration["ApplicationSettings:JWT_Secret"].ToString());

        services.AddAuthentication(x =>
        {
            x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            x.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
        }).AddJwtBearer(x=> {
            x.RequireHttpsMetadata = false;
            x.SaveToken = false;
            x.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
            {
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(key),
                ValidateIssuer = false,
                ValidateAudience = false,
                ClockSkew = TimeSpan.Zero
            };
        });
    }

另一种使jwt失效的解决方案是在用户表上实现一个新的jwt_version整数列,而不需要在后端进行任何额外的安全存储。如果用户希望注销或过期现有令牌,他们只需增加jwt_version字段。

在生成一个新的JWT时,将jwt_version编码到JWT有效负载中,如果新的JWT应该替换所有其他JWT,则可以选择提前增加该值。

在验证JWT时,jwt_version字段将与user_id进行比较,只有匹配时才授予授权。

实际上,我使用Guzzle客户端在PHP中实现了这一点,为api制作了一个客户端库,但这个概念应该适用于其他平台。

基本上,我发行了两个代币,一个短的(5分钟),一个长的,一周后到期。如果客户机库接收到对某个请求的401响应,则使用中间件尝试对短令牌进行一次刷新。然后,它将再次尝试原始请求,如果它能够刷新,就会获得正确的响应,对用户透明。如果失败了,它会把401发给用户。

如果短令牌过期了,但仍然是可信的,而长令牌是有效且可信的,它将使用长令牌验证的服务上的一个特殊端点刷新短令牌(这是它唯一可以用于的事情)。然后,它将使用短令牌来获得一个新的长令牌,从而在每次刷新短令牌时将其延长一周。

这种方法还允许我们在最多5分钟内撤销访问,这对于我们的使用是可以接受的,而不必存储令牌黑名单。

后期编辑:在我脑海中记忆犹新的几个月后重新阅读这篇文章,我应该指出,你可以在刷新短令牌时撤销访问权,因为它为更昂贵的调用提供了机会(例如调用数据库来查看用户是否已被禁止),而无需为每一次对你的服务的调用付费。

今天,许多人选择使用jwt进行会话管理,却没有意识到他们为了简单而放弃了什么。我的回答详细阐述了问题的第二部分:

那么真正的好处是什么呢?为什么不只有一个令牌(不是JWT)并将过期时间保存在服务器上呢? 还有其他选择吗?使用JWT不适合这种情况吗?

jwt能够支持基本的会话管理,但有一些限制。由于是自描述令牌,它们在服务器端不需要任何状态。这使得他们很有吸引力。例如,如果服务没有持久层,它就不需要仅仅为了会话管理而引入持久层。

然而,无国籍也是他们缺点的主要原因。由于它们只发布一次,内容固定且到期,因此您无法使用典型的会话管理设置完成您想做的事情。

也就是说,您不能按需使它们失效。这意味着您无法实现安全注销,因为没有办法使已经发出的令牌过期。出于同样的原因,您也不能实现空闲超时。一个解决方案是保留一个黑名单,但这会引入状态。

我写了一篇文章详细解释了这些缺点。需要明确的是,您可以通过添加更复杂的内容(滑动会话、刷新令牌等)来解决这些问题。

至于其他选项,如果您的客户端仅通过浏览器与您的服务交互,我强烈建议使用基于cookie的会话管理解决方案。我还列出了目前在web上广泛使用的认证方法。

JWT的想法很好,你把你需要的东西放进JWT,然后无状态化。 两个问题:

糟糕的JWT标准化。 JWT是不可能失效的,如果创建快速到期,它会迫使用户频繁登录。

1的解。使用自定义JSON:

 {"userId": "12345", "role": "regular_user"}

使用对称(AES)算法加密它(它比使用非对称算法签名更快),并将其放入快速过期的cookie中。我仍然会称它为JWT,因为它是JSON,并且在Web应用程序中用作令牌。现在,服务器检查cookie是否存在,其值是否可以解密。

2的解。使用刷新令牌:

将userId作为12345,对其加密,并将其放入长期过期的cookie中。不需要在DB中为刷新令牌创建一个特殊字段。

现在,每次访问令牌(JWT) cookie过期时,服务器都会检查刷新令牌cookie,解密,获取值,并在DB中查找用户。如果找到用户,则生成一个新的访问令牌,否则(或者如果刷新令牌也过期)强制用户登录。

最简单的替代方法是使用刷新令牌作为访问令牌,即根本不使用JWT。

使用JWT的优点是,在它的过期时间内,服务器不会访问DB。即使我们在cookie中放入一个过期时间只有2分钟的访问令牌,对于像eBay这样繁忙的应用程序,它也会导致每秒避免数千DB的点击。