企业号:新增获取微信服务器IP地址接口&调整回调模式下的首次验证的签名方式
This commit is contained in:
parent
167f696e83
commit
a3cec6a17b
@ -200,9 +200,13 @@ netty的代码没有放到maven中心仓库,也没什么意义,因为最终需
|
||||
|
||||
* 2014-12-28
|
||||
|
||||
+ **weixin4j-qy**:增加用户进入应用的callback事件
|
||||
+ **weixin4j-qy**: 增加用户进入应用的callback事件
|
||||
|
||||
+ **weixin4j-qy**:增加批量获取用户详情的接口
|
||||
+ **weixin4j-qy**: 增加批量获取用户详情的接口
|
||||
|
||||
+ **weixin4j-qy**: 新增获取微信服务器IP接口
|
||||
|
||||
+ **weixin4j-qy**: 调整回调模式下的首次验证的签名方式
|
||||
|
||||
接下来
|
||||
------
|
||||
|
||||
@ -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 + "]";
|
||||
}
|
||||
}
|
||||
|
||||
@ -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())
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -91,6 +91,10 @@ weixin4j-qy
|
||||
|
||||
* 2014-12-28
|
||||
|
||||
+ **weixin4j-qy**:增加用户进入应用的callback事件
|
||||
+ **weixin4j-qy**: 增加用户进入应用的callback事件
|
||||
|
||||
+ **weixin4j-qy**:增加批量获取用户详情的接口
|
||||
+ **weixin4j-qy**: 增加批量获取用户详情的接口
|
||||
|
||||
+ **weixin4j-qy**: 新增获取微信服务器IP接口
|
||||
|
||||
+ **weixin4j-qy**: 调整回调模式下的首次验证的签名方式
|
||||
|
||||
@ -61,3 +61,5 @@ weixin.properties说明
|
||||
* 2014-12-28
|
||||
|
||||
+ 增加`批量获取用户详情`的接口
|
||||
|
||||
+ 新增`获取微信服务器IP`接口
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -54,4 +54,6 @@ menu_delete_uri={api_base_url}/menu/delete?access_token=%s&agentid=%d
|
||||
# \u67e5\u8be2\u83dc\u5355
|
||||
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
|
||||
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
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -55,4 +55,6 @@ weixin4j-qy-server
|
||||
|
||||
* 2014-12-28
|
||||
|
||||
+ 增加用户进入应用的callback事件
|
||||
+ 增加用户进入应用的callback事件
|
||||
|
||||
+ 调整回调模式下的首次验证的签名方式
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user