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();
}