我尝试为okhttp实现oAuth拦截器,但我无法获得该问题的完整解决方案。我目前的解决方案如下:
public abstract class AuthInterceptor implements Interceptor {
/**
* We are super lazy and use this lock object, instead of Lock and Condition
*/
private Object lock = new Object();
private AtomicBoolean authenticated = new AtomicBoolean(false);
@Override public Response intercept(Chain chain) throws IOException {
final Request request = chain.request();
Response response = chain.proceed(request);
if (response.code() != 401 ) {
return response; // should not intercept, we are done;
}
// response code was 401
authenticated.set(false);
synchronized (lock) {
// TODO pause okhttp executore service
if (!authenticated.get()) {
if (doAuthentication()) {
authenticated.set(true);
} else {
// authentication failed, so forward original response (401)
return response;
}
// TODO resume okhttp executor service
}
}
// Redo the request, by adding the fresh access token
return redoRequest(chain);
}
/**
* Gets a fresh auth token from server.
* Returns true if a new valid auth token was retrieved from server
*/
protected abstract boolean doAuthentication();
/**
* re-execute the request by setting the new (valid) auth token
*/
protected abstract redoRequest(Chain chain);
}
这个想法是只允许一个具有401响应的线程(或者你可以说一个请求)通过调用doAuthentication()
来刷新身份验证令牌。所有其他线程应该在synchronized
块前面等待。执行doAuthentication()
的线程离开synchronized
块authenticated = true
后,其他线程不应再次调用doAuthentication()
。
但我看到一个问题:理论上,以下场景中有两个线程的线程是可能的:
syncronized
块doAuthentication()
并获得一个新的(有效)身份验证令牌。authenticated = true
authenticated.set(false);
(synchronized
阻止之前)authenticated
已被覆盖,它会再次执行doAuthentication()
,不应该执行,因为几毫秒之前检索到的身份验证令牌应该使用Thread1。我认为在实践中这不会经常发生。但是,你们中谁都看到了解决这个问题的完美解决方案吗?我还考虑了在获取新的身份验证令牌时暂停/ okhttps ExecutorService
以最大限度地降低在这种情况下挣扎的风险。