英文:
Java Apache HttpClient - Authentication becomes Stale
问题
我希望这个问题对每个人都有意义,所以我要开始了。我有一个单例 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 {
// 通过执行身份验证握手来准备 HTTPClient
performAuthenticationHandshake();
} catch (IOException ioException) {
connectionLogger.log(Level.ERROR, "无法对 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("在 credentialsPostResponse 中返回了无效的状态码(已更新 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("在 locationGetResponse 中返回了无效的状态码:" + locationGetResponse.getStatusLine().getStatusCode());
}
storeCookies(locationGetResponse.getAllHeaders());
auth = cookieStore.stream()
.filter(cookie -> cookie.getName().startsWith("Auth"))
.map(NameValuePair::getValue)
.findFirst()
.orElseThrow(HttpException::new);
}
(注意:此处仅为代码翻译,不包含其他内容)
英文:
I hope that this question will make sense for everyone so here goes. I have a Singleton HttpClient manager class that upon instantiation will Authenticate against Windows Live (Microsoft Auth)
After a while of inactivity (lack of requests), however, this authentication becomes stale and subsequent requests to URL's return a page requiring me to sign-in. My question is essentially, how should I handle re-authenticating to the Server? Should I have another thread that periodically makes get requests and checks to see if the sign-in page is returned, then re-instantiating the HttpClient? Please let me know what best practices there are for this.
Here is a snippet from my connection manager class that performs the authentication:
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);
}
答案1
得分: 0
一个方法是在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,都必须插入以下检查。
// 让cookiestore成为httpClient中使用的那个
Cookie auth = cookieStore.getCookies().stream()
.filter(cookie -> cookie.getName().startsWith("Auth"))
.findFirst()
.orElseThrow(HttpException::new);
Date currentDate = new Date();
if( auth.isExpire(currentDate) ){
执行身份验证握手();
}
英文:
One approach would be to use cookiestore in httpClient and the expiration date fo the cookie can be get from that cookiestore.
we can assign cookiestore to httpclient like this.
BasicCookieStore cookieStore = new BasicCookieStore();
client = HttpClients.custom().setDefaultRequestConfig(RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD).build())
.setConnectionManager(connectionManager)
.setDefaultCookieStore(cookieStore)
.disableRedirectHandling()
.build();
Now the cookiestore object contains all the cookies along with their expiration date. we check the expiration date of the authentication cookie whenever you are trying to execute any URLs in the HTTPClient. If the current Date exceeds the expiration date of the authentication cookie then the HTTPClient must relogin to get the authentication cookie and continue with the actual process.
The below check must be inserted whenever any URL is executed using HTTPClient.
// 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();
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论