在Retrofit中向请求头传入JWT调用后端受保护端点遇阻
Retrofit调用受保护接口的认证与解析问题
问题场景
后端有个简单的受保护接口,Postman测试完全正常:发送GET请求时设置Authorization头为bearer + JWT字符串,能拿到200 OK响应及对应内容,后端无问题。但用Retrofit复现流程时,通过OkHttpClient.Builder插入JWT到请求头,遇到两类问题:
- 直接传入JWT字符串:
Request.Builder newRequest = request.newBuilder().header("Authorization", bearerToken);
接口返回401未授权状态码。
- 按Postman格式添加
Bearer前缀后,请求直接进入onFailure方法,报错:
use jsonreader.setlenient(true) to accept malformed json at line 1 column 1
给addConverterFactory配置带setLenient()的GsonBuilder后,仍触发onFailure,报错变为:
JSON document was not fully consumed.
相关代码
CoffeeController 核心代码
public CoffeeController() { okhttpBuilder = new OkHttpClient.Builder() .addInterceptor(new Interceptor() { @NonNull @Override public okhttp3.Response intercept(@NonNull Chain chain) throws IOException { Request request = chain.request(); bearerToken = "Bearer " +LoginController.getToken(); bearerToken = LoginController.getToken(); Request.Builder newRequest = request.newBuilder().header("Authorization", bearerToken); return chain.proceed(newRequest.build()); } }); gson = new GsonBuilder() .setLenient() .create(); retrofit = new Retrofit.Builder() .baseUrl("http://10.0.2.2:8080/") .client(okhttpBuilder.build()) .addConverterFactory(GsonConverterFactory.create(gson)) .build(); } public static void CoffeeRead(Context context, TextView ResponseView) { try { CoffeeRepo repo = retrofit.create(CoffeeRepo.class); Call<String> call = repo.Read(); call.enqueue(new Callback<String>() { @Override public void onResponse(Call<String> call, Response<String> response) { message = "Read Coffee: \nToken: " +bearerToken +"\nResponse: " + response.code(); ResponseView.setText(message); Toast.makeText(context, message, Toast.LENGTH_LONG).show(); } @Override public void onFailure(Call<String> call, Throwable t) { message = "Failed to read coffee: \n" + t.getMessage(); ResponseView.setText(message); Toast.makeText(context, message, Toast.LENGTH_LONG).show(); } }); } catch (Exception e) { message = "Caught Exception: \n" + e.getMessage(); ResponseView.setText(message); Toast.makeText(context, message, Toast.LENGTH_LONG).show(); } }
CoffeeRepo 接口定义
public interface CoffeeRepo { @Headers({ "Cache-Control: max-age=3600", "User-Agent: Android" }) @GET("coffee") Call<String> Read(); }
解决思路
1. 修复Authorization头的核心问题
看Interceptor里的代码,你先给bearerToken赋值了"Bearer " + LoginController.getToken(),但紧接着又把它重新赋值为LoginController.getToken(),等于完全没加Bearer前缀,这就是直接传JWT返回401的原因。删掉多余的赋值行即可:
bearerToken = "Bearer " + LoginController.getToken(); // 删掉这行无效代码:bearerToken = LoginController.getToken();
确保最终请求头的格式和Postman一致:Bearer {你的JWT字符串}。
2. 解决JSON解析报错问题
你用Call<String>接收响应,但GsonConverterFactory会默认把响应内容当成JSON格式解析。如果后端返回的是纯文本(而非带引号的JSON字符串),Gson就会抛出解析错误。有两种解决方式:
- 方式一:改用
ResponseBody接收,手动读取内容(推荐,避免解析冲突)
修改CoffeeRepo接口:
然后在@GET("coffee") Call<ResponseBody> Read();onResponse中处理响应:@Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { if (response.isSuccessful()) { try { String content = response.body().string(); message = "Read Coffee: \nToken: " + bearerToken + "\nResponse Code: " + response.code() + "\nContent: " + content; } catch (IOException e) { message = "Failed to read response content"; e.printStackTrace(); } } else { message = "Read Coffee: \nResponse Code: " + response.code(); } ResponseView.setText(message); Toast.makeText(context, message, Toast.LENGTH_LONG).show(); } - 方式二:如果后端确实返回的是JSON格式字符串(比如
"coffee list data"),可以保持Call<String>,但要确保Gson的解析逻辑匹配返回格式。
3. 额外排查点
- 确认
LoginController.getToken()拿到的JWT和Postman中使用的完全一致,没有过期或格式错误。 - 检查是否有其他Interceptor覆盖了
Authorization头。 - 可以添加OkHttp的日志拦截器(
HttpLoggingInterceptor),打印完整的请求头和响应内容,直观排查问题。
内容的提问来源于stack exchange,提问作者Wabi-Sabi
相关产品推荐
相关产品推荐

