【问题标题】:Run AJAX Request After JWT RefreshJWT 刷新后运行 AJAX 请求
【发布时间】:2018-02-09 16:58:12
【问题描述】:

我有一个现有的单页应用程序,已转换为 JWT 而不是 PHP 会话。它运行良好,但我试图弄清楚如何在 AJAX 请求之前刷新 JWT(如有必要)。

一点背景知识:用户登录后,我存储了一个 JWT(10 分钟到期)和一个 Refresh Token(2 小时到期)。我有以下函数,我想在每次 AJAX 调用 API 之前运行它。该函数检查 JWT 是否过期,如果是,则通过 Refresh 令牌检索新的。显然,在 API 中,我在允许发回数据之前检查了 JWT 和 Refresh 令牌的有效性。这部分运行良好。

    function checkTokenExp(){
     var validToken = false;   // DEFAULT TO EXPIRED
     var apiClaims = localStorage.getItem('apiToken').split('.');
     var api = JSON.parse(atob(apiClaims[1]));
     var apiExpiration = new Date(api['exp'] * 1000);
     var now = new Date();
     if (apiExpiration < now) {
       // API TOKEN EXPIRED - CHECK REFRESH TOKEN
       var refreshClaims = localStorage.getItem('refreshToken').split('.');
       var refresh = JSON.parse(atob(refreshClaims[1]));
       var refreshExpiration = new Date(refresh['exp'] * 1000);
       if (refreshExpiration > now) {
         //  REFRESH TOKEN NOT EXPIRED - NEED NEW API TOKEN
         $.ajax({
           type: "GET",
           url: 'api/session/token',
           contentType: 'application/json',
           beforeSend: function (xhr) {
             xhr.setRequestHeader("Authorization", 'Bearer '+localStorage.getItem('refreshToken'));
           }
         }).done(function (data) {
           var data = jQuery.parseJSON(data);
           if (data.status == 'success'){
             localStorage.setItem('apiToken', data.apiToken);
           }
           validToken = true;
         });
       }else{
         //  REFRESH TOKEN EXPIRED - FORCE LOG OUT
         $("#SessionExpiredModal").modal('show');
       }
     }else{
       //  API TOKEN NOT EXPIRED
       validToken = true;
     }
     return validToken;
   }

我遇到问题的地方是下面的代码(以及所有其他 AJAX 调用)。如您所见,我试图在此 AJAX 调用之前运行上面的函数并验证我的 JWT(如果它无效,则请求并存储一个新的),然后使用新的 JWT 运行 AJAX 调用。除了 AJAX 返回 401 未授权之外,一切正常,因为它仍在发送以前的 JWT(显然已过期)。因此,似乎在上述函数完成并存储新的 JWT 之前发送了 AJAX。但我知道我的函数运行正确并且 JWT 已更新,因为我第二次单击该按钮时它运行正确,并且我从 API 得到了正确的回复。

$('#BtnTest').on('click', function(){
     $.ajax({
      type: "GET",
      url: 'api/random',
      contentType: 'application/json',
      beforeSend: function (xhr) {
        checkTokenExp();  // SHOULD BE SAVING NEW TOKEN
        xhr.setRequestHeader("Authorization", 'Bearer '+ localStorage.getItem('apiToken'));  // SHOULD BE SENDING NEW TOKEN
      }
    }).done(function (response) {
      alert(response);
    })
   });

我希望这是有道理的,并提前感谢您的帮助。另外,有没有一种简单的方法可以让我的函数在每次 ajax 调用之前运行,而不必在每个 AJAX 语句中对其进行编码。

【问题讨论】:

    标签: javascript jquery ajax token jwt


    【解决方案1】:

    您需要向checkTokenExp() 添加回调,以便在刷新 API 令牌后完成您的 API 调用。

    现在发生了什么:

    1. 你准备打电话给api/random
    2. beforeSend 执行 checkTokenExp()
    3. 如果令牌过期,它会异步请求另一个令牌
    4. 实际调用api/random,可能在服务器使用新令牌回复第三步之前

    看看jQuery's ajaxPrefilter

    你可以试试这样的:

    var currentRequests = {};
    
    $.ajaxPrefilter(function( options, originalOptions, jqXHR ) {
        // Skip URLs that don't need a token
        if( options.url == 'your api/session/token URL' ) {
            console.log("No ajax prefilter on api/session/token");
            return;
        }
    
        successCb = function() {
            currentRequests[ options.url ] = jqXHR;
        };
        errorCb = function() {
            console.error("Could not refresh token, aborting Ajax call to " + options.url);
            currentRequests[ options.url ].abort();
        };
        checkTokenExp(successCb, errorCb);
    });
    
    
    
    function checkTokenExp(successCb, errorCb){
        // ...
        if (refreshExpiration > now) {
            //  REFRESH TOKEN NOT EXPIRED - NEED NEW API TOKEN
            $.ajax({
                type: "GET",
                url: 'api/session/token',
                contentType: 'application/json',
            }).done(function (data) {
                var data = jQuery.parseJSON(data);
                if (data.status == 'success'){
                    localStorage.setItem('apiToken', data.apiToken);
                    successCb(); // the new token is set, you can make ajax calls
                }
                // TODO: log error here, don't do validToken = true
            }).fail(errorCb(data));
        }else{
            //  REFRESH TOKEN EXPIRED - FORCE LOG OUT
            $("#SessionExpiredModal").modal('show');
        }
        }else{
            //  API TOKEN NOT EXPIRED
            successCb();
        }
    }
    

    【讨论】:

    • 感谢您的提示。它还没有完全工作。正如我所说,这对我来说有点新,我不熟悉 ajaxPrefilter。请告诉我我对此的理解是否正确。 Prefilter 在每次 ajax 调用之前运行我的函数。在 checkTokenExp() 的某个地方,它会触发 successCb() 并允许 ajax 请求继续。那是对的吗。添加的 Prefilter 可以正常工作,直到令牌过期,然后它像以前一样失败。在保存新令牌之前,似乎第一个 ajax 调用仍在完成。
    • 我能够使用您建议的链接之一中的代码解决这个问题 - https://stackoverflow.com/questions/41118347/prevent-ajax-request-till-new-token-is-obtained/41118703。我的下一个问题是这个解决方案是否安全。如果访问令牌以某种方式被更改,服务器将返回 401,然后通过刷新令牌请求新令牌。这有什么要注意的吗?
    • 错误:我没有完整的代码,因此很难进一步提供帮助。如果您还没有,请了解如何使用浏览器的开发人员工具和 JavaScript 调试。您将能够看到正在发生的事情。问题:请将您的问题标记为已回答,以便社区知道您不再需要帮助。安全性:服务器可能会实施某种临时禁令。
    猜你喜欢
    • 1970-01-01
    • 2021-04-22
    • 2013-10-24
    • 1970-01-01
    • 1970-01-01
    • 2011-07-06
    • 2017-07-03
    • 1970-01-01
    • 2020-05-25
    相关资源
    最近更新 更多