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

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

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

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

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


当前回答

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

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

Web应用程序

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

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

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

移动/本机应用程序

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

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

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

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

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

其他回答

这个方法怎么样:

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

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

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

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

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

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

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

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过期,用户/系统将调用 另一个url suppose /refreshtoken。与此请求一起通过的还有过期的JWT。服务器会返回一个新的JWT供用户/系统使用。

问得好——问题本身包含了丰富的信息。

文章“刷新令牌:何时使用它们以及它们如何与jwt交互”为这种场景提供了一个很好的想法。一些要点是:-

刷新令牌携带获取新访问所需的信息 令牌。 刷新令牌也会过期,但寿命相当长。 刷新令牌通常需要严格的存储要求 确保它们不会泄露。 它们还可以被授权服务器列入黑名单。

也可以看看auth0/angular-jwt angularjs

用于Web API。在AngularJS应用程序中启用OAuth刷新令牌使用ASP .NET Web API 2和Owin