如何刷新 jwt 并重新发送失败的 http 请求?

Posted

技术标签:

【中文标题】如何刷新 jwt 并重新发送失败的 http 请求?【英文标题】:How to refresh jwt and resend failed http request? 【发布时间】:2015-12-08 08:08:29 【问题描述】:

我有一个带有 jwt auth 的后端,我想处理过期的令牌。

需要以下流程:

    发出带有令牌的请求(并期望得到承诺) 如果一切正常,则返回 promise(并执行调用者的 then/fail 方法) 如果失败(401 未授权),则请求刷新令牌并在本地更新令牌 如果第 3 步成功,则返回原始请求的承诺 如果第 3 步失败并出现 401(无法刷新令牌)错误重定向到登录页面

问题: 在第 4 步中,(再次)调用了原始函数,但未触发调用者的 then/fail 方法。

以下是我将 jwt 令牌附加到 url 并发送 http 请求的方法:

var AuthenticatedRequest = function(url, data, method) 
  return (function tryRequest()
    console.log('calling tryRequest');
    return reqwest(
        url: ApiUtil.tokenUrlTo(url),
        method: method,
        crossOrigin: true,
        type: 'json',
        data: data
      )
      .fail(function(err) 
        if (err.status === 401) 
          return post('/auth/refresh-token')
            .then(function(response)
              console.log('assume token set');
              //code to update token locally
            )
            .then(tryRequest)
            .fail(function(err)
              // Can't refresh token. Send to login page
            )
          ;      
        
      )
    ;
  )();
;

这里是调用者:

fetchModules: function() 
    get('/admin/modules')
      .then(function(response) 
        Actions.modulesFetchSuccess(response.collection);
      )
      .fail(function(err) 
        Actions.modulesFetchError(ApiUtil.errorArrayForResponse(err));
      )
    ;
  ,

现在,如果我因为令牌过期而收到 401,我会触发一个新周期来刷新令牌,如本问题 Restart a promise after fail 中所建议的那样。

注意:postget 函数只是 AuthenticatedRequest 函数的包装器,其方法设置为 POSTGET

AuthenticatedRequest 函数返回一个承诺,如果令牌未过期,则运行正常,但是,当令牌过期时,我在控制台中收到错误并获取新令牌并再次调用该函数, 我的控制台截图 - http://i.stack.imgur.com/hJdId.png

但是fetchModulesthen 方法在令牌更新后不会被触发。我做错了什么?

可能的重复:

AngularJS - Handling refresh token? How to resend request when not authorized Restart a promise after fail

2015 年 9 月 13 日更新

@Bergi's 当我替换 reqwest.js 并使用 q.js 与 vanilla ajax 时,@Bergi's 答案有效,如 gist 所示

【问题讨论】:

你用的是什么promise库,reqwest是什么? fail 方法通常不链接(例如在 jQuery 中) @Bergi:我正在使用reqwest.js 啊,好吧,那个库似乎不支持真正的承诺。它确实提供了一个类似于 Promise 的界面,但它并没有像人们预期的那样工作。您应该使用适当的承诺库(选择任何一个)和cast the reqwest to one like here。 如果您选择了一个库,如果您仍有问题,我很乐意协助您应用它。请edit您的问题添加新信息。 不,如果需要,我可以更改库。 【参考方案1】:

问题是.fail 总是能发现你的错误,而不仅仅是第一次。您对tryRequest 的递归调用将包括重试本身,并且永远不会返回失败的承诺。 如果您只想重试一次,则需要将其放在外部:

function AuthenticatedRequest(url, data, method) 
  function tryRequest() 
    console.log('calling tryRequest');
    return reqwest(
      url: ApiUtil.tokenUrlTo(url),
      method: method,
      crossOrigin: true,
      type: 'json',
      data: data
    );
  
  return tryRequest().fail(function(err) 
    if (err.status !== 401) throw err;
    return post('/auth/refresh-token')
    .then(function(response) 
      console.log('assume token set');
      // code to update token locally
    )
    .then(tryRequest)
    .fail(function(err) 
      // Can't refresh token. Send to login page
    );
  );

请注意,从AuthenticatedRequest 函数将用户发送到另一个页面可能不是一个好的设计,也许可以考虑重新抛出错误(在使令牌无效之后?)并将重定向和所有内容放在调用者的错误处理程序中.

【讨论】:

我试过了,还是同样的错误,caller's then /fail 没有被调用。 我替换了 reqwest 并将您的方法与 q.js 和本机 XHR 一起使用。像魅力一样工作。 @ShivekKhurana:完美:-) 如果您需要更多选项,您还应该能够使用function xhr(options) return Q(reqwest(options)); ,而不是原生 XHR 功能。 我会记住这一点并在需要时扩展它。谢谢你,你帮了很多忙:)

以上是关于如何刷新 jwt 并重新发送失败的 http 请求?的主要内容,如果未能解决你的问题,请参考以下文章

页面刷新时 JWT Token 已从标题中删除,如何修复?

JWT 令牌刷新后返回重新调用

如何在jwt中过期时刷新令牌

java 点击浏览器刷新按钮和回退前进按钮时是发送新的http请求么

如何在 React.js 应用程序中刷新 JWT 令牌?

Firebase php-jwt 令牌刷新