1、该代码展示了使用Apache HttpClient库进行HTTP请求,并处理基于MD5的HTTP Digest认证的过程。 Digests类实现了MD5加密算法,HttpUtils类处理了GET、POST、PUT和DELETE方法的请求,包括设置请求头、生成授权信息和处理响应。
2、请求流程
2.1.发送一个请求
2.2.服务器返回401响应头,要求输入用户凭据(建立通信,发起请求。如果返回401,继续下一次请求。重新设置响应,并获取随机数,通过md5加密返回)
2.3.输入凭据后再发送请求
2.4.服务端验证通过后返回数据
2.4代码示例:本案例使用xml请求并返回xml数据响应,json请求同理
<dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>4.5</version></dependency><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpmime</artifactId><version>4.5</version></dependency><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpcore</artifactId><version>4.4.1</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.8.1</version></dependency>
packagecom.ywb.common.utils.http;importcom.ywb.common.constant.Constants;importorg.junit.Test;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importjavax.net.ssl.*;importjava.io.*;importjava.net.ConnectException;importjava.net.SocketTimeoutException;importjava.net.URL;importjava.net.URLConnection;importjava.security.cert.X509Certificate;importorg.apache.commons.codec.digest.DigestUtils;importorg.apache.commons.lang3.StringUtils;importorg.apache.http.Header;importorg.apache.http.HeaderElement;importorg.apache.http.HttpEntity;importorg.apache.http.HttpStatus;importorg.apache.http.client.config.RequestConfig;importorg.apache.http.client.methods.CloseableHttpResponse;importorg.apache.http.client.methods.HttpPost;importorg.apache.http.entity.StringEntity;importorg.apache.http.impl.client.CloseableHttpClient;importorg.apache.http.impl.client.HttpClients;importorg.apache.http.util.EntityUtils;importjava.io.*;importjava.nio.charset.StandardCharsets;importjava.util.HashMap;/** * 通用http发送方法 * * @authorywb*/publicclass HttpUtils { privatestaticfinal Logger log = LoggerFactory.getLogger(HttpUtils.class);/** * 向指定 URL 发送GET方法的请求 * * @param url 发送请求的 URL * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 * @return所代表远程资源的响应结果*/publicstatic String sendGet(String url, String param) { return sendGet(url, param, Constants.UTF8); } /** * 向指定 URL 发送GET方法的请求 * * @param url 发送请求的 URL * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 * @param contentType 编码类型 * @return所代表远程资源的响应结果*/publicstatic String sendGet(String url, String param, String contentType) { StringBuilder result =new StringBuilder(); BufferedReader in =null;try { String urlNameString = url +"?" + param; log.info("sendGet - {}", urlNameString); URL realUrl =new URL(urlNameString); URLConnection connection = realUrl.openConnection(); connection.setRequestProperty("accept","*/*"); connection.setRequestProperty("connection","Keep-Alive"); connection.setRequestProperty("user-agent","Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); connection.connect(); in =newBufferedReader(new InputStreamReader(connection.getInputStream(), contentType)); String line; while ((line = in.readLine()) != null) { result.append(line); } log.info("recv - {}", result); } catch (ConnectException e) { log.error("调用HttpUtils.sendGet ConnectException, url=" + url +",param=" + param, e); } catch (SocketTimeoutException e) { log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url +",param=" + param, e); } catch (IOException e) { log.error("调用HttpUtils.sendGet IOException, url=" + url +",param=" + param, e); } catch (Exception e) { log.error("调用HttpsUtil.sendGet Exception, url=" + url +",param=" + param, e); } finally{try{if (in != null) { in.close(); } } catch (Exception ex) { log.error("调用in.close Exception, url=" + url +",param=" + param, ex); } } return result.toString(); } /** * 向指定 URL 发送POST方法的请求 * * @param url 发送请求的 URL * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 * @return所代表远程资源的响应结果*/publicstatic String sendPost(String url, String param) { PrintWriter out =null; BufferedReader in =null; StringBuilder result =newStringBuilder();try { String urlNameString = url; log.info("sendPost - {}", urlNameString); URL realUrl =new URL(urlNameString); URLConnection conn = realUrl.openConnection(); conn.setRequestProperty("accept","**"); conn.setRequestProperty("connection","Keep-Alive"); conn.setRequestProperty("user-agent","Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); conn.setRequestProperty("Accept-Charset","utf-8"); conn.setRequestProperty("contentType","utf-8"); conn.setDoOutput(true); conn.setDoInput(true); conn.setSSLSocketFactory(sc.getSocketFactory()); conn.setHostnameVerifier(new TrustAnyHostnameVerifier()); conn.connect(); InputStream is = conn.getInputStream(); BufferedReader br =newBufferedReader(new InputStreamReader(is)); String ret ="";while ((ret = br.readLine()) != null) { if (ret != null && !"".equals(ret.trim())) { result.append(new String(ret.getBytes("ISO-8859-1"),"utf-8")); } } log.info("recv - {}", result); conn.disconnect(); br.close(); } catch (ConnectException e) { log.error("调用HttpUtils.sendSSLPost ConnectException, url=" + url +",param=" + param, e); } catch (SocketTimeoutException e) { log.error("调用HttpUtils.sendSSLPost SocketTimeoutException, url=" + url +",param=" + param, e); } catch (IOException e) { log.error("调用HttpUtils.sendSSLPost IOException, url=" + url +",param=" + param, e); } catch (Exception e) { log.error("调用HttpsUtil.sendSSLPost Exception, url=" + url +",param=" + param, e); } return result.toString(); } /** * 摘要认证 两次请求 * * @param url * @return返回结果*/publicstatic String doPostDigest(String url, String uri, String username, String password, String requestXml) { CloseableHttpClient httpClient =null; CloseableHttpResponse response =null; HttpPost httpPost =null; String strResponse =null;try { httpClient =HttpClients.createDefault();// httpClient = new SSLClient(); httpPost = newHttpPost(url);//构造请求头 httpPost.setHeader("Content-Type","application/xml"); httpPost.setHeader("X-Requested-With","XMLHttpRequest"); httpPost.addHeader("Cache-Control","no-cache"); //设置缓存 httpPost.setHeader("Connection","Close"); RequestConfig.Builder builder = RequestConfig.custom(); builder.setSocketTimeout(5000);//设置请求时间builder.setConnectTimeout(8000);//设置超时时间builder.setRedirectsEnabled(false);//设置是否跳转链接(反向代理)// 设置 连接 属性 httpPost.setConfig(builder.build()); StringEntity entityss =new StringEntity(requestXml,"utf-8"); httpPost.setEntity(entityss); //执行请求 response = httpClient.execute(httpPost); HttpEntity responseEntity =response.getEntity();//检验返回码int statusCode = response.getStatusLine().getStatusCode(); log.debug("第一次发送摘要认证 Post请求 返回码:{}" +statusCode);if (401 == statusCode) { strResponse = EntityUtils.toString(responseEntity,"utf-8"); log.debug("Post请求401返回结果:{}" +strResponse);//组织参数,发起第二次请求 Header[] headers = response.getHeaders("WWW-Authenticate"); HeaderElement[] elements = headers[0].getElements(); String realm =null; String qop =null; String nonce =null; String opaque =null; String method ="POST";for (HeaderElement element : elements) { if (element.getName().equals("Digest realm")) { realm = element.getValue(); } elseif(element.getName().equals("qop")) { qop = element.getValue(); } elseif(element.getName().equals("nonce")) { nonce = element.getValue(); } elseif(element.getName().equals("opaque")) { opaque = element.getValue(); } } // 以上为 获取第一次请求后返回的 数据 String nc ="00000001"; String cnonce ="uniview";//后期变成可配置 String a1 = username +":" + realm +":" + password; String a2 = method +":" + uri; String response1 =null;// 获取 Digest 这个字符串 String backString = response.getFirstHeader("WWW-Authenticate").getValue();try { response1 = DigestUtils.md5Hex((DigestUtils.md5Hex(a1.getBytes("UTF-8")) +":" + nonce +":" +nc+":" +"uniview" +":" + qop +":" + DigestUtils.md5Hex(a2.getBytes("UTF-8"))).getBytes("UTF-8")); } catch (UnsupportedEncodingException e) { log.error("MD5异常:{}" + e.getLocalizedMessage(), e); } httpPost.addHeader("Authorization", backString +",username=\"" + username +"\"" +",realm=\"" + realm +"\"" +",nonce=\"" + nonce +"\"" +",uri=\"" + uri +"\"" +",qop=\"" + qop +"\"" +",nc=\"" + nc +"\"" +",cnonce=\"" + cnonce +"\"" +",response=\"" + response1 +"\"" +",opaque=\"" +opaque);//发送第二次请求 response = httpClient.execute(httpPost); HttpEntity entity =response.getEntity();int statusCode1 = response.getStatusLine().getStatusCode(); log.debug("第二次发送摘要认证 Post请求 返回码:{}" +statusCode1);if (HttpStatus.SC_OK == statusCode1) { strResponse = EntityUtils.toString(entity, StandardCharsets.UTF_8); log.debug("第二次发送strResponse" +strResponse);return strResponse; } else { strResponse = EntityUtils.toString(entity, StandardCharsets.UTF_8); log.debug("第二次鉴权认证请求非 200 返回结果:{}" +strResponse);return strResponse; } } else { strResponse = EntityUtils.toString(responseEntity, StandardCharsets.UTF_8); log.error("第一次鉴权认证请求非401 返回结果:{}" + strResponse); } } catch (Exception e) { log.error("摘要认证 发送请求失败" + e.getLocalizedMessage(), e); } finally{if(null!= httpPost) { httpPost.releaseConnection(); } if(null!= response) { try { response.close(); } catch (IOException e) { log.error("httpResponse流关闭异常:", e); } } if(null!= httpClient) { try { httpClient.close(); } catch (IOException e) { log.error("httpClient 流关闭异常:", e); } } } return strResponse; } privatestaticclassTrustAnyTrustManagerimplements X509TrustManager { @Override publicvoid checkClientTrusted(X509Certificate[] chain, String authType) { } @Override publicvoid checkServerTrusted(X509Certificate[] chain, String authType) { } @Override public X509Certificate[] getAcceptedIssuers() { returnnew X509Certificate[]{}; } } privatestaticclassTrustAnyHostnameVerifierimplements HostnameVerifier { @Override publicboolean verify(String hostname, SSLSession session) { returntrue; } } @Test publicvoid testHttpPostRaw() { final String url ="http://18.18.167.120/ISAPI/Traffic/ContentMgmt/Statistic/operator";final String uri ="/ISAPI/Traffic/ContentMgmt/Statistic/operator";final String encode ="utf-8"; String requestXml ="<?xml version='1.0' encoding='utf-8'?>" +"<StatisticOperator>" +"<operationType>search</operationType>" +"<searchCond>" +"<searchID>CAD8E0D6-1480-0001-C0B7-1E50E290140D</searchID>" +"<timeSpanList><timeSpan>" +"<startTime>2024-06-21T15:25:10Z</startTime>" +"<endTime>2024-06-21T15:27:59Z</endTime>" +"</timeSpan></timeSpanList>" +"<maxResults>20</maxResults>" +"<searchResultPosition>0</searchResultPosition>" +"</searchCond>" +"</StatisticOperator>";final HashMap<String, String> headers = new HashMap<String, String>(); headers.put("Content-Type","application/x-www-form-urlencoded"); headers.put("X-Requested-With","XMLHttpRequest"); System.out.println(doPostDigest(url, uri, "admin","123456", requestXml)); 响应结果:
<?xml version="1.0" encoding="UTF-8"?>-<StatisticOperatorResultxmlns="http://www.isapi.org/ver20/XMLSchema"version="2.0"><statusCode>1</statusCode><description>OK</description><searchID>CAD8E0D6-1480-0001-C0B7-1E50E290140D</searchID><numOfMatches>2</numOfMatches><totalMatches>2</totalMatches>-<StatisticList>-<Statistic><IDmax="65535"min="1">1</ID><direction>0</direction><laneNo>18</laneNo><vehicleType>0</vehicleType><startTime>2024-06-21T15:26:03</startTime><endTime>2024-06-21T15:27:03</endTime><timePoint>2024-06-21T15:26:03</timePoint><channel>1</channel><carFlux>2</carFlux><smallCarFlux>0</smallCarFlux><bigCarFlux>1</bigCarFlux><passerbyFlux>0</passerbyFlux><averOccpancy>26.2</averOccpancy><averSpeed>0</averSpeed><averCarDis>0</averCarDis><midCarFlux>0</midCarFlux><averTimeOccupancy>50</averTimeOccupancy><averQueueLength>35</averQueueLength><averTimeHeadway>102</averTimeHeadway><nonmotorFlux>1</nonmotorFlux></Statistic>-<Statistic><IDmax="65535"min="1">2</ID><direction>0</direction><laneNo>19</laneNo><vehicleType>0</vehicleType><startTime>2024-06-21T15:26:03</startTime><endTime>2024-06-21T15:27:03</endTime><timePoint>2024-06-21T15:26:03</timePoint><channel>1</channel><carFlux>0</carFlux><smallCarFlux>0</smallCarFlux><bigCarFlux>0</bigCarFlux><passerbyFlux>0</passerbyFlux><averOccpancy>0.0</averOccpancy><averSpeed>0</averSpeed><averCarDis>0</averCarDis><midCarFlux>0</midCarFlux><averTimeOccupancy>0</averTimeOccupancy><averQueueLength>0</averQueueLength><averTimeHeadway>0</averTimeHeadway><nonmotorFlux>0</nonmotorFlux></Statistic>-<Statistic><IDmax="65535"min="1">3</ID><direction>0</direction><laneNo>3</laneNo><vehicleType>0</vehicleType><startTime>2024-06-21T15:26:03</startTime><endTime>2024-06-21T15:27:03</endTime><timePoint>2024-06-21T15:26:03</timePoint><channel>1</channel><carFlux>0</carFlux><smallCarFlux>0</smallCarFlux><bigCarFlux>0</bigCarFlux><passerbyFlux>0</passerbyFlux><averOccpancy>0.0</averOccpancy><averSpeed>0</averSpeed><averCarDis>0</averCarDis><midCarFlux>0</midCarFlux><averTimeOccupancy>0</averTimeOccupancy><averQueueLength>0</averQueueLength><averTimeHeadway>0</averTimeHeadway><nonmotorFlux>0</nonmotorFlux></Statistic>-<Statistic><IDmax="65535"min="1">4</ID><direction>0</direction><laneNo>18</laneNo><vehicleType>0</vehicleType><startTime>2024-06-21T15:27:03</startTime><endTime>2024-06-21T15:28:03</endTime><timePoint>2024-06-21T15:27:03</timePoint><channel>1</channel><carFlux>1</carFlux><smallCarFlux>0</smallCarFlux><bigCarFlux>1</bigCarFlux><passerbyFlux>0</passerbyFlux><averOccpancy>27.0</averOccpancy><averSpeed>0</averSpeed><averCarDis>0</averCarDis><midCarFlux>0</midCarFlux><averTimeOccupancy>98</averTimeOccupancy><averQueueLength>35</averQueueLength><averTimeHeadway>54</averTimeHeadway><nonmotorFlux>0</nonmotorFlux></Statistic>-<Statistic><IDmax="65535"min="1">5</ID><direction>0</direction><laneNo>19</laneNo><vehicleType>0</vehicleType><startTime>2024-06-21T15:27:03</startTime><endTime>2024-06-21T15:28:03</endTime><timePoint>2024-06-21T15:27:03</timePoint><channel>1</channel><carFlux>0</carFlux><smallCarFlux>0</smallCarFlux><bigCarFlux>0</bigCarFlux><passerbyFlux>0</passerbyFlux><averOccpancy>0.0</averOccpancy><averSpeed>0</averSpeed><averCarDis>0</averCarDis><midCarFlux>0</midCarFlux><averTimeOccupancy>0</averTimeOccupancy><averQueueLength>0</averQueueLength><averTimeHeadway>0</averTimeHeadway><nonmotorFlux>0</nonmotorFlux></Statistic>-<Statistic><IDmax="65535"min="1">6</ID><direction>0</direction><laneNo>3</laneNo><vehicleType>0</vehicleType><startTime>2024-06-21T15:27:03</startTime><endTime>2024-06-21T15:28:03</endTime><timePoint>2024-06-21T15:27:03</timePoint><channel>1</channel><carFlux>0</carFlux><smallCarFlux>0</smallCarFlux><bigCarFlux>0</bigCarFlux><passerbyFlux>0</passerbyFlux><averOccpancy>0.0</averOccpancy><averSpeed>0</averSpeed><averCarDis>0</averCarDis><midCarFlux>0</midCarFlux><averTimeOccupancy>0</averTimeOccupancy><averQueueLength>0</averQueueLength><averTimeHeadway>0</averTimeHeadway><nonmotorFlux>0</nonmotorFlux></Statistic></StatisticList></StatisticOperatorResult>