importorg.apache.http.HttpResponse;importorg.apache.http.client.HttpClient;importorg.apache.http.client.methods.HttpPost;importorg.apache.http.entity.StringEntity;importorg.apache.http.impl.client.HttpClients;importorg.jdom2.Document;importorg.jdom2.Element;importorg.jdom2.JDOMException;importorg.jdom2.input.SAXBuilder;importorg.springframework.stereotype.Component;importorg.springframework.stereotype.Service;importjavax.servlet.http.HttpServletRequest;import java.io.*;importjava.security.MessageDigest;importjava.security.NoSuchAlgorithmException;import java.util.*;
@Componentpublic classWxUtils {/*** 执行 POST 方法的 HTTP 请求
*
*@paramurl
*@paramparameters
*@return*@throwsIOException*/
public String executeHttpPost(String url, SortedMap parameters) throwsIOException {
HttpClient client=HttpClients.createDefault();
HttpPost request= newHttpPost(url);
request.setHeader("Content-type", "application/xml");
request.setHeader("Accept", "application/xml");
request.setEntity(new StringEntity(transferMapToXml(parameters), "UTF-8"));
HttpResponse response=client.execute(request);returnreadResponse(response);
}/*** 第一次签名
*
*@paramparameters 数据为服务器生成,下单时必须的字段排序签名
*@paramkey
*@return
*/
public String createSign(SortedMapparameters, String key) {
StringBuffer sb= newStringBuffer();
Set es= parameters.entrySet();//所有参与传参的参数按照accsii排序(升序)
Iterator it =es.iterator();while(it.hasNext()) {
Map.Entry entry=(Map.Entry) it.next();
String k=(String) entry.getKey();
Object v=entry.getValue();if (null != v && !"".equals(v)&& !"sign".equals(k) && !"key".equals(k)) {
sb.append(k+ "=" + v + "&");
}
}
sb.append("key=" +key);returnencodeMD5(sb.toString());
}/*** 第二次签名
*
*@paramresult 数据为微信返回给服务器的数据(XML 的 String),再次签名后传回给客户端(APP)使用
*@paramkey 密钥
*@return*@throwsIOException*/
public Map createSign2(String result, String key) throwsIOException {
SortedMap map = new TreeMap<>(transferXmlToMap(result));
Map app= newHashMap();
System.out.println(map);
app.put("appId", map.get("appid"));
app.put("nonceStr", map.get("nonce_str"));
app.put("package", "prepay_id="+map.get("prepay_id"));
app.put("signType", "MD5");
app.put("timeStamp",String.valueOf(new Date().getTime() / 1000) ); //时间为秒,JDK 生成的是毫秒,故除以 1000
app.put("sign", createSign(new TreeMap<>(app), key));returnapp;
}/*** 验证签名是否正确
*
*@returnboolean
*@throwsException*/
public boolean checkSign(SortedMap parameters, String key) throwsException {
String signWx= parameters.get("sign").toString();if (signWx == null) return false;
parameters.remove("sign"); //需要去掉原 map 中包含的 sign 字段再进行签名
String signMe =createSign(parameters, key);returnsignWx.equals(signMe);
}/*** 读取 request body 内容作为字符串
*
*@paramrequest
*@return*@throwsIOException*/
public String readRequest(HttpServletRequest request) throwsIOException {
InputStream inputStream;
StringBuffer sb= newStringBuffer();
inputStream=request.getInputStream();
String str;
BufferedReader in= new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));while ((str = in.readLine()) != null) {
sb.append(str);
}
in.close();
inputStream.close();returnsb.toString();
}/*** 读取 response body 内容为字符串*/
public String readResponse(HttpResponse response) throwsIOException {
BufferedReader in= newBufferedReader(newInputStreamReader(response.getEntity().getContent()));
String result= newString();
String line;while ((line = in.readLine()) != null) {
result+=line;
}returnresult;
}/*** 将 Map 转化为 XML
*
*@parammap
*@return
*/
public String transferMapToXml(SortedMapmap) {
StringBuffer sb= newStringBuffer();
sb.append("");for(String key : map.keySet()) {
sb.append("")
.append(map.get(key))
.append("").append(key).append(">");
}return sb.append("").toString();
}/*** 将 XML 转化为 map
*
*@paramstrxml
*@return*@throwsorg.jdom2.JDOMException
*@throwsIOException*/
public Map transferXmlToMap(String strxml) throwsIOException {
strxml= strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");if (null == strxml || "".equals(strxml)) {return null;
}
Map m= newHashMap();
InputStream in= new ByteArrayInputStream(strxml.getBytes("UTF-8"));
SAXBuilder builder= newSAXBuilder();
Document doc= null;try{
doc=builder.build(in);
}catch(JDOMException e) {throw new IOException(e.getMessage()); //统一转化为 IO 异常输出
}//解析 DOM
Element root =doc.getRootElement();
List list=root.getChildren();
Iterator it=list.iterator();while(it.hasNext()) {
Element e=(Element) it.next();
String k=e.getName();
String v= "";
List children=e.getChildren();if(children.isEmpty()) {
v=e.getTextNormalize();
}else{
v=getChildrenText(children);
}
m.put(k, v);
}//关闭流
in.close();returnm;
}//辅助 transferXmlToMap 方法递归提取子节点数据
private String getChildrenText(Listchildren) {
StringBuffer sb= newStringBuffer();if (!children.isEmpty()) {
Iterator it =children.iterator();while(it.hasNext()) {
Element e=(Element) it.next();
String name=e.getName();
String value=e.getTextNormalize();
List list =e.getChildren();
sb.append("");if (!list.isEmpty()) {
sb.append(getChildrenText(list));
}
sb.append(value);
sb.append("" + name + ">");
}
}returnsb.toString();
}/*** 生成 32 位随机字符串,包含:数字、字母大小写
*
*@return
*/
publicString gen32RandomString() {char[] dict = {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0','A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z','a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
StringBuffer sb= newStringBuffer();
Random random= newRandom();for (int i = 0; i < 32; i++) {
sb.append(String.valueOf(dict[(int) (Math.random() * 36)]));
}returnsb.toString();
}/*** MD5 签名
*
*@paramstr
*@return签名后的字符串信息*/
publicString encodeMD5(String str) {try{
MessageDigest messageDigest= MessageDigest.getInstance("MD5");byte[] inputByteArray =(str).getBytes();
messageDigest.update(inputByteArray);byte[] resultByteArray =messageDigest.digest();returnbyteArrayToHex(resultByteArray);
}catch(NoSuchAlgorithmException e) {return null;
}
}//辅助 encodeMD5 方法实现
private String byteArrayToHex(byte[] byteArray) {char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};char[] resultCharArray = new char[byteArray.length * 2];int index = 0;for (byteb : byteArray) {
resultCharArray[index++] = hexDigits[b >>> 4 & 0xf];
resultCharArray[index++] = hexDigits[b & 0xf];
}//字符数组组合成字符串返回
return newString(resultCharArray);
}
}