httpClient4.3.3ABTH认证+密码访问
程序员文章站
2024-03-19 13:35:40
...
这里,跟大家分享一个工具类。使用HTTPClient,访问需要密码的连接时,一般需要一个BATH认证。
package com.lzq;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLContext;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.Header;
import org.apache.http.HttpException;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.HttpStatus;
import org.apache.http.auth.AuthScheme;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.AuthState;
import org.apache.http.auth.Credentials;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.CookieStore;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.AuthSchemes;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.protocol.ClientContext;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.AllowAllHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeader;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.ExecutionContext;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.lzq.monitor.utils.PropertiesFileUtil;
import com.lzq.monitor.utils.https.DateFormatUtil;
/**
* httpClient 工具类
*/
@SuppressWarnings("deprecation")
public class HttpClientUtils {
/**HttpClient客户端连接管理器中,连接池最大连接数*/
public static final int HTTPCLIENT_CONN_MANAGER_MAXTOTAL = 1100;
/**HttpClient客户端连接管理器中,连接池最大连接数*/
public static final int HTTPCLIENT_CONN_MANAGER_MAXPERROUTE = 100;
private static final Logger logger = LoggerFactory.getLogger(HttpClientUtils.class);
/**httpClient客户端*/
private CloseableHttpClient httpClient = null;
/**httpContext,可指定认证方式*/
private BasicHttpContext localContext = null;
/** ip */
public static final String IP = "192.168.24.33";
/** 端口 */
private static final int PORT = 438;
public static void main(String[] args) {
try {
int intPort = 9696;
URI uriDialing = new URIBuilder()
.setScheme("http")
.setHost(IP)
.setPort(PORT)
.setPath("/api/v1/port/"+intPort+"/redial").build();
HttpClientUtils httpClientUtils = new HttpClientUtils();
CloseableHttpResponse response =httpClientUtils.requestProcessorGet(uriDialing);
String content = httpClientUtils.getContent(response);
int intStatusCode = response.getStatusLine().getStatusCode(); //获取请求行
if (intStatusCode != HttpStatus.SC_OK) { //如果不是200,则打印response的body信息
logger.error(response.toString());
}else {
System.out.println(content);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 发送请求
* @param port
* @return
* @throws Exception
*/
public CloseableHttpResponse requestProcessorGet(URI uri) throws Exception {
List<Header> headers = this.getDefaultHeaders(null); //访问拨号接口的headers
CloseableHttpResponse response = null; //访问拨号接口返回的response
CloseableHttpClient httpClient = null;
try {
httpClient = this.getHttpClient(uri.getHost(),uri.getPort());
HttpGet httpGet = new HttpGet(uri); //拨号接口get请求
// 设置请求头信息
httpGet.setHeaders(headers.toArray(new Header[0]));
response = httpClient.execute(httpGet,localContext);
} catch (URISyntaxException e) {
logger.error("uri异常", e);
} catch (ClientProtocolException e) {
logger.error("协议错误", e);
} catch (IOException e) {
logger.error("网络错误", e);
} catch (Exception e) {
logger.error("response未正常返回", e);
}
return response;
}
private CloseableHttpClient getHttpClient(String ip,int port) throws Exception{
if (httpClient == null) {
httpClient = this.proxyClientBuilder(ip,port);
}
return httpClient;
}
/**
* 获取文本内容
* */
public String getContent(CloseableHttpResponse resp) throws Exception {
String content = null;
try {
content = EntityUtils.toString(resp.getEntity(),"gbk");
EntityUtils.consume(resp.getEntity());
} catch (Exception ex) {
logger.error("获取内容失败", ex);
} finally {
if (resp != null) {
resp.close();
}
}
return content;
}
/**
* 初始化代理客户端
* @throws Exception
*/
private CloseableHttpClient proxyClientBuilder(String ip,int port) throws Exception {
//http连接池
PoolingHttpClientConnectionManager connManager = this.getHttpPoolManager();
//userAnent
String userAgent = "Mozilla/5.0 (Windows NT 6.1, WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.81 Safari/537.36";
//http请求的配置信息
RequestConfig requestConfig = this.getReuestConfig();
//cookie
CookieStore cookieStore = new BasicCookieStore();
//远程被访问主机
HttpHost targetHost = new HttpHost(ip, port, "http");
CredentialsProvider credentialsProvider = this.getCredentialsProvider(targetHost);
BasicScheme basicAuth = new BasicScheme();
localContext = new BasicHttpContext();
localContext.setAttribute("preemptive-auth", basicAuth);
HttpClientBuilder httpClientBuilder = HttpClients.custom()
.setConnectionManager(connManager)
.setUserAgent(userAgent)
.setDefaultCookieStore(cookieStore)
.setDefaultRequestConfig(requestConfig)
.disableRedirectHandling();
if (credentialsProvider!=null) {
httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider)
.addInterceptorFirst(new PreemptiveAuthInterceptor()); // 此处连接器功能与credentialsProvider功能类似,二者可以选其一即可
}
CloseableHttpClient client = httpClientBuilder.build();
return client;
}
/**
* 获取主机信息
* @param targetHost
* @return
*/
private CredentialsProvider getCredentialsProvider(HttpHost targetHost) {
String strUserName = "abc"; //http请求的用户名
String strPassword = "pwd"; //http请求的密码
CredentialsProvider credentialsProvider = null;
AuthScope authScope = new AuthScope(targetHost.getHostName(), targetHost.getPort(),AuthScope.ANY_REALM);
Credentials userNameCredentials = new UsernamePasswordCredentials(strUserName, strPassword);
credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(authScope,userNameCredentials);
logger.info("<<<<<<<<<<<<<<<<<<<<<<<<<password="+
userNameCredentials.getPassword()+";userName="+userNameCredentials.getUserPrincipal().getName());
return credentialsProvider;
}
/**
* http连接池
* @return
* @throws Exception
*/
private PoolingHttpClientConnectionManager getHttpPoolManager() throws Exception{
Registry<ConnectionSocketFactory> registry = this.sslBuilder();
PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(registry);
//设置整个连接池最大连接数 根据自己的场景决定
connManager.setMaxTotal(HTTPCLIENT_CONN_MANAGER_MAXTOTAL);
// 将每个路由基础的连接增加
connManager.setDefaultMaxPerRoute(HTTPCLIENT_CONN_MANAGER_MAXPERROUTE);
connManager.closeIdleConnections(0, TimeUnit.SECONDS);
connManager.closeExpiredConnections();
return connManager;
}
/**
* httpClient连接配置信息
* @return
*/
private RequestConfig getReuestConfig(){
RequestConfig requestConfig = RequestConfig.custom()
.setCookieSpec(CookieSpecs.BROWSER_COMPATIBILITY)
.setProxyPreferredAuthSchemes(Arrays.asList(AuthSchemes.BASIC))
.setSocketTimeout(20000)
.setConnectTimeout(20000)
.setConnectionRequestTimeout(20000)
.setRedirectsEnabled(false)
.build();
return requestConfig;
}
/**
* http连接器,请求发出前,进行拦截
*/
class PreemptiveAuthInterceptor implements HttpRequestInterceptor {
@SuppressWarnings("deprecation")
@Override
public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
AuthState authState = (AuthState) context.getAttribute(ClientContext.TARGET_AUTH_STATE);
if (authState.getAuthScheme() == null) {
AuthScheme authScheme = (AuthScheme) context.getAttribute("preemptive-auth");
if (authScheme != null) {
CredentialsProvider credsProvider = (CredentialsProvider) context.getAttribute(ClientContext.CREDS_PROVIDER);
HttpHost targetHost = (HttpHost) context.getAttribute(ExecutionContext.HTTP_TARGET_HOST);
AuthScope authScope = new AuthScope(targetHost.getHostName(),targetHost.getPort(),AuthScope.ANY_REALM);
Credentials creds = credsProvider.getCredentials(authScope);
if (creds == null) {
String strUserName = PropertiesFileUtil.MONITORINIT.getProperty("httpUserName"); //http请求的用户名
String strPassword = PropertiesFileUtil.MONITORINIT.getProperty("httpPassword"); //http请求的密码
if (StringUtils.isBlank(strUserName) && StringUtils.isBlank(strPassword)) {
logger.info("配置文件中并没有取到:ADSL拨号的用户名、密码"+DateFormatUtil.formatDate(new Date()));
}
Credentials userNameCredentials = new UsernamePasswordCredentials(strUserName, strPassword);
authState.setCredentials(userNameCredentials);
// throw new HttpException("No credentials for preemptive authentication");
}else {
authState.setCredentials(creds);
logger.info("<<<<<<<<<<<<<<<<<<<<<<认证信息,拦截器截到不为空!!!!"+DateFormatUtil.formatDate(new Date()));
}
authState.setAuthScheme(authScheme);
}
}
}
}
/**
* 获取固定的请求header
* */
public List<Header> getDefaultHeaders(String referer) {
List<Header> headers = new ArrayList<Header>();
headers.add(new BasicHeader(HttpHeaders.ACCEPT,"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"));
headers.add(new BasicHeader(HttpHeaders.ACCEPT_ENCODING, "gzip, deflate, br"));
headers.add(new BasicHeader(HttpHeaders.ACCEPT_LANGUAGE,"zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3"));
if (StringUtils.isNotBlank(referer)) {
headers.add(new BasicHeader(HttpHeaders.REFERER, referer));
}
return headers;
}
/**
* 销毁httpClient
* @throws Exception
*/
public void destroyHttpClient() {
try {
if (httpClient != null) {
httpClient.close();
httpClient = null;
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 构建SSLContext
* 1.支持https访问
* 2.支持https双向证书验证
*/
protected Registry<ConnectionSocketFactory> sslBuilder() throws Exception {
SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, new TrustStrategy() {
@Override
public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
return true;
}
}).build();
X509HostnameVerifier hostnameVerifier = new AllowAllHostnameVerifier();
Registry<ConnectionSocketFactory> socketFactoryRegistry = null;
socketFactoryRegistry = RegistryBuilder
.<ConnectionSocketFactory> create()
.register("http", PlainConnectionSocketFactory.INSTANCE)
.register(
"https", new SSLConnectionSocketFactory(sslContext,
hostnameVerifier)).build();
return socketFactoryRegistry;
}
}
HttpClient,源代码中,第一次访问时,默认并不带上BATH认证信息,而是走普通的Http请求,访问失败后,HttpClient有重试机制,它会去匹配,最相近的访问方式,然后匹配到BATH认证方式之后,再带上BATH信息访问链接。
这种方式实现也可以达到目的,不过我们可以通知强制HttpClient使用BATH认证进行访问,那么每一次访问,就不需要多一次Http请求了。
HttpClient的使用,还是有一些坑的,不过也给我们提供了更大的便利。这就需要我们根据自己的需要去完善它。