关于android:何时用刷新令牌交换访问令牌

When to exchange access token with refresh token

我理解使用 OAuth2 的流程是:

在短期访问令牌过期后(服务器返回 401),客户端必须使用刷新令牌请求新的。

要在 iOS(使用 AFNetworking)或 Android(使用 Volley)应用程序中实现它,我想网络管理器必须能够检测返回的 401 错误,然后向身份验证服务器发送请求。

问题在于网络的并发使用。考虑访问已过期的场景,应用程序发送 2 个请求:req1 和 100 毫秒后,req2。绘制在时间轴上,如下所示:

1
2
req1 --> 401 --> (refresh req) --> OK, new access and fresh tokens --> retry req1
  req2 --> 401 --> (refresh req) --> 403, wrong refresh token

最终的结果是req2会失败,应用会因为403错误退出用户。

所以我的问题是

这个实现是否朝着正确的方向发展?还是收到401后刷新不对?我是否应该在用户启动应用程序时刷新令牌(以减慢应用程序启动为代价)

如何解决并发问题?


由于您有一个现有的令牌管理器,我会在其中添加一些额外的逻辑(在 Java 中):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class TokenManager {

  private String accessToken;
  private CompletableFuture<String> accessTokenRefreshComletableFuture;

  public CompletableFuture<String> getAccessToken() {
    if (this.accessToken is expired) {
       // If refreshed accessToken is being requested
       CompletableFuture<String> runningRequestFuture = this.accessTokenRefreshComletableFuture;
       if (runningRequestFuture == null) {
          // For thread safety, this assignment should be synchronized (or made atomic)
          // with the previous reading
          this.accessTokenRefreshComletableFuture = new CompletableFuture<>();
          // Request a fresh access token.
          // When you get a new access token, set the this.accessTokenRefreshComletableFuture
          // complete and remove its reference from the manager class.
       }
       return runningRequestFuture;
    }
    // Synchronous result
    return CompletableFuture.completedFuture(this.accessToken);
  }
}

管理器不返回访问令牌,而是返回 CompletableFuture(JavaScript 中的 Promise - 异步结果)。如果需要刷新访问令牌,请先检查 /token 端点请求是否已在运行。如果是,则返回它的 CompletableFuture.

这样,您将始终拥有一个有效的访问令牌或单个 CompletableFuture 等待新的访问令牌。