欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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的使用,还是有一些坑的,不过也给我们提供了更大的便利。这就需要我们根据自己的需要去完善它。