企业号:新增获取微信服务器IP地址接口&调整回调模式下的首次验证的签名方式

This commit is contained in:
jy.hu 2014-12-28 21:08:16 +08:00
parent 167f696e83
commit a3cec6a17b
14 changed files with 167 additions and 60 deletions

View File

@ -200,9 +200,13 @@ netty的代码没有放到maven中心仓库,也没什么意义,因为最终需
* 2014-12-28
+ **weixin4j-qy**:增加用户进入应用的callback事件
+ **weixin4j-qy**: 增加用户进入应用的callback事件
+ **weixin4j-qy**:增加批量获取用户详情的接口
+ **weixin4j-qy**: 增加批量获取用户详情的接口
+ **weixin4j-qy**: 新增获取微信服务器IP接口
+ **weixin4j-qy**: 调整回调模式下的首次验证的签名方式
接下来
------

View File

@ -15,7 +15,6 @@ public class HttpWeixinMessage implements Serializable {
@XStreamAlias("Encrypt")
private String encryptContent;
private EncryptType encryptType;
private String msgSignature;
// 以下字段每次被动消息时都会带上
private String echoStr;
@ -23,10 +22,11 @@ public class HttpWeixinMessage implements Serializable {
private String nonce;
private String signature;
// 冗余字段
private String token;
// xml消息主体
private String xmlContent;
// xml消息明文主体
private String originalContent;
// request method
private String method;
@ -55,14 +55,6 @@ public class HttpWeixinMessage implements Serializable {
this.encryptType = encryptType;
}
public String getMsgSignature() {
return msgSignature;
}
public void setMsgSignature(String msgSignature) {
this.msgSignature = msgSignature;
}
public String getEchoStr() {
return echoStr;
}
@ -103,12 +95,12 @@ public class HttpWeixinMessage implements Serializable {
this.token = token;
}
public String getXmlContent() {
return xmlContent;
public String getOriginalContent() {
return originalContent;
}
public void setXmlContent(String xmlContent) {
this.xmlContent = xmlContent;
public void setOriginalContent(String originalContent) {
this.originalContent = originalContent;
}
public String getMethod() {
@ -123,9 +115,9 @@ public class HttpWeixinMessage implements Serializable {
public String toString() {
return "HttpMessage [toUserName=" + toUserName + ", encryptContent="
+ encryptContent + ", encryptType=" + encryptType
+ ", msgSignature=" + msgSignature + ", echoStr=" + echoStr
+ ", timeStamp=" + timeStamp + ", nonce=" + nonce
+ ", signature=" + signature + ", token=" + token
+ ", xmlContent=" + xmlContent + ", method=" + method + "]";
+ ", echoStr=" + echoStr + ", timeStamp=" + timeStamp
+ ", nonce=" + nonce + ", signature=" + signature + ", token="
+ token + ", originalContent=" + originalContent + ", method="
+ method + "]";
}
}

View File

@ -55,7 +55,7 @@ public class MessageUtil {
/**
* 对xml消息加密
*
* @param appId
* @param appId 应用ID
* @param encodingAesKey
* 加密密钥
* @param xmlContent
@ -107,7 +107,7 @@ public class MessageUtil {
// 使用BASE64对加密后的字符串进行编码
return Base64.encodeBase64String(encrypted);
} catch (Exception e) {
throw new WeixinException("-40006", "AES加密失败");
throw new WeixinException("-40006", "AES加密失败:" + e.getMessage());
}
}
@ -138,7 +138,7 @@ public class MessageUtil {
// 解密
original = cipher.doFinal(encrypted);
} catch (Exception e) {
throw new WeixinException("-40007", "AES解密失败");
throw new WeixinException("-40007", "AES解密失败:" + e.getMessage());
}
String xmlContent, fromAppId;
try {
@ -156,7 +156,8 @@ public class MessageUtil {
fromAppId = new String(Arrays.copyOfRange(bytes, 20 + xmlLength,
bytes.length), org.apache.http.Consts.UTF_8);
} catch (Exception e) {
throw new WeixinException("-40008", "公众平台发送的xml不合法");
throw new WeixinException("-40008", "公众平台发送的xml不合法:"
+ e.getMessage());
}
// 校验appId是否一致
if (!fromAppId.trim().equals(appId)) {
@ -198,8 +199,7 @@ public class MessageUtil {
return null;
}
MessageType messageType = MessageType.valueOf(type.toLowerCase());
Class<? extends BaseMsg> messageClass = messageType
.getMessageClass();
Class<? extends BaseMsg> messageClass = messageType.getMessageClass();
if (messageType == MessageType.event) {
type = doc.selectSingleNode("/xml/Event").getStringValue();
messageClass = EventType.valueOf(type.toLowerCase())

View File

@ -38,10 +38,10 @@ public class WeixinMessageDecoder extends
protected void decode(ChannelHandlerContext ctx, FullHttpRequest req,
List<Object> out) throws Exception {
WeixinMpAccount mpAccount = ConfigUtil.getWeixinMpAccount();
String xmlContent = req.content().toString(Consts.UTF_8);
String content = req.content().toString(Consts.UTF_8);
HttpWeixinMessage message = new HttpWeixinMessage();
if (StringUtils.isNotBlank(xmlContent)) {
message = XmlStream.get(xmlContent, HttpWeixinMessage.class);
if (StringUtils.isNotBlank(content)) {
message = XmlStream.get(content, HttpWeixinMessage.class);
}
message.setMethod(req.getMethod().name());
QueryStringDecoder queryDecoder = new QueryStringDecoder(req.getUri(),
@ -49,14 +49,11 @@ public class WeixinMessageDecoder extends
log.info("\n=================receive request=================");
log.info("{}", req.getMethod());
log.info("{}", req.getUri());
log.info("{}", xmlContent);
log.info("{}", content);
Map<String, List<String>> parameters = queryDecoder.parameters();
String encryptType = parameters.containsKey("encrypt_type") ? parameters
.get("encrypt_type").get(0) : EncryptType.RAW.name();
message.setEncryptType(EncryptType.valueOf(encryptType.toUpperCase()));
String msgSignature = parameters.containsKey("msg_signature") ? parameters
.get("msg_signature").get(0) : "";
message.setMsgSignature(msgSignature);
String echoStr = parameters.containsKey("echostr") ? parameters.get(
"echostr").get(0) : "";
message.setEchoStr(echoStr);
@ -70,12 +67,12 @@ public class WeixinMessageDecoder extends
.get("signature").get(0) : "";
message.setSignature(signature);
message.setXmlContent(xmlContent);
message.setOriginalContent(content);
if (message.getEncryptType() == EncryptType.AES) {
message.setXmlContent(MessageUtil.aesDecrypt(mpAccount.getId(),
mpAccount.getEncodingAesKey(), message.getEncryptContent()));
message.setOriginalContent(MessageUtil.aesDecrypt(
mpAccount.getId(), mpAccount.getEncodingAesKey(),
message.getEncryptContent()));
}
message.setToken(mpAccount.getToken());
out.add(message);
}
}

View File

@ -47,6 +47,7 @@ public class WeixinServerHandler extends
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
log.error("catch the exception:{}", cause.getMessage());
}
@Override
@ -69,7 +70,7 @@ public class WeixinServerHandler extends
validate = MessageUtil.signature(httpMessage.getToken(),
httpMessage.getTimeStamp(), httpMessage.getNonce(),
httpMessage.getEncryptContent()).equals(
httpMessage.getMsgSignature());
httpMessage.getSignature());
}
if (!validate) {
ctx.write(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
@ -77,7 +78,7 @@ public class WeixinServerHandler extends
return;
}
String xmlContent = httpMessage.getXmlContent();
String xmlContent = httpMessage.getOriginalContent();
WeixinAction action = actionMapping.getAction(xmlContent);
if (action == null) {
ctx.write(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,

View File

@ -91,6 +91,10 @@ weixin4j-qy
* 2014-12-28
+ **weixin4j-qy**:增加用户进入应用的callback事件
+ **weixin4j-qy**: 增加用户进入应用的callback事件
+ **weixin4j-qy**:增加批量获取用户详情的接口
+ **weixin4j-qy**: 增加批量获取用户详情的接口
+ **weixin4j-qy**: 新增获取微信服务器IP接口
+ **weixin4j-qy**: 调整回调模式下的首次验证的签名方式

View File

@ -61,3 +61,5 @@ weixin.properties说明
* 2014-12-28
+ 增加`批量获取用户详情`的接口
+ 新增`获取微信服务器IP`接口

View File

@ -6,6 +6,7 @@ import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.JsonResult;
import com.foxinmy.weixin4j.model.WeixinQyAccount;
import com.foxinmy.weixin4j.qy.api.DepartApi;
import com.foxinmy.weixin4j.qy.api.HelperApi;
import com.foxinmy.weixin4j.qy.api.TagApi;
import com.foxinmy.weixin4j.qy.api.UserApi;
import com.foxinmy.weixin4j.qy.model.Department;
@ -29,6 +30,7 @@ public class WeixinProxy {
private final DepartApi departApi;
private final UserApi userApi;
private final TagApi tagApi;
private final HelperApi helperApi;
/**
* 默认采用文件存放Token信息
@ -65,6 +67,7 @@ public class WeixinProxy {
this.departApi = new DepartApi(tokenHolder);
this.userApi = new UserApi(tokenHolder);
this.tagApi = new TagApi(tokenHolder);
this.helperApi = new HelperApi(tokenHolder);
}
/**
@ -342,4 +345,17 @@ public class WeixinProxy {
throws WeixinException {
return tagApi.deleteTagUsers(tagId, userIds);
}
/**
* 获取微信服务器IP地址
*
* @return IP地址
* @see com.foxinmy.weixin4j.qy.api.HelperApi
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E5%9B%9E%E8%B0%83%E6%A8%A1%E5%BC%8F#.E8.8E.B7.E5.8F.96.E5.BE.AE.E4.BF.A1.E6.9C.8D.E5.8A.A1.E5.99.A8.E7.9A.84ip.E6.AE.B5">获取IP地址</a>
* @throws WeixinException
*/
public List<String> getcallbackip() throws WeixinException {
return helperApi.getcallbackip();
}
}

View File

@ -0,0 +1,43 @@
package com.foxinmy.weixin4j.qy.api;
import java.util.List;
import com.alibaba.fastjson.JSON;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.Response;
import com.foxinmy.weixin4j.model.Token;
import com.foxinmy.weixin4j.token.TokenHolder;
/**
* 辅助API
*
* @className HelperApi
* @author jy
* @date 2014年12月28日
* @since JDK 1.7
* @see
*/
public class HelperApi extends QyApi {
private final TokenHolder tokenHolder;
public HelperApi(TokenHolder tokenHolder) {
this.tokenHolder = tokenHolder;
}
/**
* 获取微信服务器IP地址
*
* @return IP地址
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E5%9B%9E%E8%B0%83%E6%A8%A1%E5%BC%8F#.E8.8E.B7.E5.8F.96.E5.BE.AE.E4.BF.A1.E6.9C.8D.E5.8A.A1.E5.99.A8.E7.9A.84ip.E6.AE.B5">获取IP地址</a>
* @throws WeixinException
*/
public List<String> getcallbackip() throws WeixinException {
String getcallbackip_uri = getRequestUri("getcallbackip_uri");
Token token = tokenHolder.getToken();
Response response = request.post(String.format(getcallbackip_uri,
token.getAccessToken()));
return JSON.parseArray(response.getAsJson().getString("ip_list"),
String.class);
}
}

View File

@ -55,3 +55,5 @@ menu_delete_uri={api_base_url}/menu/delete?access_token=%s&agentid=%d
menu_get_uri={api_base_url}/menu/get?access_token=%s&agentid=%d
# \u53d1\u9001\u6d88\u606f
message_send_uri={api_base_url}/message/send?access_token=%s
# \u83b7\u53d6\u5fae\u4fe1IP\u5730\u5740
getcallbackip_uri={api_base_url}/getcallbackip?access_token=%s

View File

@ -0,0 +1,35 @@
package com.foxinmy.weixin4j.qy.test;
import java.util.List;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.qy.api.HelperApi;
/**
* 辅助API测试
*
* @className HelperTest
* @author jy
* @date 2014年12月28日
* @since JDK 1.7
* @see
*/
public class HelperTest extends TokenTest {
public HelperApi helperApi;
@Before
public void init() {
this.helperApi = new HelperApi(tokenHolder);
}
@Test
public void backip() throws WeixinException {
List<String> ips = helperApi.getcallbackip();
Assert.assertTrue(ips != null && !ips.isEmpty());
System.out.println(ips);
}
}

View File

@ -56,3 +56,5 @@ weixin4j-qy-server
* 2014-12-28
+ 增加用户进入应用的callback事件
+ 调整回调模式下的首次验证的签名方式

View File

@ -3,6 +3,7 @@ package com.foxinmy.weixin4j.qy.server;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.QueryStringDecoder;
import java.util.List;
@ -38,13 +39,14 @@ public class WeixinMessageDecoder extends
protected void decode(ChannelHandlerContext ctx, FullHttpRequest req,
List<Object> out) throws Exception {
WeixinQyAccount qyAccount = ConfigUtil.getWeixinQyAccount();
String xmlContent = req.content().toString(Consts.UTF_8);
String content = req.content().toString(Consts.UTF_8);
HttpWeixinMessage message = new HttpWeixinMessage();
message.setXmlContent(xmlContent);
if (StringUtils.isNotBlank(xmlContent)) {
message = XmlStream.get(xmlContent, HttpWeixinMessage.class);
message.setXmlContent(MessageUtil.aesDecrypt(qyAccount.getId(),
qyAccount.getEncodingAesKey(), message.getEncryptContent()));
message.setOriginalContent(content);
if (StringUtils.isNotBlank(content)) {
message = XmlStream.get(content, HttpWeixinMessage.class);
message.setOriginalContent(MessageUtil.aesDecrypt(
qyAccount.getId(), qyAccount.getEncodingAesKey(),
message.getEncryptContent()));
}
message.setMethod(req.getMethod().name());
QueryStringDecoder queryDecoder = new QueryStringDecoder(req.getUri(),
@ -52,11 +54,11 @@ public class WeixinMessageDecoder extends
log.info("\n=================receive request=================");
log.info("{}", req.getMethod());
log.info("{}", req.getUri());
log.info("{}", xmlContent);
log.info("{}", content);
Map<String, List<String>> parameters = queryDecoder.parameters();
String msgSignature = parameters.containsKey("msg_signature") ? parameters
.get("msg_signature").get(0) : "";
message.setMsgSignature(msgSignature);
message.setSignature(msgSignature);
String echoStr = parameters.containsKey("echostr") ? parameters.get(
"echostr").get(0) : "";
message.setEchoStr(echoStr);
@ -66,12 +68,16 @@ public class WeixinMessageDecoder extends
String nonce = parameters.containsKey("nonce") ? parameters
.get("nonce").get(0) : "";
message.setNonce(nonce);
String signature = parameters.containsKey("signature") ? parameters
.get("signature").get(0) : "";
message.setSignature(signature);
message.setToken(qyAccount.getToken());
message.setEncryptType(EncryptType.AES);
// 解密 echostr 20141228 added
if (message.getMethod().equals(HttpMethod.GET.name())
&& StringUtils.isNotBlank(echoStr)) {
message.setOriginalContent(MessageUtil.aesDecrypt(
qyAccount.getId(), qyAccount.getEncodingAesKey(), echoStr));
}
out.add(message);
}
}

View File

@ -46,6 +46,7 @@ public class WeixinServerHandler extends
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
log.error("catch the exception:{}", cause);
}
@Override
@ -57,18 +58,20 @@ public class WeixinServerHandler extends
boolean validate = false;
if (isGet) {
validate = MessageUtil.signature(httpMessage.getToken(),
httpMessage.getTimeStamp(), httpMessage.getNonce()).equals(
httpMessage.getSignature());
httpMessage.getTimeStamp(), httpMessage.getNonce(),
httpMessage.getEchoStr())
.equals(httpMessage.getSignature());
if (validate) {
ctx.write(HttpUtil.createWeixinMessageResponse(
httpMessage.getEchoStr(), ContentType.TEXT_PLAIN));
httpMessage.getOriginalContent(),
ContentType.TEXT_PLAIN));
return;
}
} else {
validate = MessageUtil.signature(httpMessage.getToken(),
httpMessage.getTimeStamp(), httpMessage.getNonce(),
httpMessage.getEncryptContent()).equals(
httpMessage.getMsgSignature());
httpMessage.getSignature());
}
if (!validate) {
ctx.write(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
@ -76,7 +79,7 @@ public class WeixinServerHandler extends
return;
}
String xmlContent = httpMessage.getXmlContent();
String xmlContent = httpMessage.getOriginalContent();
WeixinAction action = actionMapping.getAction(xmlContent);
if (action == null) {
ctx.write(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,