水平有限 部分代码还是参考了网上大神的代码 主要是为了纪念一下自己的码代码过程 不足之处忘多多包涵。
1.springboot项目就不多说了,然后配置一下server.port=80,因为微信公众平台的服务器配置要求支持80或43端口,然后映射一下自己的127.0.0.1:80一下,就可以在本地调试事件推送了。映射方法:http://ngrok.ciqiuwl.cn/ 这是一位前辈提供的服务器,这样很方便的能在本地进行各种debug,大大方便了开发;
2.微信公众号的服务器配置要验证token的代码如下:因为这是一个get方法,后面的事件推送则是post,所以随便新建个controller 再在里面新建2个方法一个处理get用于token验证 一个用于post负责事件推送;
get部分代码如下:@api以及@apiOperation是swagger2,有了它就可以不用写开发文档了,很棒的发明!再推荐一个好用的 http://www.fangbei.org/tool/message 微信调试器 能方便调试各种接口。
@Api(tags = "微信相关模块")
@RestController
@RequestMapping(value = "/wx")
public class WXController extends BaseController {
Logger logger = LoggerFactory.getLogger(WXController.class);
@ApiOperation("get用于微信配置服务器的验证")
@RequestMapping(value = "security", method = RequestMethod.GET)
public void doGet(HttpServletRequest request, HttpServletResponse response,
@RequestParam(value = "signature", required = true) String signature,
@RequestParam(value = "timestamp", required = true) String timestamp,
@RequestParam(value = "nonce", required = true) String nonce,
@RequestParam(value = "echostr", required = true) String echostr) {
try {
if (SignUtil.checkSignature(signature, timestamp, nonce)) {
PrintWriter out = response.getWriter();
out.print(echostr);
out.close();
} else {
logger.info("这里存在非法请求!");
}
} catch (Exception e) {
logger.error("" + e.getMessage());
}
下面是验证签名的类,然后微信平台测试账号那里填上你之前用ngrok映射的域名/wx/security token填你在下面接口里面设置的 看能否设置成功 不成功的话看127.0.0.1/wx/security 这个你能否成功访问 可能需要加上你的项目名称什么的 反正我是可以的。
package soke.home.util;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Random;
import org.apache.commons.codec.digest.DigestUtils;
public class SignUtil {
// TODO 与接口配置信息中的 Token 要一致
/** * 验证签名 * @param signature * @param timestamp * @param nonce * @return */
public static boolean checkSignature(String signature, String timestamp, String nonce) {
// 将token、timestamp、nonce三个参数进行字典序排序
String[] arr = new String[] { WeixinUtil.token, timestamp, nonce };
Arrays.sort(arr);
StringBuilder content = new StringBuilder();
for (int i = 0; i < arr.length; i++) {
content.append(arr[i]);
}
MessageDigest md = null;
String tmpStr = null;
try {
md = MessageDigest.getInstance("SHA-1");
// 将三个参数字符串拼接成一个字符串进行 sha1 加密 byte[]
byte[] digest = md.digest(content.toString().getBytes());
tmpStr = byteToStr(digest);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
content = null;
// 将 sha1 加密后的字符串可与 signature 对比,标识该请求来源于微信
return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false;
}
/** * 将字节数组转换为十六进制字符串 * @param byteArray * @return */
public static String byteToStr(byte[] byteArray) {
String strDigest = "";
for (int i = 0; i < byteArray.length; i++) {
strDigest += byteToHexStr(byteArray[i]);
}
return strDigest;
}
/** * 将字节转换为十六进制字符串 * @param mByte * @return */
private static String byteToHexStr(byte mByte) {
char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
char[] tempArr = new char[2];
tempArr[0] = Digit[(mByte >>> 4) & 0X0F];
tempArr[1] = Digit[mByte & 0X0F];
String s = new String(tempArr);
return s;
}
/**
*
* @param params
* Map数组,里面需要 appid//appId mch_id//商户号 device_info//设备信息
* body//商品描述 nonce_str//随机字符串
* @param key
* @return String(签名)
*/
public static String WeChatPay(Map<String, String> params, String key) {
List<String> list = new ArrayList<String>(params.keySet());
Collections.sort(list);
StringBuffer sb = new StringBuffer();
for (String keyVal : list) {
sb.append(keyVal + "=" + params.get(keyVal) + "&");
}
sb.append("key=" + key);
// System.err.println(sb.toString());
return DigestUtils.md5Hex(sb.toString()).toUpperCase();
}
/**
* 获得随机字符串
*
* @return
*/
public static String getNonceStr() {
Random random = new Random();
long val = random.nextLong();
String res = DigestUtils.md5Hex(val + "yzx").toUpperCase();
if (32 < res.length()) {
return res.substring(0, 32);
} else {
return res;
}
}
}
@ApiOperation("用于推送事件的回调")
@Anonymous
@RequestMapping(value = "security", method = RequestMethod.POST)
/**
*
* @param request
* @param response
* @throws IOException
*/
public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, DocumentException {
// 将请求、响应的编码均设置为UTF-8(防止中文乱码)
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
// 调用核心业务类接收消息、处理消息
String respMessage = processRequest(request);
// 响应消息
PrintWriter out = response.getWriter();
out.print(respMessage);
out.flush();
out.close();
}
private String processRequest(HttpServletRequest request) throws IOException, DocumentException {
String respMessage = "", respContent = "", respContentBussiness = "";
// xml请求解析
Map<String, String> requestMap = WeixinUtil.parseXml(request, null);
// 发送方帐号(open_id)
String fromUserName = requestMap.get("FromUserName");
// 公众帐号
String toUserName = requestMap.get("ToUserName");
// 消息类型
String msgType = requestMap.get("MsgType");
TextMessage textMessage = new TextMessage();
// FromUserName 发送方帐号
textMessage.setToUserName(fromUserName);
// ToUserName 开发者微信号
textMessage.setFromUserName(toUserName);
// MsgType-消息类型
textMessage.setMsgType(WeiXinConstants.RESP_MESSAGE_TYPE_TEXT);
// CreateTime-消息创建时间-(整型)
textMessage.setCreateTime(System.currentTimeMillis());
if (msgType.equals(WeiXinConstants.REQ_MESSAGE_TYPE_TEXT)) {
// 回复任何文字发送图文消息(这个设计,我也很绝望啊。。。)
// 这里用枚举的设计会不会好一点
respContent = WeixinUtil.getFocusOnPushTest(fromUserName, toUserName);
} else if (msgType.equals(WeiXinConstants.REQ_MESSAGE_TYPE_IMAGE)) {
} else if (msgType.equals(WeiXinConstants.REQ_MESSAGE_TYPE_LOCATION)) {
} else if (msgType.equals(WeiXinConstants.REQ_MESSAGE_TYPE_LINK)) {
} else if (msgType.equals(WeiXinConstants.REQ_MESSAGE_TYPE_VOICE)) {
} else if (msgType.equals(WeiXinConstants.REQ_MESSAGE_TYPE_EVENT)) {
String eventType = requestMap.get("Event");
// 这里获取用户的基本信息
JSONObject jsonObject = WeixinUtil.getUserInfoFromWX(fromUserName);
String unionid = jsonObject.getString("unionid");
String headimgurl = jsonObject.getString("headimgurl");
// 更新頭像
User user1 = User.findByWX(unionid);
if (user1 != null) {
if (!headimgurl.equals(user1.getAvatar())) {
user1.setAvatar(headimgurl);
user1.save();
}
}
if (eventType.equals(WeiXinConstants.EVENT_TYPE_SUBSCRIBE)) {
respContent = WeixinUtil.getFocusOnPushTest(fromUserName, toUserName);
String eventKey = requestMap.get("EventKey");
// 说明是带参数的二维码进入的
if (eventKey != null) {
// 第一次关注前面有这个需要截一下
String id = requestMap.get("EventKey").replace("qrscene_", "");
//内部逻辑
respContentBussiness = WeixinUtil.join(id, request, fromUserName, respContentBussiness);
}
} else if (eventType.equals(WeiXinConstants.EVENT_TYPE_SCAN)) {
} else if (eventType.equals(WeiXinConstants.EVENT_TYPE_UNSUBSCRIBE)) {
} else if (eventType.equals(WeiXinConstants.EVENT_TYPE_CLICK)) {
}
}
// 空白字符串
String blank = " ";
if (respContentBussiness != null && respContentBussiness != blank && respContentBussiness != "") {
respContentBussiness = "<![CDATA[" + respContentBussiness + "]]>";
textMessage.setContent(respContentBussiness);
respMessage = WeixinUtil.objectToXml(textMessage);
} else if (respContent != null && respContent != blank && respContent != "") {
respMessage = respContent;
}
System.err.println("微信触发事件回调,传递过去的信息是:" + respMessage);
return respMessage;
}
下面的对xml的处理内容 解析微信传过来的xml内容 以及把自己的内容转换成xml文件传过去 以及一些基本常量和一些基本方法的配置(比如结合里面的获取二维码地址和ticket可以获取带参数的二维码 然后扫描就会触发post事件 会接收到传过去的secen_id 然后就可以对这个id进行惨无人道的操作)
package soke.home.util;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.security.KeyStore;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import javax.net.ssl.SSLContext;
import javax.servlet.http.HttpServletRequest;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Namespace;
import org.dom4j.QName;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import com.alibaba.fastjson.JSONObject;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.core.util.QuickWriter;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.io.xml.XppDriver;
import com.typesafe.config.ConfigFactory;
import soke.common.util.GetPostUtil;
import soke.home.classes.domain.model.CpClass;
import soke.home.classes.domain.model.CpClassMember;
import soke.home.family.domain.model.CpFamily;
import soke.home.message.domain.model.CpMsg;
import soke.home.message.domain.model.CpMsgDnd;
import soke.home.message.domain.model.CpMsgInbox;
import soke.home.message.domain.model.Message;
import soke.home.message.domain.model.MessageManagerPool;
import soke.home.org.domain.model.OrgMember;
import soke.home.org.domain.model.Organization;
import soke.home.user.domain.model.User;
import soke.home.user.enums.JFTRole;
import soke.home.wx.annotation.XStreamCDATA;
import soke.home.wx.constants.WeiXinConstants;
import soke.home.wx.thread.HomeVisitThread;
import soke.memory.redis.RedisDb;
@SuppressWarnings("deprecation")
@Component
/**
*
* @author 15293
*
*/
public class WeixinUtil {
static Logger logger = LoggerFactory.getLogger(WeixinUtil.class);
/*-------------------------WX---------------------------*/
/** appId 微信的正式环境 */
public static String appID = ConfigFactory.load().getString("weixin.appId");
public static String appsecret = ConfigFactory.load().getString("weixin.appsecret");
/** 商品号 */
public static String mch_id = ConfigFactory.load().getString("weixin.mchId");
/** 付款回调地址 */
public static String notify_url = ConfigFactory.load().getString("weixin.notifyUrl");
/** 微信公众号Key */
public static String key = ConfigFactory.load().getString("weixin.key");
/** 微信公众号校验的字段 */
public static String token = ConfigFactory.load().getString("weixin.token");
/** 项目路径 **/
public static String applyaddress = ConfigFactory.load().getString("weixin.applyaddress");
/** 发送红包的证书路径 **/
public static String redPaperPath = ConfigFactory.load().getString("wexin.redPaperPath");
/**
* 获取微信的access_token(redis里面获取不到就自己去取)
*
* @return String(null)
*
**/
public static String getAccessToken() {
String accessToken = null;
if (RedisDb.exists(WeiXinConstants.ACCESSTOKEN_IN_REDIS)) {
accessToken = RedisDb.getString("access_token");
}
String aString = GetPostUtil.sendGet("https://api.weixin.qq.com/cgi-bin/token",
"grant_type=client_credential&appid=" + appID + "&secret=" + appsecret);
JSONObject jsonObject = JSONObject.parseObject(aString);
if (jsonObject.get("access_token") != null) {
accessToken = (String) jsonObject.get("access_token");
RedisDb.setAccessToken("access_token", accessToken, WeiXinConstants.EXPIRETIME);
} else {
logger.error("access_token的获取有问题" + jsonObject);
}
return accessToken;
}
/**
* 获取生成临时二维码需要的ticket
*
* 1.设置了过期时间为2592000---即30天 2.如果报40001(access_token无效)的错误
* 就移除Redis里面的access_token重新生成
*
* @param l:传入的带参数的二维码里面存的参数值
*
* @return String(null)
*
*/
public static String getTicket(long l) {
String accessToken = getAccessToken();
JSONObject jsonObject = JSONObject.parseObject(
"{\"expire_seconds\":2592000,\"action_name\": \"QR_SCENE\", \"action_info\": {\"scene\": {\"scene_id\":"
+ l + "}}}");
String aString2 = GetPostUtil
.post("https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=" + accessToken, jsonObject);
JSONObject jsonObject2 = JSONObject.parseObject(aString2);
// 当出现access_token出现40001异常的时候 从redis里面移除
String errcode = (String) jsonObject2.get("errcode");
if (errcode != null && errcode.equals("40001")) {
RedisDb.delString("access_token");
accessToken = getAccessToken();
GetPostUtil.post("https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=" + accessToken, jsonObject);
}
String ticket = (String) jsonObject2.get("ticket");
return ticket;
}
/**
* 解析微信发来的请求或者XML为键值对,方便取值(因为有request和xml2种情况 ,所以合并成一个接口,传入2个参数 )
*
* @param request:requset请求
* @param xml:String的xml语句
*
* @return HashMap<Sring,String>
*
* @throws IOException
* @throws DocumentException
*
*/
public static Map<String, String> parseXml(HttpServletRequest request, String xml)
throws IOException, DocumentException {
// 将解析结果存储在HashMap中
Map<String, String> map = new HashMap<String, String>();
InputStream inputStream = null;
Document document = null;
if (request != null) {
inputStream = request.getInputStream();
SAXReader reader = new SAXReader();
document = reader.read(inputStream);
}
if (xml != null) {
document = DocumentHelper.parseText(xml);
}
Element rootElement = document.getRootElement();
ele2map(map, rootElement);
if (inputStream != null) {
inputStream.close();
inputStream = null;
}
return map;
}
/**
* 解析xml文件
*/
static void ele2map(Map<String, String> map, Element ele) {
// 获得当前节点的子节点
List<Element> elements = ele.elements();
if (elements.size() == 0) {
// 没有子节点说明当前节点是叶子节点,直接取值即可
if (ele.getText() != null) {
map.put(ele.getName(), ele.getText());
}
} else if (elements.size() == 1) {
// 只有一个子节点说明不用考虑list的情况,直接继续递归即可
Map<String, String> tempMap = new HashMap<String, String>();
ele2map(map, elements.get(0));
if (tempMap.get(ele.getName()) != null) {
map.put(ele.getName(), tempMap.get(ele.getName()));
}
} else {
// 多个子节点的话就得考虑list的情况了,比如多个子节点有节点名称相同的
// 构造一个map用来去重
Map<String, String> tempMap = new HashMap<String, String>();
for (Element element : elements) {
tempMap.put(element.getName(), null);
}
Set<String> keySet = tempMap.keySet();
for (String string : keySet) {
Namespace namespace = elements.get(0).getNamespace();
List<Element> elements2 = ele.elements(new QName(string, namespace));
// 如果同名的数目大于1则表示要构建list 现在假设不可能重名
if (elements2.size() > 1) {
} else {
// 同名的数量不大于1则直接递归去
ele2map(map, elements2.get(0));
}
}
}
}
/**
* 根据access和OpenId获取用户基本信息
*
* @param openid
*
* @return JsonObject(null)
*/
public static JSONObject getUserInfoFromWX(String openid) {
if (openid == null) {
return null;
}
String url = "https://api.weixin.qq.com/cgi-bin/user/info";
String accessToken = getAccessToken();
String info = GetPostUtil.sendGet(url, "access_token=" + accessToken + "&openid=" + openid + "&lang=zh_CN");
JSONObject jsonObject = JSONObject.parseObject(info);
return jsonObject;
}
/**
* 把object转换成xml对象
*
* @param object
*
* @return String
*/
public static String objectToXml(Object object) {
XStream xstream = createXstream();
xstream.alias("xml", object.getClass());
return xstream.toXML(object);
}
/**
* 利用Xstream来把Object转换成xml文件(匿名内部类)
*
* @return
*/
public static XStream createXstream() {
return new XStream(new XppDriver() {
@Override
public HierarchicalStreamWriter createWriter(Writer out) {
return new PrettyPrintWriter(out) {
boolean cdata = false;
Class<?> targetClass = null;
@Override
public void startNode(String name, @SuppressWarnings("rawtypes") Class clazz) {
super.startNode(name, clazz);
// 业务处理,对于用XStreamCDATA标记的Field,需要加上CDATA标签
if (!"xml".equals(name)) {
cdata = needCDATA(targetClass, name);
} else {
targetClass = clazz;
}
}
@Override
protected void writeText(QuickWriter writer, String text) {
if (cdata) {
writer.write("<![CDATA[");
writer.write(text);
writer.write("]]>");
} else {
writer.write(text);
}
}
};
}
});
}
/**
* 循环往上面遍历,看域上面是否有注解
*
* @param targetClass
* @param fieldAlias
* @return
*/
private static boolean needCDATA(Class<?> targetClass, String fieldAlias) {
boolean cdata = false;
cdata = existsCDATA(targetClass, fieldAlias);
if (cdata) {
return cdata;
}
Class<?> superClass = targetClass.getSuperclass();
while (!superClass.equals(Object.class)) {
cdata = existsCDATA(superClass, fieldAlias);
if (cdata) {
return cdata;
}
superClass = superClass.getClass().getSuperclass();
}
return false;
}
private static boolean existsCDATA(Class<?> clazz, String fieldAlias) {
// 特例添加
if ("MediaId".equals(fieldAlias)) {
return true;
}
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (field.getAnnotation(XStreamCDATA.class) != null) {
XStreamAlias xStreamAlias = field.getAnnotation(XStreamAlias.class);
if (null != xStreamAlias) {
if (fieldAlias.equals(xStreamAlias.value())) {
return true;
}
} else {
// 如果传入的xml节点的名称和@xstreamAlias的相等也返回true
if (fieldAlias.equals(field.getName())) {
return true;
}
}
}
}
return false;
}
/**
* 判断是否是QQ表情
*
* @param content
* @return
*/
public static boolean isQqFace(String content) {
boolean result = false;
// 判断QQ表情的正则表达式
String qqfaceRegex = "/::\\)|/::~|/::B|/::\\||/:8-\\)|/::<|/::$|/::X|/::Z|/::'\\(|/::-\\||/::@|"
+ "/::P|/::D|/::O|/::\\(|/::\\+|/:--b|/::Q|/::T|/:,@P|/:,@-D|/::d|/:,@o|/::g|/:\\|-\\)|"
+ "/::!|/::L|/::>|/::,@|/:,@f|/::-S|/:\\?|/:,@x|/:,@@|/::8|/:,@!|/:!!!|/:xx|/:bye|/:wipe|"
+ "/:dig|/:handclap|/:&-\\(|/:B-\\)|/:<@|/:@>|/::-O|/:>-\\||/:P-\\(|/::'\\||/:X-\\)|/::\\*|"
+ "/:@x|/:8\\*|/:pd|/:<W>|/:beer|/:basketb|/:oo|/:coffee|/:eat|/:pig|/:rose|/:fade|/:showlove|"
+ "/:heart|/:break|/:cake|/:li|/:bome|/:kn|/:footb|/:ladybug|/:shit|/:moon|/:sun|/:gift|/:hug|"
+ "/:strong|/:weak|/:share|/:v|/:@\\)|/:jj|/:@@|/:bad|/:lvu|/:no|/:ok|/:love|/:<L>|/:jump|/:shake|"
+ "/:<O>|/:circle|/:kotow|/:turn|/:skip|/:oY|/:#-0|/:hiphot|/:kiss|/:<&|/:&>"
+ "|\\[Smart\\]|\\[Hey\\]|\\[Facepalm\\]|\\[Smirk\\]|\\[Concerned\\]|\\[Yeah!\\]|\\[Packet\\]|\\[Chick\\]";
Pattern p = Pattern.compile(qqfaceRegex);
Matcher m = p.matcher(content);
if (m.matches()) {
result = true;
}
return result;
}
/**
* 调起Js
*
* @return
*/
public static String getJsSdkConfigTicket() {
String ticket = null;
if (RedisDb.exists("jsapi_ticket")) {
ticket = RedisDb.getString("jsapi_ticket");
}
if (ticket != null) {
System.err.println("jsapi_ticket是从Redis里面取得,值为:");
return ticket;
}
String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket";
String accessToken = getAccessToken();
String info = GetPostUtil.sendGet(url, "access_token=" + accessToken + "&type=jsapi");
JSONObject jsonObject = JSONObject.parseObject(info);
System.err.print("获取JsApiTicket的值:ticket是手动获取的:" + jsonObject);
ticket = jsonObject.getString("ticket");
RedisDb.setAccessToken("jsapi_ticket", ticket, WeiXinConstants.EXPIRETIME);
return ticket;
}
/**
* 生成内部订单号
*/
public static String createOutTradeNo(Integer uid) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
String date = LocalDateTime.now().format(formatter);
Random random = new Random();
String uuid = uid + "";
int rannum = (int) (random.nextDouble() * (99999 - 10000 + 1)) + 10000;
if (uid == null) {
uuid = "00";
} else if (uid < 10) {
uuid = "0" + uid;
}
return date + uuid + rannum;
}
/**
* 因为微信授权的特殊性 同一code只能获取一次信息 所以用redis来处理一下;
*
* @param code
* 微信网页授权码
*
* @param openId
* openId
*/
public static String opreateOpenId(String code, String openId) {
if (code == null) {
logger.info("WeixinUtil的opreateOpenId传入的code为null");
return null;
}
if (openId != null) {
RedisDb.setString(code, openId);
return null;
} else {
openId = RedisDb.getString(code);
return openId;
}
}
/**
* @param mchId
* ----商户Id
*
* @param xml
* ----包含签名的xml文件
*
* @param url
* ----需要post数据的url地址
*
* @return String
*
*/
public static String WxRedPaperSend(String mchId, String xml, String url) {
try {
KeyStore keyStore = KeyStore.getInstance("PKCS12");
FileInputStream instream = new FileInputStream(new File(redPaperPath));
try {
keyStore.load(instream, mchId.toCharArray());
} finally {
instream.close();
}
// Trust own CA and all self-signed certs
SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, mchId.toCharArray()).build();
// Allow TLSv1 protocol only
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" },
null, SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
StringBuffer result = new StringBuffer();
try {
HttpPost httpPost = new HttpPost(url);
// 构造HttpClient的HttpPost请求
httpPost.addHeader("Connection", "keep-alive");
httpPost.addHeader("Accept", "*/*");
httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
httpPost.addHeader("Host", "api.mch.weixin.qq.com");
httpPost.addHeader("X-Requested-With", "XMLHttpRequest");
httpPost.addHeader("Cache-Control", "max-age=0");
httpPost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
httpPost.setEntity(new StringEntity(xml, "UTF-8"));
CloseableHttpResponse response = httpclient.execute(httpPost);
try {
HttpEntity entity = response.getEntity();
System.out.println(response.getStatusLine());
if (entity != null) {
System.out.println("Response content length: " + entity.getContentLength());
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent()));
String text;
while ((text = bufferedReader.readLine()) != null) {
result.append(text);
result.append("\n");
}
}
EntityUtils.consume(entity);
} finally {
response.close();
}
} finally {
httpclient.close();
}
return result.toString();
} catch (Exception e) {
e.printStackTrace();
logger.error("发送现金红包的封装接口出错:" + e.getMessage());
return null;
}
}
}
还有一个关于注解的类和文本信息类,注解主要是为了有的字段返回去的xml不加上><![CDATA[]]>的标识,文本信息类用于返回文本信息;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD })
public @interface XStreamCDATA {
}
import com.thoughtworks.xstream.annotations.XStreamAlias;
public class TextMessage {
// 接收方帐号(收到的OpenID)
@XStreamAlias("ToUserName")
private String ToUserName;
// 开发者微信号
@XStreamAlias("FromUserName")
private String FromUserName;
// 消息创建时间 (整型)
private long CreateTime;
// 消息类型(text/music/news)
@XStreamAlias("MsgType")
private String MsgType;
// 位0x0001被标志时,星标刚收到的消息
@XStreamAlias("Context")
private String Content;
public String getToUserName() {
return ToUserName;
}
public void setToUserName(String toUserName) {
ToUserName = toUserName;
}
public String getFromUserName() {
return FromUserName;
}
public void setFromUserName(String fromUserName) {
FromUserName = fromUserName;
}
public long getCreateTime() {
return CreateTime;
}
public void setCreateTime(long createTime) {
CreateTime = createTime;
}
public String getMsgType() {
return MsgType;
}
public void setMsgType(String msgType) {
MsgType = msgType;
}
public String getContent() {
return Content;
}
public void setContent(String content) {
Content = content;
}
}
顺便加上几个需要的注释(关于xml的),顺便吐槽一下:没有认证的话获取不到unionId,很坑,scene_str的话EventKey获取不到:因为有些代码是项目里面的逻辑调用,所以手动删了一点,希望不会报错哈哈。。。。无聊的话可以随便接个机器人来玩玩。。。。
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.10</version>
</dependency>