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

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

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

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

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


当前回答

这个方法怎么样:

对于每个客户端请求,服务器将令牌的过期时间与(currentTime - lastAccessTime)进行比较。 如果expirationTime < (currentTime - lastAccessedTime),则将上一次lastAccessedTime更改为currentTime。 如果浏览器上的不活动时间超过了过期时间,或者如果浏览器窗口被关闭并且过期时间为> (currentTime - lastAccessedTime),那么服务器可以使令牌过期并要求用户再次登录。

在这种情况下,刷新令牌不需要额外的端点。 将感激任何反馈。

其他回答

这个方法怎么样:

对于每个客户端请求,服务器将令牌的过期时间与(currentTime - lastAccessTime)进行比较。 如果expirationTime < (currentTime - lastAccessedTime),则将上一次lastAccessedTime更改为currentTime。 如果浏览器上的不活动时间超过了过期时间,或者如果浏览器窗口被关闭并且过期时间为> (currentTime - lastAccessedTime),那么服务器可以使令牌过期并要求用户再次登录。

在这种情况下,刷新令牌不需要额外的端点。 将感激任何反馈。

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

我在Auth0工作,我参与了刷新令牌功能的设计。

这完全取决于应用程序的类型,这里是我们推荐的方法。

Web应用程序

一个好的模式是在令牌过期之前刷新令牌。

将令牌过期时间设置为一周,并在用户每次打开web应用程序和每一小时刷新令牌。如果用户超过一周没有打开应用程序,他们将不得不再次登录,这是可接受的web应用程序UX。

要刷新令牌,您的API需要一个新的端点,该端点接收有效的、未过期的JWT,并返回带有新过期字段的签名JWT。然后web应用程序将把令牌存储在某个地方。

移动/本机应用程序

大多数本机应用程序只登录一次。

其思想是,刷新令牌永远不会过期,并且可以始终将其交换为有效的JWT。

永不过期的令牌的问题在于,它永远不会意味着永远不会。如果你丢了手机怎么办?因此,它需要被用户以某种方式识别,应用程序需要提供一种撤销访问的方法。我们决定使用设备的名称,例如。玛丽:iPad。然后用户可以进入应用程序,撤销对“maryo的iPad”的访问权限。

另一种方法是撤销特定事件上的刷新令牌。一个有趣的事件是修改密码。

我们认为JWT对这些用例没有用处,所以我们使用一个随机生成的字符串,并将其存储在我们这边。

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

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

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

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

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

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

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

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

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

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

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

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