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。
- 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.
相关产品推荐
相关产品推荐

