刷新令牌时,匕首2获得旧令牌

时间:2019-01-12 11:10:13

标签: android dagger-2

我在使用匕首2时遇到问题。简要地说,匕首2在片段中进行注入,并且令牌已过期。 TokenAuthenticator要求保存在SharedPreferences中的新令牌。

不会重新创建我的片段,并且匕首2使用过期的令牌而不是新令牌来进行呼叫。

现在我将详细解释。

匕首2

我的匕首2逻辑微不足道。

NetworkModule.java

@Provides
@Nullable
String provideAuthToken(Context context) {
    return AccountUtils.getCurrentUserToken(context);
}

@Provides
AuthenticationInterceptor provideInterceptor(@Nullable String authToken) {
    return new AuthenticationInterceptor(authToken);
}

@Provides
TokenAuthenticator provideAuthenticator(Context context, @Nullable String authToken) {
    return new TokenAuthenticator(context, authToken);
}

@Provides
OkHttpClient.Builder provideOkHttpClientBuilder(AuthenticationInterceptor interceptor, TokenAuthenticator authenticator) {
    OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder();
    okHttpClientBuilder.connectTimeout(15, TimeUnit.SECONDS);
    okHttpClientBuilder.readTimeout(15, TimeUnit.SECONDS);
    okHttpClientBuilder.writeTimeout(15, TimeUnit.SECONDS);
    if (!okHttpClientBuilder.interceptors().contains(interceptor)) {
        okHttpClientBuilder.addInterceptor(interceptor);
        okHttpClientBuilder.authenticator(authenticator);
    }
    return okHttpClientBuilder;
}

它从SharedPreferences获取令牌,并进行改装以调用API。

改造电话

然后我对API进行简单调用(这是具有授权令牌的GET)

ProfileFragment.java

@Inject
ViewModelFactory viewModelFactory;
UserViewModel userViewModel;

@Override
public void onAttach(Context context) {
    ((BaseApplication) context.getApplicationContext())
            .getAppComponent()
            .inject(this);
    super.onAttach(context);
}

@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fragment_profile, container, false);
    ...
    userViewModel = ViewModelProviders.of(this, viewModelFactory).get(UserViewModel.class);
    userViewModel.getUserInfoMutableLiveData().observe(this, this::consumeResponse);
    ...
}

private void consumeResponse(UserResponse userResponse) {
    switch (userResponse.status) {
        case LOADING:
            showProgressBar();
            break;
        case SUCCESS:
            handleSuccessResponse(userResponse.data);
            break;
        case ERROR:
            dismissAll();
            if (getActivity() != null) {
                ResponseHelper.handleErrorResponse(getActivity(), userResponse.error,
                        mContainer, mMainContainer, this);
            }
            break;
    }
}

刷新令牌

现在让我解释发生了什么。

ProfileFragment已创建,匕首2使用注入方式通过获取保存在SharedPreferences中的令牌来创建组件。

ProfileFragment对该API进行了调用,由于令牌已过期,它会收到401错误代码。

调用

TokenAuthenticatior刷新令牌,它成功刷新令牌并将 NEW 令牌保存在SharedPreferences中。

再次调用用户信息,但是,由于没有重新创建ProfileFragment,它使用具有旧令牌的相同依赖项注入进行调用。它使用旧令牌调用API,然后出现错误401。

如果我现在离开此页面然后再返回,它将正常工作,因为它再次进行了依赖项注入并获取了保存在SharedPreferences中的新令牌。

解决方案

  1. 这就是为什么我在考虑可以再次进行注射的某种方式。例如,重新创建配置文件片段,但我认为这不是一个好主意。在使用匕首2之前我没有任何问题。

希望有人会帮助您。谢谢!

2 个答案:

答案 0 :(得分:1)

在当前作用域生存期内注入更改的东西通常是一个坏主意,因为您不能再注入某些东西。一旦令牌失效,您就必须重新创建整个组件,在这种情况下,您还必须强制重新创建片段。

更好的方法是让您的TokenAuthenticatorAuthenticationInterceptor依赖于AccountUtils,然后他们可以根据需要读取和更新令牌。无需将401传播给用户,因为您可以在身份验证器中以静默方式刷新令牌。

答案 1 :(得分:1)

正如@David Medenjak在接受的答案中指出的那样。我确实使我的TokenAuthenticationAuthenticationInterceptor直接依赖令牌,而不是在NetworkModule中提供令牌。

NetworkModule.java

@Provides
AuthenticationInterceptor provideInterceptor(Context context) {
    return new AuthenticationInterceptor(context);
}

@Provides
TokenAuthenticator provideAuthenticator(Context context) {
    return new TokenAuthenticator(context);
}

现在,NetworkModule不再有对该令牌的引用。

AuthenticationInterceptor直接引用保存在SharedPreferences中的令牌。

public AuthenticationInterceptor(Context context) {
    this.context = context;
}

@Override
public Response intercept(@NonNull Chain chain) throws IOException {
    String authToken = AccountUtils.getCurrentUserToken(context);
    Request request = chain.request();
    Request.Builder newRequest;
    if (authToken != null) {
        int index = authToken.lastIndexOf(".");
        newRequest = request.newBuilder().header(Constants.AUTHORIZATION, authToken);
    } else {
        newRequest = request.newBuilder();
    }
    return chain.proceed(newRequest.build());
}

通过这种方式,即使我们使用的是AuthenticationInterceptor的相同实例,它也会在每个请求中都要求保存在SharedPreferences中的令牌。然后,我们将根据需要成功刷新令牌。