| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389 |
- package com.template.common.utils;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.w3c.dom.Node;
- import org.w3c.dom.NodeList;
- import javax.crypto.Mac;
- import javax.crypto.spec.SecretKeySpec;
- import javax.servlet.ServletOutputStream;
- import javax.servlet.http.HttpServletResponse;
- import javax.xml.parsers.DocumentBuilder;
- import javax.xml.parsers.DocumentBuilderFactory;
- import javax.xml.transform.OutputKeys;
- import javax.xml.transform.Transformer;
- import javax.xml.transform.TransformerFactory;
- import javax.xml.transform.dom.DOMSource;
- import javax.xml.transform.stream.StreamResult;
- import java.io.*;
- import java.security.MessageDigest;
- import java.text.SimpleDateFormat;
- import java.util.*;
- /**
- * 微信公众号接口工具类
- * @author lujunjie
- * @date 2018/03/01
- */
- public class WxUtil {
- private static Logger logger = LoggerFactory.getLogger( WxUtil.class);
- /**
- * 加密/校验流程如下:
- * 1. 将token、timestamp、nonce三个参数进行字典序排序<br>
- * 2. 将三个参数字符串拼接成一个字符串进行sha1加密<br>
- * 3. 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信<br>
- *
- * @param token Token验证密钥
- * @param signature 微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数,nonce参数
- * @param timestamp 时间戳
- * @param nonce 随机数
- * @return 验证成功返回:true,失败返回:false
- */
- public static boolean checkSignature(String token, String signature, String timestamp, String nonce) {
- List<String> params = new ArrayList<String>();
- params.add(token);
- params.add(timestamp);
- params.add(nonce);
- //1. 将token、timestamp、nonce三个参数进行字典序排序
- Collections.sort(params, new Comparator<String>() {
- @Override
- public int compare(String o1, String o2) {
- return o1.compareTo(o2);
- }
- });
- //2. 将三个参数字符串拼接成一个字符串进行sha1加密
- String temp = SHA1.encode(params.get(0) + params.get(1) + params.get(2));
- //3. 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
- return temp.equals(signature);
- }
- /**
- * 输入流转化为字符串
- * @param inputStream 流
- * @return String 字符串
- * @throws Exception
- */
- public static String getStreamString(InputStream inputStream) throws Exception{
- StringBuffer buffer=new StringBuffer();
- InputStreamReader inputStreamReader = null;
- BufferedReader bufferedReader = null;
- try{
- inputStreamReader=new InputStreamReader(inputStream, WxConstants.DEFAULT_CHARSET);
- bufferedReader=new BufferedReader(inputStreamReader);
- String line;
- while((line=bufferedReader.readLine())!=null){
- buffer.append(line);
- }
- }catch(Exception e){
- throw new Exception();
- }finally {
- if(bufferedReader != null){
- bufferedReader.close();
- }
- if(inputStreamReader != null){
- inputStreamReader.close();
- }
- if(inputStream != null){
- inputStream.close();
- }
- }
- return buffer.toString();
- }
- /**
- * 获取随机字符串 Nonce Str
- * @return String 随机字符串
- */
- public static String getNonceStr() {
- return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
- }
- /**
- * 生成签名. 注意,若含有sign_type字段,必须和signType参数保持一致。
- * @param data 待签名数据
- * @param apikey API密钥
- * @return 签名
- */
- public static String getSignature(final Map<String, String> data, String apikey,
- String signType) throws Exception {
- Set<String> keySet = data.keySet();
- String[] keyArray = keySet.toArray(new String[keySet.size()]);
- Arrays.sort(keyArray);
- StringBuilder sb = new StringBuilder();
- sb.append(apikey);
- sb.append("amount10apicodeCS0001collect_code");
- sb.append(data.get("collect_code").trim());
- sb.append("order_no");
- sb.append(data.get("order_no").trim());
- sb.append("timestamp");
- sb.append(data.get("timestamp").trim());
- sb.append(apikey);
- logger.info(sb.toString());
- if (signType.equals(WxConstants.SING_MD5)) {
- logger.info(MD5(sb.toString()).toUpperCase());
- return MD5(sb.toString()).toLowerCase();
- }
- else if (signType.equals(WxConstants.SING_HMACSHA256)) {
- return HMACSHA256(sb.toString(), apikey);
- }
- else {
- throw new Exception(String.format("Invalid sign_type: %s", signType));
- }
- }
- /**
- * 生成 MD5
- * @param data 待处理数据
- * @return MD5结果
- */
- public static String MD5(String data) throws Exception {
- MessageDigest md = MessageDigest.getInstance("MD5");
- byte[] array = md.digest(data.getBytes("UTF-8"));
- StringBuilder sb = new StringBuilder();
- for (byte item : array) {
- sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
- }
- return sb.toString().toUpperCase();
- }
- /**
- * 生成 HMACSHA256
- * @param data 待处理数据
- * @param key 密钥
- * @return 加密结果
- * @throws Exception
- */
- public static String HMACSHA256(String data, String key) throws Exception {
- Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
- SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
- sha256_HMAC.init(secret_key);
- byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
- StringBuilder sb = new StringBuilder();
- for (byte item : array) {
- sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
- }
- return sb.toString().toUpperCase();
- }
- /**
- * @param data Map类型数据
- * @return XML格式的字符串
- * @throws Exception
- */
- public static String mapToXml(Map<String, String> data) throws Exception {
- DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
- DocumentBuilder documentBuilder= documentBuilderFactory.newDocumentBuilder();
- org.w3c.dom.Document document = documentBuilder.newDocument();
- org.w3c.dom.Element root = document.createElement("xml");
- document.appendChild(root);
- for (String key: data.keySet()) {
- String value = data.get(key);
- if (value == null) {
- value = "";
- }
- value = value.trim();
- org.w3c.dom.Element filed = document.createElement(key);
- filed.appendChild(document.createTextNode(value));
- root.appendChild(filed);
- }
- TransformerFactory tf = TransformerFactory.newInstance();
- Transformer transformer = tf.newTransformer();
- DOMSource source = new DOMSource(document);
- transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
- transformer.setOutputProperty(OutputKeys.INDENT, "yes");
- StringWriter writer = new StringWriter();
- StreamResult result = new StreamResult(writer);
- transformer.transform(source, result);
- String output = writer.getBuffer().toString(); //.replaceAll("\n|\r", "");
- try {
- writer.close();
- }
- catch (Exception ex) {
- }
- return output;
- }
- /**
- * 处理 HTTPS API返回数据,转换成Map对象。return_code为SUCCESS时,验证签名。
- * @param xmlStr API返回的XML格式数据
- * @return Map类型数据
- * @throws Exception
- */
- public static Map<String, String> processResponseXml(String xmlStr,String signType) throws Exception {
- String RETURN_CODE = WxConstants.RETURN_CODE;
- String return_code;
- Map<String, String> respData = xmlToMap(xmlStr);
- if (respData.containsKey(RETURN_CODE)) {
- return_code = respData.get(RETURN_CODE);
- }
- else {
- throw new Exception(String.format("No `return_code` in XML: %s", xmlStr));
- }
- if (return_code.equals("FAIL")) {
- return respData;
- }
- else if (return_code.equals("SUCCESS")) {
- if (isResponseSignatureValid(respData,signType)) {
- return respData;
- }
- else {
- throw new Exception(String.format("Invalid sign value in XML: %s", xmlStr));
- }
- }
- else {
- throw new Exception(String.format("return_code value %s is invalid in XML: %s", return_code, xmlStr));
- }
- }
- /**
- * XML格式字符串转换为Map
- * @param strXML XML字符串
- * @return XML数据转换后的Map
- * @throws Exception
- */
- public static Map<String, String> xmlToMap(String strXML) throws Exception {
- try {
- Map<String, String> data = new HashMap<String, String>();
- DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
- String FEATURE = "http://apache.org/xml/features/disallow-doctype-decl";
- documentBuilderFactory.setFeature(FEATURE, true);
- FEATURE = "http://xml.org/sax/features/external-general-entities";
- documentBuilderFactory.setFeature(FEATURE, false);
- FEATURE = "http://xml.org/sax/features/external-parameter-entities";
- documentBuilderFactory.setFeature(FEATURE, false);
- FEATURE = "http://apache.org/xml/features/nonvalidating/load-external-dtd";
- documentBuilderFactory.setFeature(FEATURE, false);
- documentBuilderFactory.setXIncludeAware(false);
- documentBuilderFactory.setExpandEntityReferences(false);
- DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
- InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
- org.w3c.dom.Document doc = documentBuilder.parse(stream);
- doc.getDocumentElement().normalize();
- NodeList nodeList = doc.getDocumentElement().getChildNodes();
- for (int idx = 0; idx < nodeList.getLength(); ++idx) {
- Node node = nodeList.item(idx);
- if (node.getNodeType() == Node.ELEMENT_NODE) {
- org.w3c.dom.Element element = (org.w3c.dom.Element) node;
- data.put(element.getNodeName(), element.getTextContent());
- }
- }
- try {
- stream.close();
- } catch (Exception ex) {
- // do nothing
- }
- return data;
- } catch (Exception ex) {
- throw ex;
- }
- }
- /**
- * 判断xml数据的sign是否有效,必须包含sign字段,否则返回false。
- * @param reqData 向wxpay post的请求数据
- * @return 签名是否有效
- * @throws Exception
- */
- private static boolean isResponseSignatureValid(final Map<String, String> reqData,String signType) throws Exception {
- // 返回数据的签名方式和请求中给定的签名方式是一致的
- return isSignatureValid(reqData,WxConfig.apikey,signType);
- }
- /**
- * 判断签名是否正确,必须包含sign字段,否则返回false。
- * @param data Map类型数据
- * @param key API密钥
- * @param signType 签名方式
- * @return 签名是否正确
- * @throws Exception
- */
- public static boolean isSignatureValid(Map<String, String> data, String key, String signType) throws Exception {
- if (!data.containsKey("sign")) {
- return false;
- }
- String sign = data.get("sign");
- return getSignature(data, key, signType).equals(sign);
- }
- // /**
- // * 生成支付二维码
- // * @param response 响应
- // * @param contents url链接
- // * @throws Exception
- // */
- // public static void writerPayImage(HttpServletResponse response, String contents) throws Exception{
- // ServletOutputStream out = response.getOutputStream();
- // try {
- // Map<EncodeHintType,Object> hints = new HashMap<EncodeHintType,Object>();
- // hints.put(EncodeHintType.CHARACTER_SET,"UTF-8");
- // hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L);
- // hints.put(EncodeHintType.MARGIN, 0);
- // BitMatrix bitMatrix = new MultiFormatWriter().encode(contents, BarcodeFormat.QR_CODE,300,300,hints);
- // MatrixToImageWriter.writeToStream(bitMatrix,"jpg",out);
- // }catch (Exception e){
- // throw new Exception("生成二维码失败!");
- // }finally {
- // if(out != null){
- // out.flush();
- // out.close();
- // }
- // }
- // }
- /**
- * 生成商户订单号
- * @return String
- */
- public static String mchOrderNo(){
- SimpleDateFormat sdf = new SimpleDateFormat("yyMMddHHmmss");
- String date = sdf.format(new Date());
- Random random = new Random();
- String fourRandom = String.valueOf(random.nextInt(10000));
- int randLength = fourRandom.length();
- //不足4位继续补充
- if(randLength<4){
- for(int remain = 1; remain <= 4 - randLength; remain ++ ){
- fourRandom += random.nextInt(10) ;
- }
- }
- return date+fourRandom;
- }
- /**
- * 生成默认用户名
- * @return String
- */
- public static String createDefaultUserName(){
- SimpleDateFormat sdf = new SimpleDateFormat("ddHHmmss");
- String date = sdf.format(new Date());
- return "微信用户" + date;
- }
- /**
- * 返回信息给微信
- * @param response
- * @param content 内容
- * @throws Exception
- */
- public static void responsePrint(HttpServletResponse response, String content) throws Exception{
- response.setCharacterEncoding("UTF-8");
- response.setContentType("text/xml");
- response.getWriter().print(content);
- response.getWriter().flush();
- response.getWriter().close();
- }
- }
|