此方法在更新token
时始终有效。也就是说,对于每个请求,如果我收到一个error 401
,操作员retryWhen()
都会触发它更新令牌。
代码如下:
private Observable<TokenModel> refreshAccessToken() {
Map<String, String> requestBody = new HashMap<>();
requestBody.put(Constants.EMAIL_KEY, Constants.API_EMAIL);
requestBody.put(Constants.PASSWORD_KEY, Constants.API_PASSWORD);
return RetrofitHelper.getApiService().getAccessToken(requestBody)
.subscribeOn(Schedulers.io())
.doOnNext((AccessToken refreshedToken) -> {
PreferencesHelper.putAccessToken(mContext, refreshedToken);
});
}
public Function<Observable<Throwable>, ObservableSource<?>> isUnauthorized (){
return throwableObservable -> throwableObservable.flatMap((Function<Throwable, ObservableSource<?>>) (Throwable throwable) -> {
if (throwable instanceof HttpException) {
HttpException httpException = (HttpException) throwable;
if (httpException.code() == 401) {
return refreshAccessToken();
}
}
return Observable.error(throwable);
});
}
我在isUnauthorized()
运算符处调用retryWhen()
,在那里我向服务器发出请求
class RetrofitHelper {
static ApiService getApiService() {
return initApi();
}
private static OkHttpClient createOkHttpClient() {
final OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.addInterceptor(chain -> {
Request originalRequest = chain.request();
AccessToken accessToken= PreferencesHelper.getAccessToken(BaseApplication.getInstance());
String accessTokenStr = accessToken.getAccessToken();
Request.Builder builder =
originalRequest.newBuilder().header("Authorization", "Bearer " + accessTokenStr);
Request newRequest = builder.build();
return chain.proceed(newRequest);
});
return httpClient.build();
}
private static ApiService initApi(){
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Constants._api_url)
.addConverterFactory(GsonConverterFactory.create())
.addConverterFactory(ScalarsConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(createOkHttpClient())
.build();
return retrofit.create(ApiService.class);
}
}
但是我们最近添加了Basic Auth
,现在在第一个请求时我得到了401
,retryWhen()
试图更新令牌,但仍然得到了401
。也就是说,doOnNext()
不起作用,但是onError()
立即起作用
private static Observable<AccessToken> refreshAccessToken() {
return RetrofitHelper.getApiService()
.getAccessToken(
Credentials.basic(
Constants._API_USERNAME, Constants._API_PASSWORD
),
Constants._API_BODY_USERNAME,
Constants._API_BODY_PASSWORD,
Constants._API_BODY_GRANT_TYPE
)
.doOnNext((AccessToken refreshedToken) -> {
PreferencesHelper.putObject(BaseApplication.getInstance(), PreferenceKey.ACCESS_TOKEN_KEY, refreshedToken);
}
});
}
// Api服务
public interface ApiService {
// Get Bearer Token
@FormUrlEncoded
@POST("oauth/token")
Observable<AccessToken> getAccessToken(@Header("Authorization") String basicAuth,
@Field("username") String username,
@Field("password") String password,
@Field("grant_type") String grantType);
}
在这里,告诉我为什么这是一个错误?为什么在第一个请求中我得到401
,而从第二个请求中一切正常?
答案 0 :(得分:1)
我想提出一个更好的解决方案。
public class RefreshTokenTransformer<T extends Response<?>> implements ObservableTransformer<T, T> {
private class HttpCode {
private static final int UNAUTHORIZED_HTTP_CODE = 401;
}
private ApiService mApiService;
private UserRepository mUserRepository;
public RefreshTokenTransformer(ApiService service, UserRepository userRepository) {
mApiService = service;
mUserRepository = userRepository;
}
@Override
public ObservableSource<T> apply(final Observable<T> stream) {
return stream.flatMap(new Function<T, ObservableSource<T>>() {
@Override
public ObservableSource<T> apply(T response) throws Exception {
if (response.code() == HttpCode.UNAUTHORIZED_HTTP_CODE) {
return mApiService.refreshToken(mUserRepository.getRefreshTokenHeaders())
.filter(new UnauthorizedPredicate<>(mUserRepository))
.flatMap(new Function<Response<TokenInfo>, ObservableSource<T>>() {
@Override
public ObservableSource<T> apply(Response<TokenInfo> tokenResponse) throws Exception {
return stream.filter(new UnauthorizedPredicate<T>(mUserRepository));
}
});
}
return stream;
}
});
}
private class UnauthorizedPredicate<R extends Response<?>> implements Predicate<R> {
private UserRepository mUserRepository;
private UnauthorizedPredicate(UserRepository userRepository) {
mUserRepository = userRepository;
}
@Override
public boolean test(R response) throws Exception {
if (response.code() == HttpCode.UNAUTHORIZED_HTTP_CODE) {
throw new SessionExpiredException();
}
if (response.body() == null) {
throw new HttpException(response);
}
Class<?> responseBodyClass = response.body().getClass();
if (responseBodyClass.isAssignableFrom(TokenInfo.class)) {
try {
mUserRepository.validateUserAccess((TokenInfo) response.body());
} catch (UnverifiedAccessException error) {
throw new SessionExpiredException(error);
}
}
return true;
}
}
}
我已经编写了自定义运算符,该运算符将执行以下操作:
第一个请求开始,我们得到401响应代码;
然后我们执行/ refresh_token请求以更新令牌;
之后,如果令牌刷新成功,我们将重复 第一个请求。如果/ refresh_token令牌失败,则会引发异常
然后,您可以在任何这样的请求中轻松实现它:
Observable
.compose(new RefreshTokenResponseTransformer<Response<{$your_expected_result}>>
(mApiService, mUserRepository()));
另一件事: 最有可能的是,您最初的可观察到的改造具有参数,例如:
mApiService.someRequest(token)
如果期望参数在执行RefreshTokenTransformer期间发生变化(例如/ refresh_token请求将获得新的访问令牌,并将其保存在某个位置,那么您想使用新鲜的访问令牌重复该请求),则需要包装可通过 defer 运算符进行观察,以强制创建新的可观察对象,如下所示:
Observable.defer(new Callable<ObservableSource<Response<? extends $your_expected_result>>>() {
@Override
public Response<? extends $your_expected_result> call() throws Exception {
return mApiService.someRequest(token);
}
})
答案 1 :(得分:0)
我认为不需要使用拦截器,而是可以实现身份验证器,通过该身份验证器可以访问刷新的令牌,okhttp会自动处理该令牌。如果您收到401,它将使用刷新的令牌更新标头并发出新请求。
public class TokenAuthenticator implements Authenticator {
@Override
public Request authenticate(Proxy proxy, Response response) throws IOException {
// Refresh your access_token using a synchronous api request
newAccessToken = service.refreshToken();
// Add new header to rejected request and retry it
return response.request().newBuilder()
.header(AUTHORIZATION, newAccessToken)
.build();
}