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

Android中Retrofit带参数双Header请求报401未授权问题求助

解决Retrofit请求带双Header返回401的问题

Hey Pablo,咱们来一步步拆解你遇到的401认证失败问题~

首先复盘你尝试的三种写法,再重点说说你提到的token更新这个核心坑:

1. 三种写法的问题分析

  • 第一种写法(静态@Headers+动态@Header):
    这个写法本身是完全正确的!你也提到Robert的回答确认了这点。那为啥会401?大概率是两个细节:

    • token格式错误:你调用时写的" Bearer " + token,前面多了个空格!标准的Bearer认证格式是Bearer (仅后面一个空格)紧跟token,正确写法应该是"Bearer " + MySharedUser.getInstance().getToken(),多余的空格会导致服务端解析token失败。
    • token已过期:这就是你提到的核心问题——token更新机制没跟上,传了失效的旧token。
  • 第二种写法(双动态@Header):
    写法本身没问题,但把Content-Type作为动态参数传入完全没必要,用静态@Headers固定写死更简洁,除非你需要在不同请求里切换Content-Type,否则第一种写法更优。同样,401的原因还是token的有效性或格式问题。

  • 第三种写法(静态@Headers带占位符):
    这个写法直接踩了Retrofit的坑——@Headers是静态注解,不支持{token}这种动态参数替换!服务端会直接收到Authorization: Bearer {token}的Header,自然认证失败,这种写法直接排除。

2. 核心解决方案:用拦截器统一管理Header+自动刷新token

手动在每个接口传token不仅麻烦,还没法自动处理token过期的情况。推荐用Retrofit的Interceptor来统一处理:

第一步:实现认证拦截器

public class AuthInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request originalRequest = chain.request();
        
        // 获取当前有效token
        String currentToken = MySharedUser.getInstance().getToken();
        // 构建带标准Header的请求
        Request authenticatedRequest = originalRequest.newBuilder()
                .header("Authorization", "Bearer " + currentToken)
                .header("Content-Type", "application/json")
                .build();
        
        Response response = chain.proceed(authenticatedRequest);
        
        // 拦截401响应,自动刷新token并重试
        if (response.code() == 401) {
            // 调用刷新token接口获取新token(注意这里要用不带拦截器的OkHttpClient,避免循环调用)
            String newToken = refreshAuthToken();
            if (newToken != null && !newToken.equals(currentToken)) {
                // 保存新token到SharedPreferences
                MySharedUser.getInstance().saveToken(newToken);
                // 用新token重新发起请求
                Request newAuthenticatedRequest = originalRequest.newBuilder()
                        .header("Authorization", "Bearer " + newToken)
                        .header("Content-Type", "application/json")
                        .build();
                return chain.proceed(newAuthenticatedRequest);
            }
        }
        return response;
    }
    
    // 实现你的token刷新逻辑
    private String refreshAuthToken() throws IOException {
        // 示例:用原生OkHttpClient调用刷新接口
        OkHttpClient refreshClient = new OkHttpClient.Builder().build();
        Request refreshRequest = new Request.Builder()
                .url("你的baseUrl/refresh-token")
                .post(RequestBody.create(MediaType.parse("application/json"), "你的刷新参数"))
                .build();
        try (Response refreshResponse = refreshClient.newCall(refreshRequest).execute()) {
            if (refreshResponse.isSuccessful()) {
                // 解析返回的新token
                return refreshResponse.body().string();
            }
        }
        return null;
    }
}

第二步:给OkHttpClient添加拦截器

OkHttpClient okHttpClient = new OkHttpClient.Builder()
        .addInterceptor(new AuthInterceptor())
        .build();

// 初始化Retrofit
Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("你的baseUrl")
        .client(okHttpClient)
        .addConverterFactory(GsonConverterFactory.create())
        .build();

第三步:简化接口定义

现在接口里不需要再写Header相关的注解了,直接定义接口即可:

@GET("/ebuzon/client/show")
Call<Client> showClient();

3. 额外排查点

  • 用Logcat或抓包工具打印实际发送的请求Header,确认Authorization的格式和内容是否正确
  • 确认服务端的认证逻辑是否接受Bearer前缀的token,以及token的有效期是否正常
  • 检查SharedPreferences中保存的token是否正确,有没有被意外覆盖或清空

内容的提问来源于stack exchange,提问作者Pablo R.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 03:16:52