改造无法将新令牌设置为请求标头

Posted

技术标签:

【中文标题】改造无法将新令牌设置为请求标头【英文标题】:Retrofit unable to set new Token to Header of Requests 【发布时间】:2018-06-29 16:15:28 【问题描述】:

我有一个改造客户端,它可以帮助我为我向 REST API 发出的任何请求设置标头。在用户登录时,我从服务器获取令牌并将此令牌设置为请求的标头。我将此令牌保存到 SharedPreferences 以便我可以在需要向我的 REST API 发出请求时获取它。问题是,每当我在新用户登录时为我的 SharedPreferences 文件设置一个新令牌时,它仍然会获取旧令牌,而不是保存这个新令牌以用于将来的请求。

这是我下面的改造客户端:

public class RetrofitClient 

    private static Retrofit retrofit = null;

    public static Retrofit getClient(String token) 

        HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
        logging.setLevel(HttpLoggingInterceptor.Level.BODY);
        OkHttpClient okClient = new OkHttpClient();

        Gson gson = new GsonBuilder()
                .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
                .create();

        okClient.interceptors().add(chain -> chain.proceed(chain.request()));

        okClient.interceptors().add(chain -> 
            Request original = chain.request();
            Request request = original.newBuilder()
                    .header(Config.X_AUTH_TOKEN, "Bearer" + " " + token)
                    .method(original.method(), original.body())
                    .build();
            Log.d("Authorization", token);

            return chain.proceed(request);
        );

        okClient.interceptors().add(logging);

        if (retrofit==null) 
            retrofit = new Retrofit.Builder()
                    .baseUrl(Config.BASE_URL1)
                    .client(okClient)
                    .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                    .addConverterFactory(GsonConverterFactory.create(gson))
                    .build();
        
        return retrofit;
    


这是我设置和获取令牌的代码

public String getToken() 
    return prefs.getString(AuthUser.USER_TOKEN, "");


public void setToken(String token) 
    SharedPreferences.Editor editor = prefs.edit();
    editor.putString(AuthUser.USER_TOKEN, token);
    editor.apply();

这是我调用 set token 方法将新令牌保存到 SharedPreference 的地方

 authUser.setToken(token);

【问题讨论】:

请显示调用 setToke/getToken 方法的代码。这一行不足以理解发生了什么。 @algrid 我在其他地方阅读了一个解决方案,但我不知道如何实现它。这就是用户所说的“这是因为您添加的拦截器在 httpClient 中仍然存在。一旦您删除了令牌(或将 null 传递给 createService() 方法,您也需要删除拦截器。” 你必须编写拦截器来附加新令牌 @IshanFernando 你能展示一段代码 sn-p 来编写拦截器吗? @LendingSquare 我添加了示例代码 【参考方案1】:

我完全不明白这有多令人惊讶。您的 RetrofitClient 是一个令人困惑的(并且可以说是写得不好)的单身人士。让我们来看看这会失败的典型情况。

您使用之前保存的令牌启动您的应用程序。起初一切正常。在某些时候,您调用 RetrofitClient.getClient(token) 并且所有请求都会成功。一段时间后,服务器使令牌无效。您可能会从服务器收到 403 响应,再次启动登录屏幕并在 SharedPreferences 中更新您的令牌。这是您的问题开始的地方。尽管您正确保存了新令牌,但您的 RetrofitClient 将执行单例操作并继续返回存储在 private static Retrofit retrofit 字段中的自身的第一个实例化。

一种快速的解决方法是向您的RetrofitClient 添加一个无效方法。类似的东西。

public static void invalidate() 
  this.retrofit = null;

在收到 403 响应或注销时调用它。

PS:请在getClient 方法的开头移动以下行if (retrofit==null) 。新建一个okHttp客户端,白费,每次有人打电话getClient就是浪费。

【讨论】:

非常感谢您的回答 没问题,很高兴它有帮助【参考方案2】:

您可以编写拦截器在每次网络请求发生之前执行。创建新文件作为 HeaderIntercepter

public class HeaderIntercepter implements Interceptor 
@Override
public Response intercept(Chain chain) throws IOException 
    Request request = chain.request();
    String token = context.getSharedPreferences(FILENAME, MODE_PRIVATE).getString("TOKEN","");

    Request tokenRequest = request.newBuilder()
            .addHeader("Authorization", "Bearer " + token)
            .addHeader("Content-Type", "application/json")
            .build();

    return chain
            .proceed(tokenRequest);


给okHttpclient添加拦截器

 OkHttpClient.Builder builder = new OkHttpClient.Builder()
            .addInterceptor(new HeaderIntercepter());

【讨论】:

先生,在这个类中context将如何被传递?如果我没记错的话,我们必须在添加拦截器时通过 RetrofitClient 类的 HeaderInterceptor 类的构造函数传递上下文。所以每次我们调用 RetrofitClient 的静态方法 getClient() 时,我们都需要传递上下文。这个过程对吗?还是有其他方法? 是的,这就是我实施的方式。我不知道其他方式。如果我发现任何东西,我会发布它@HasanAbdullah

以上是关于改造无法将新令牌设置为请求标头的主要内容,如果未能解决你的问题,请参考以下文章

如何使用带有标头的改造来检索 JSON?

有没有一种方法可以使用改造来刷新带有计时器的令牌?

在 ember-file-upload 的请求标头中添加身份验证令牌

如何将承载令牌添加到 HTTP 请求的标头?

如何在 Android OKHTTPClient 请求上设置(OAuth 令牌)授权标头

如何在 Android OKHTTPClient 请求上设置(OAuth 令牌)授权标头