You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

不使用Retrofit时OkHttpClient Authenticator刷新Token的回调处理问题

问题与解决方案

问题描述

我在应用中刷新Access Token时遇到了回调处理的问题:需要在authenticate方法中返回携带新Token的请求,但getAuthRequest是异步的(void方法),没法在回调里把生成的Request传递出去,也没法在回调内部直接返回给authenticate方法。代码示例如下:

public Request authenticate(Route route, Response response) throws IOException {
    // GetAuthRequest是void方法,无法在回调中赋值String值。
    getAuthRequest(new AuthResponse() {
        @Override
        public Request onSuccess(String token) {
            return response.request().newBuilder()
                    .header("Authorization", "Bearer " + token)
                    .build();
        }
    });
}

解决方法

核心矛盾是OkHttp的authenticate方法为同步执行,而异步请求的回调无法直接为该方法提供返回值,解决思路是将刷新Token的操作改为同步调用,或通过阻塞等待的方式获取异步回调结果。

方案一:重构为同步请求(推荐)

将异步的getAuthRequest改造成同步方法,直接在authenticate内获取新Token并生成请求:

  1. 新增同步获取Token的方法:
public String getAuthRequestSync() throws IOException {
    // 构造刷新Token的同步请求
    Request refreshRequest = new Request.Builder()
            .url("你的刷新Token接口地址")
            // 根据实际接口调整请求方式、参数
            .post(RequestBody.create(MediaType.parse("application/json"), "{\"refresh_token\":\"你的刷新Token\"}"))
            .build();

    // 同步执行请求并解析响应
    try (Response refreshResponse = okHttpClient.newCall(refreshRequest).execute()) {
        if (!refreshResponse.isSuccessful()) {
            throw new IOException("刷新Token失败,响应码:" + refreshResponse.code());
        }
        // 解析响应体获取新Token,此处示例用Gson解析,根据实际情况调整
        String respBody = refreshResponse.body().string();
        TokenResp tokenResp = new Gson().fromJson(respBody, TokenResp.class);
        return tokenResp.getAccessToken();
    }
}
  1. 在authenticate中调用同步方法:
@Override
public Request authenticate(Route route, Response response) throws IOException {
    // 同步获取新Token
    String newToken = getAuthRequestSync();
    // 生成并返回携带新Token的请求
    return response.request().newBuilder()
            .header("Authorization", "Bearer " + newToken)
            .build();
}

方案二:用CountDownLatch阻塞等待异步回调(临时方案)

如果无法修改getAuthRequest的异步逻辑,可以用CountDownLatch阻塞当前线程,等待回调完成后获取结果:

@Override
public Request authenticate(Route route, Response response) throws IOException {
    CountDownLatch latch = new CountDownLatch(1);
    Request[] newRequestHolder = new Request[1]; // 用数组存储回调结果,规避匿名内部类无法修改外部变量的限制

    getAuthRequest(new AuthResponse() {
        @Override
        public Request onSuccess(String token) {
            newRequestHolder[0] = response.request().newBuilder()
                    .header("Authorization", "Bearer " + token)
                    .build();
            latch.countDown(); // 通知等待线程任务完成
            return newRequestHolder[0];
        }

        @Override
        public void onFailure() {
            latch.countDown();
            // 此处可添加失败处理逻辑
        }
    });

    try {
        // 设置超时时间,避免无限阻塞
        if (!latch.await(10, TimeUnit.SECONDS)) {
            throw new IOException("刷新Token超时");
        }
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        throw new IOException("等待刷新Token时被中断", e);
    }

    if (newRequestHolder[0] == null) {
        throw new IOException("刷新Token失败");
    }
    return newRequestHolder[0];
}

注意事项

  • 同步请求会阻塞当前线程,需合理设置超时时间,避免影响应用响应速度。
  • 无论哪种方案,都要处理刷新Token失败的场景,及时抛出异常终止请求。
  • 优先选择同步请求方案,代码更简洁易维护。

内容的提问来源于stack exchange,提问作者Daniel Hernández

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 08:35:30