最近刚做了微信开放平台全网发布的开发,整理一下贴出来
前置条件 已经做好了相关的开发工作(比如扫码授权之类的),项目导入了微信SDK(最后会附上我自己用的SDK jar包)
sdk jar 地址: https://github.com/liyiorg/weixin-popular
BUG :weixin.popular.bean.message.EventMessage 内的@XmlElement(name = "MsgID") private String msgId; // 消息ID号 红色处原来D是小写,应该是大写,不知道现在改了没有,自己注意
platformToken:你要全网发布的开放平台填写的token, platformAESKey:你要全网发布的开放平台填写的加密秘钥, platformAppId:你要全网发布的开放平台的appId
/**
* 功能描述:扫码授权,第三方开放平台每10分钟推送一次component_verify_ticket接受处理方法,这是controller
* @author yanfei.li
* @date 2017年9月26日 下午3:42:24
* @param request
* @param response
* @return "success"
* @throws BusinessException
*/
@RequestMapping(value = "/platform/event/receive")
@ResponseBody
public String receiver(HttpServletRequest request, HttpServletResponse response) throws BusinessException{
try {
ServletInputStream inputStream = request.getInputStream();
if (inputStream != null) {
String xmlData = StreamUtils.copyToString(inputStream,Charset.forName("utf-8"));
String timestamp = request.getParameter("timestamp");
String nonce = request.getParameter("nonce");
String msgSignature = request.getParameter("msg_signature");
//解密,platformToken:你要全网发布的开放平台填写的token, platformAESKey:你要全网发布的开放平台填写的加密秘钥, platformAppId:你要全网发布的开放平台的appId
WXBizMsgCrypt wxBizMsgCrypt = new WXBizMsgCrypt(platformToken, platformAESKey, platformAppId);
xmlData = wxBizMsgCrypt.decryptMsg(msgSignature, timestamp, nonce, xmlData);
//下面是将component_verify_ticket 信息保存到自己的数据库,这一块应该是调用service再调用dao的,为了方便整合到一起了,自己拆分。
//转换成map,XMLConverUtil是微信SDK的工具
Map<String,String> xmlMap=XMLConverUtil.convertToMap(xmlData);
String appId = xmlMap.get("AppId");
String componentVerifyTicket = xmlMap.get("ComponentVerifyTicket");
Date createTime = new Date(Long.parseLong(xmlMap.get("CreateTime"))*1000);
String infoType = xmlMap.get("InfoType");
String authorizerAppid = xmlMap.get("AuthorizerAppid");
String authCode = xmlMap.get("AuthorizationCode");
String authCodeExpiredTime = xmlMap.get("AuthorizationCodeExpiredTime");
//数据库表实体初始化
WechatVerifyTicketEntity verifyticket = null;
verifyticket = verifyticketDao.findOne(appId, infoType);
if(verifyticket == null ) {
verifyticket = new WechatVerifyTicketEntity();
}
verifyticket.setAppId(appId);
verifyticket.setCreateTime(createTime);
verifyticket.setInfoType(infoType);
verifyticket.setAuthorizerAppid(authorizerAppid);
verifyticket.setAuthCode(authCode);
verifyticket.setAuthCodeExpiredTime(authCodeExpiredTime);
verifyticket.setVerifyTicket(componentVerifyTicket);
//保存到数据库
this.verifyticketDao.saveOrUpdate(verifyticket);
}
} catch (IOException | AesException e) {
logger.error("/wxopen/platform/event/receive error: ", e);
}
//不管出没出错,都返回了success,出错信息自己查自己的日志
return "success";
}
消息与事件推送受理入口
/**
* 功能描述:微信消息与事件推送 接受处理方法入口
* @author yanfei.li
* @date 2017年9月26日 下午3:44:29
* @param appid 公众号appid
* @param request
* @param response
* @throws BusinessException
*/
@RequestMapping(value = "/platform/callback/{appid}")
@ResponseBody
public void callback(@PathVariable("appid") String appid, HttpServletRequest request, HttpServletResponse response) throws BusinessException{
try{
ServletInputStream inputStream = request.getInputStream();
if (inputStream != null) {
//StreamUtils 微信SDK的工具类
String xmlData = StreamUtils.copyToString(inputStream,Charset.forName("utf-8"));
inputStream.close();
String timestamp = request.getParameter("timestamp");
String nonce = request.getParameter("nonce");
String msgSignature = request.getParameter("msg_signature");
//解密
WXBizMsgCrypt wxBizMsgCrypt = new WXBizMsgCrypt(platformToken, platformAESKey, platformAppId);
xmlData = wxBizMsgCrypt.decryptMsg(msgSignature, timestamp, nonce, xmlData);
logger.info("==>receiveData=" + xmlData );
EventMessage eventMessage = null;
eventMessage = XMLConverUtil.convertToObject(EventMessage.class, xmlData);
if("wx570bc396a51b8ff8".equals(appid)){//全网发布,wx570bc396a51b8ff8是固定的微信全网发布测试公众号appid
publishThirdPlatform(eventMessage, request, response);
}else {//正常业务处理
normalBusiness(appid, eventMessage, request, response);
}
}else {
responseReplyMessage(response,"success");
}
}catch(Exception e){
logger.error("/wxopen/platform/callback/" + appid + " error: ", e);
if(!(e instanceof IOException)){
try {
responseReplyMessage(response,"success");
} catch (IOException e1) {
logger.error("/wxopen/platform/callback/" + appid + " return error: ", e1);
}
}else {
logger.error("IOException");
}
}
}
全网发布相关处理函数
/**
* 功能描述:全网发布主函数处理入口
* @author yanfei.li
* @date 2017年9月26日 下午3:41:22
* @param eventMessage
* @param request
* @param response
* @throws IOException
* @throws AesException
*/
private void publishThirdPlatform(EventMessage eventMessage,HttpServletRequest request, HttpServletResponse response) throws IOException, AesException{
String event = eventMessage.getMsgType();
if("event".equals(event)){
replyEventMessage(request, response,eventMessage);
}else if ("text".equals(eventMessage.getMsgType())) {
replyTextMessage(request, response, eventMessage);
}
}
/**
* 功能描述:全网发布,步骤二、三 回复文本消息
* @author yanfei.li
* @date 2017年9月26日 下午4:10:56
* @param request
* @param response
* @param eventMessage
* @throws BusinessException
* @throws IOException
*/
private void replyTextMessage(HttpServletRequest request, HttpServletResponse response, EventMessage eventMessage) throws BusinessException, IOException{
if(eventMessage == null){
throw new BusinessException(BusinessException.ERROR_INTERNAL_SERVER_ERROR, "==>replyTextMessage,eventMessage is null");
}
String content = eventMessage.getContent();
if("TESTCOMPONENT_MSG_TYPE_TEXT".equals(content)){//步骤二,回复文本消息
content = content + "_callback";
StringBuffer sb = new StringBuffer();
sb.append("<xml>");
sb.append("<ToUserName><![CDATA["+eventMessage.getFromUserName()+"]]></ToUserName>");
sb.append("<FromUserName><![CDATA["+eventMessage.getToUserName()+"]]></FromUserName>");
sb.append("<CreateTime>"+eventMessage.getCreateTime()+"</CreateTime>");
sb.append("<MsgType><![CDATA[text]]></MsgType>");
sb.append("<Content><![CDATA["+content+"]]></Content>");
sb.append("</xml>");
String replyMsg = sb.toString();
String returnValue = "";
WXBizMsgCrypt pc;
try {
//platformToken:你要全网发布的开放平台填写的token, platformAESKey:你要全网发布的开放平台填写的加密秘钥, platformAppId:你要全网发布的开放平台的appId
pc = new WXBizMsgCrypt(platformToken, platformAESKey, platformAppId);
returnValue = pc.encryptMsg(replyMsg, eventMessage.getCreateTime().toString(), request.getParameter("nonce"));
responseReplyMessage(response,returnValue);
} catch (AesException e) {
throw new BusinessException(BusinessException.ERROR_INTERNAL_SERVER_ERROR, "replyTextMessage",e);
}
}else {//步骤三,回复空字符串,然后调用客服接口发送消息
String touser = eventMessage.getFromUserName();//因为是往回发,所有接收人是消息发送人,容易写错
String authCode = content.replaceAll("QUERY_AUTH_CODE:", "");//authcode 用于“使用授权码换取公众号的授权信息”API,将$query_auth_code$的值赋值给API所需的参数authorization_code获取接口调用凭证。
try {
//直接回复""字符串
responseReplyMessage(response,"");
} catch (IOException e) {
throw new BusinessException(BusinessException.ERROR_INTERNAL_SERVER_ERROR, "replyTextMessage",e);
}
//调用客服接口发送消息
//获取测试公众号接口调用凭证,接口方法实现 见最后同名方法
String authorizerAccessToken = this.wechatTokenService.publishGetToken(authCode);
if(authorizerAccessToken == null){
throw new BusinessException(BusinessException.ERROR_INTERNAL_SERVER_ERROR, "==>get authorizer access token failure,value is null");
}
//Message TextMessage是微信SDK的
Message message = new TextMessage(touser,authCode+"_from_api");
//发送客服消息
weixin.popular.api.MessageAPI.messageCustomSend(token,JSON.toJSONString(message));
}
}
/**
* 功能描述:全网发布,步骤一回复事件消息
* @author yanfei.li
* @date 2017年9月26日 下午4:34:14
* @param request
* @param response
* @param eventMessage
* @throws IOException
* @throws AesException
*/
private void replyEventMessage(HttpServletRequest request, HttpServletResponse response, EventMessage eventMessage) throws BusinessException, IOException{
if(eventMessage == null){
throw new BusinessException(BusinessException.ERROR_INTERNAL_SERVER_ERROR, "==>replyEvetMessage,eventMessage is null");
}
String content = eventMessage.getEvent() + "from_callback";
StringBuffer sb = new StringBuffer();
sb.append("<xml>");
sb.append("<ToUserName><![CDATA["+eventMessage.getFromUserName()+"]]></ToUserName>");
sb.append("<FromUserName><![CDATA["+eventMessage.getToUserName()+"]]></FromUserName>");
sb.append("<CreateTime>"+eventMessage.getCreateTime()+"</CreateTime>");
sb.append("<MsgType><![CDATA[text]]></MsgType>");
sb.append("<Content><![CDATA["+content+"]]></Content>");
sb.append("</xml>");
String replyMsg = sb.toString();
String returnValue = "";
WXBizMsgCrypt pc;
try {
//platformToken:你要全网发布的开放平台填写的token, platformAESKey:你要全网发布的开放平台填写的加密秘钥, platformAppId:你要全网发布的开放平台的appId
pc = new WXBizMsgCrypt(platformToken, platformAESKey, platformAppId);
returnValue = pc.encryptMsg(replyMsg, eventMessage.getCreateTime().toString(), request.getParameter("nonce"));
responseReplyMessage(response,returnValue);
} catch (AesException e) {
throw new BusinessException(BusinessException.ERROR_INTERNAL_SERVER_ERROR, "replyEventMessage",e);
}
}
/**
* 统一回复微信服务器
* @param response
* @param content
* @throws IOException
*/
public void responseReplyMessage(HttpServletResponse response,String content) throws IOException{
PrintWriter pw = response.getWriter();
pw.write(content);
pw.flush();
pw.close();
}
/**
* 功能描述:获取access_token
* @author yanfei.li
* @date 2017年9月27日 下午2:18:24
* @param authCode
* @return
*/
public String publishGetToken(String authCode) {
if(authCode == null){
logger.error("================>publishGetToken,authCode is null");
return null;
}
//获取每十分钟推送一次的那个ticket, platformAppId:你要全网发布的开放平台的appId
String verifyTicket = this.verifyticketDao.getRecentTicket(platformAppId);
if(verifyTicket == null){
logger.error("================>publishGetToken,verifyTicket is null");
return null;
}
//获取开放平台access_token,appSecret:你要全网发布的开放平台的appsecret, appId:你要全网发布的开放平台的appId
ComponentAccessToken accessToken = weixin.popular.api.ComponentAPI.api_component_token(appId, appSecret, verifyTicket);
if(accessToken == null){
logger.error("================>publishGetToken,accessToken is null");
return null;
}
String accessTokenStr = accessToken.getComponent_access_token();
ApiQueryAuthResult queryAuth = weixin.popular.api.ComponentAPI.api_query_auth(accessToken, appId, authCode);
if(queryAuth == null){
logger.error("================>publishGetToken,queryAuth is null");
return null;
}
return queryAuth.getAuthorization_info().getAuthorizer_access_token();
}
结合微信SDK工具包 实现的。请忽略日志打印,不能直接复制使用,另外正常的业务处理分支没有贴出来,只贴了全网发布的。消息推送入口对全网发布和正常业务处理做了分流处理,不影响原本的项目
全网发布,开放平台注意事项
1.公众号的IP白名单只针对通过公众号的appid和secret获取access_token时有效(直接通过api接口调用https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=xxx&secret=xxx),如果是通过扫码授权即微信开放平台则IP白名单无效(个人推测微信开放平台在所有微信公众号的默认白名单内),最好配置IP白名单,这样在appid和secret泄露后还有一层保护,因为IP白名单的修改需要管理员扫码确认,不能直接通过这俩值获取token
2.微信开放平台全网发布前,只接受授权测试公众号列表内的授权,一旦全网发布后会支持所有微信公众的授权。IP白名单配置的是访问开放平台的服务器的IP地址,发布不发布都需要,否则会报错
3.开放平台IP白名单的修改没有在发布的范畴内,可随时更改
4.调用开放平台接口所需要的appId和secret是对应开放平台应用的,跟公众号没有关系
5.授权事件接受URL主要是每十分钟推送一次component_verify_ticket,该值用户获取开放平台的接口调用凭证的参数之一
6.开放平台的公众号消息校验Token和加解密Key可以自己定义,但一定要和我们开发配置中保持对应一致
7.公众号消息与事件接受URL会将粉丝跟公众号的互动推送到该URL,%APPID%是当前公众号的APPID
8.已经发布的开放平台内容可以更改,更改完了还需要进行一次全网发布,会有一个覆盖现有发布,需要审核,审核完成之前将继续使用上次发布的内容
转载自:https://blog.csdn.net/li_yan_fei/article/details/78111996