Dagger 2 получает старый токен при обновлении токена - PullRequest
0 голосов
/ 12 января 2019

У меня проблема с кинжалом 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 голосов
/ 13 января 2019

Как отметил @Давид Меденяк в принятом ответе. Я сделал свои TokenAuthentication и AuthenticationInterceptor зависимыми напрямую от токена, вместо предоставления токена в 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 каждый отдельный запрос. Затем мы успешно обновим токен, как мы хотели.

0 голосов
/ 12 января 2019

Обычно плохая идея вводить вещи, которые изменяют в течение текущего срока действия областей, поскольку вы не можете просто внедрить что-то снова. Как только токен станет недействительным, вам придется воссоздать весь компонент, в этом случае также вынудите вас воссоздать фрагмент, как вы указали.

Лучше было бы, чтобы ваши TokenAuthenticator и AuthenticationInterceptor зависели от AccountUtils, тогда они могли бы читать и обновлять токен по мере необходимости. Нет необходимости передавать 401 пользователю, так как вы можете молча обновить токен в Authenticator.

...