Java Apache HttpClient - 身份验证变得过时
Java Apache HttpClient - Authentication becomes Stale
我希望这个问题对每个人都有意义,所以这里开始。我有一个 Singleton HttpClient 管理器 class,在实例化时将针对 Windows Live (Microsoft Auth)
进行身份验证
但是,在一段时间不活动(没有请求)之后,此身份验证变得陈旧,随后对 URL 的 return 的请求要求我登录。我的问题本质上是,我应该如何处理对服务器的重新验证?我是否应该有另一个线程定期发出获取请求并检查登录页面是否 returned,然后重新实例化 HttpClient?请让我知道这方面的最佳做法。
这是我的连接管理器 class 执行身份验证的片段:
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();
}
我希望这个问题对每个人都有意义,所以这里开始。我有一个 Singleton HttpClient 管理器 class,在实例化时将针对 Windows Live (Microsoft Auth)
进行身份验证但是,在一段时间不活动(没有请求)之后,此身份验证变得陈旧,随后对 URL 的 return 的请求要求我登录。我的问题本质上是,我应该如何处理对服务器的重新验证?我是否应该有另一个线程定期发出获取请求并检查登录页面是否 returned,然后重新实例化 HttpClient?请让我知道这方面的最佳做法。
这是我的连接管理器 class 执行身份验证的片段:
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();
}