问题描述
我希望这个问题对每个人都有意义,所以这里继续。我有一个Singleton HttpClient管理器类,该类在实例化后将针对Windows Live(Microsoft Auth)进行身份验证
但是,在一段时间不活动(缺少请求)后,此身份验证变得过时,随后对URL的请求返回了一个页面,要求我登录。我的问题本质上是,我应该如何处理对服务器的重新身份验证?我是否应该有另一个线程定期发出get请求并检查是否返回了登录页面,然后重新实例化HttpClient?请让我知道有什么最佳实践。
这是我的连接管理器类中执行身份验证的摘录:
public static synchronized httpconnectionManager getInstance() {
if (INSTANCE == null) {
INSTANCE = new httpconnectionManager();
}
return INSTANCE;
}
private httpconnectionManager() {
final PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
connectionManager.setMaxTotal(100);
connectionManager.setDefaultMaxPerRoute(40);
client = HttpClients.custom()
.setDefaultRequestConfig(RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD).build())
.setConnectionManager(connectionManager)
.disableRedirectHandling()
.build();
try {
// Prepare HTTPClient by performing Authentication Handshake
performAuthenticationHandshake();
} catch (IOException ioException) {
connectionLogger.log(Level.ERROR,"Unable to Authenticate to https://login.live.com");
} catch (HttpException httpException) {
connectionLogger.log(Level.ERROR,httpException.getMessage());
}
}
private void performAuthenticationHandshake() throws IOException,HttpException {
final HttpGet authenticatedGet = new HttpGet(LIVE_URI);
final HttpResponse authGetResponse = client.execute(authenticatedGet);
final String authResponseStr = IoUtils.toString(authGetResponse.getEntity().getContent(),StandardCharsets.UTF_8);
final HttpPost credentialsPost = getCredentialsPost(authResponseStr);
final HttpResponse credentialsPostResponse = client.execute(credentialsPost);
if (credentialsPostResponse.getStatusLine().getStatusCode() != 302) {
throw new HttpException("An invalid status code was returned in credentialsPostResponse (Updated TOS?)): " + credentialsPostResponse.getStatusLine().getStatusCode());
}
final String locationURIStr = Arrays.stream(credentialsPostResponse.getAllHeaders())
.filter(header -> header.getName().startsWith("Location"))
.map(Header::getValue)
.findFirst()
.orElseThrow(HttpException::new);
final HttpGet locationGet = new HttpGet(locationURIStr);
final HttpResponse locationGetResponse = client.execute(locationGet);
if (locationGetResponse.getStatusLine().getStatusCode() != 302) {
throw new HttpException("An invalid status code was returned in locationGetResponse: " + locationGetResponse.getStatusLine().getStatusCode());
}
storeCookies(locationGetResponse.getAllHeaders());
auth = cookieStore.stream()
.filter(cookie -> cookie.getName().startsWith("Auth"))
.map(NameValuePair::getValue)
.findFirst()
.orElseThrow(HttpException::new);
}
解决方法
一种方法是在httpClient中使用cookiestore,并且可以从该cookiestore中获取cookie的到期日期。
我们可以像这样将cookiestore分配给httpclient。
BasicCookieStore cookieStore = new BasicCookieStore();
client = HttpClients.custom().setDefaultRequestConfig(RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD).build())
.setConnectionManager(connectionManager)
.setDefaultCookieStore(cookieStore)
.disableRedirectHandling()
.build();
现在,cookiestore对象包含所有cookie及其过期日期。每当您尝试在HTTPClient中执行任何URL时,我们都会检查身份验证cookie的到期日期。如果当前日期超过了身份验证cookie的过期日期,则HTTPClient必须重新登录以获取身份验证cookie并继续实际过程。
只要使用HTTPClient执行任何URL,都必须插入以下检查。
// let cookiestore one the one used in httpClient
Cookie auth = cookieStore.getCookies().stream()
.filter(cookie -> cookie.getName().startsWith("Auth"))
.findFirst()
.orElseThrow(HttpException::new);
Date currentDate = new Date();
if( auth.isExpire(currentDate) ){
performAuthenticationHandshake();
}