新增获取微信服务器IP地址接口以及消息AES加密解密实现
This commit is contained in:
@@ -28,6 +28,7 @@ weixin4j-mp-server
|
||||
|
||||
> account={"appId":"appId","appSecret":"appSecret",
|
||||
> "token":"开放者的token 非必须","openId":"公众号的openid 非必须",
|
||||
> "encodingAesKey":"公众号设置了加密方式且为「安全模式」需要填入",
|
||||
> "mchId":"V3.x版本下的微信商户号",
|
||||
> "partnerId":"财付通的商户号","partnerKey":"财付通商户权限密钥Key",
|
||||
> "version":"针对微信支付的版本号(目前可能为2,3),如果不填则按照mchId非空与否来做判断",
|
||||
@@ -48,4 +49,10 @@ weixin4j-mp-server
|
||||
-------
|
||||
* 2014-11-03
|
||||
|
||||
+ 得到`weixin-mp-server`工程
|
||||
+ 得到`weixin-mp-server`工程
|
||||
|
||||
* 2014-11-15
|
||||
|
||||
+ 解决`server工程`打包不能运行问题(`ClassUtil`无法获取jar包里面的类)
|
||||
|
||||
+ 新增被动消息的`加密`以及回复消息的`解密`
|
||||
@@ -13,7 +13,7 @@
|
||||
<url>https://github.com/foxinmy/weixin4j/tree/master/weixin4j-mp/weixin4j-server</url>
|
||||
<description>微信公众号服务</description>
|
||||
<build>
|
||||
<finalName>weixin4j-mp-server</finalName>
|
||||
<finalName>weixin-mp-server</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
<formats>
|
||||
<format>zip</format>
|
||||
</formats>
|
||||
<includeBaseDirectory>true</includeBaseDirectory>
|
||||
<dependencySets>
|
||||
<dependencySet>
|
||||
<useProjectArtifact>true</useProjectArtifact>
|
||||
|
||||
+5
-6
@@ -17,28 +17,27 @@ import com.foxinmy.weixin4j.xml.XStream;
|
||||
* @author jy
|
||||
* @date 2014年10月12日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
* @see com.foxinmy.weixin4j.mp.action.WeixinAction
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public abstract class AbstractAction<M extends BaseMessage> implements
|
||||
WeixinAction {
|
||||
|
||||
public abstract BaseResponse execute(M inMessage);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public String execute(String msg) throws DocumentException {
|
||||
public BaseResponse execute(String msg) throws DocumentException {
|
||||
BaseMessage message = MessageUtil.xml2msg(msg);
|
||||
if (message == null) {
|
||||
Class<M> messageClass = getGenericType();
|
||||
XStream xstream = XStream.get();
|
||||
xstream.processAnnotations(messageClass);
|
||||
xstream.alias("xml", messageClass);
|
||||
return execute(xstream.fromXML(msg, messageClass)).toXml();
|
||||
return execute(xstream.fromXML(msg, messageClass));
|
||||
}
|
||||
return execute((M) message).toXml();
|
||||
return execute((M) message);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Class<M> getGenericType() {
|
||||
Class<M> clazz = null;
|
||||
Type type = getClass().getGenericSuperclass();
|
||||
|
||||
+5
-5
@@ -1,21 +1,21 @@
|
||||
package com.foxinmy.weixin4j.mp.action;
|
||||
|
||||
import com.foxinmy.weixin4j.mp.response.TextResponse;
|
||||
import com.foxinmy.weixin4j.mp.response.BaseResponse;
|
||||
import com.foxinmy.weixin4j.msg.BaseMessage;
|
||||
|
||||
/**
|
||||
* 输出空白消息
|
||||
* 回复一个空字符串 而不是一个XML结构体中content字段的内容为空
|
||||
*
|
||||
* @className BlankAction
|
||||
* @author jy.hu
|
||||
* @date 2014年10月2日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
* @see com.foxinmy.weixin4j.mp.action.AbstractAction
|
||||
*/
|
||||
public class BlankAction<M extends BaseMessage> extends AbstractAction<M> {
|
||||
|
||||
@Override
|
||||
public TextResponse execute(M inMessage) {
|
||||
return new TextResponse("", inMessage);
|
||||
public BaseResponse execute(M inMessage) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
package com.foxinmy.weixin4j.mp.action;
|
||||
|
||||
import io.netty.handler.codec.http.QueryStringDecoder;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.dom4j.DocumentException;
|
||||
|
||||
import com.foxinmy.weixin4j.mp.mapping.Action;
|
||||
import com.foxinmy.weixin4j.type.MessageType;
|
||||
import com.foxinmy.weixin4j.util.ConfigUtil;
|
||||
import com.foxinmy.weixin4j.util.MessageUtil;
|
||||
|
||||
/**
|
||||
* 用于校验消息是否来自微信
|
||||
*
|
||||
* @className SignatureAction
|
||||
* @author jy
|
||||
* @date 2014年10月24日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
@Action(msgType = MessageType.signature)
|
||||
public class SignatureAction implements WeixinAction {
|
||||
|
||||
@Override
|
||||
public String execute(String uri) throws DocumentException {
|
||||
String[] paths = uri.split("\\?");
|
||||
if (paths == null || paths.length < 2) {
|
||||
return "";
|
||||
}
|
||||
QueryStringDecoder queryDecoder = new QueryStringDecoder(paths[1],
|
||||
false);
|
||||
Map<String, List<String>> parameters = queryDecoder.parameters();
|
||||
String echostr = parameters.containsKey("echostr") ? parameters.get(
|
||||
"echostr").get(0) : null;
|
||||
String timestamp = parameters.containsKey("timestamp") ? parameters
|
||||
.get("timestamp").get(0) : null;
|
||||
String nonce = parameters.containsKey("nonce") ? parameters
|
||||
.get("nonce").get(0) : null;
|
||||
String signature = parameters.containsKey("signature") ? parameters
|
||||
.get("signature").get(0) : null;
|
||||
String token = ConfigUtil.getValue("app_token");
|
||||
return MessageUtil.signature(token, echostr, timestamp, nonce,
|
||||
signature);
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -2,6 +2,8 @@ package com.foxinmy.weixin4j.mp.action;
|
||||
|
||||
import org.dom4j.DocumentException;
|
||||
|
||||
import com.foxinmy.weixin4j.mp.response.BaseResponse;
|
||||
|
||||
/**
|
||||
* 消息处理接口
|
||||
*
|
||||
@@ -14,5 +16,5 @@ import org.dom4j.DocumentException;
|
||||
* @see com.foxinmy.weixin4j.mp.action.DebugAction
|
||||
*/
|
||||
public interface WeixinAction {
|
||||
public String execute(String msg) throws DocumentException;
|
||||
public BaseResponse execute(String msg) throws DocumentException;
|
||||
}
|
||||
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
package com.foxinmy.weixin4j.mp.model;
|
||||
|
||||
import io.netty.handler.codec.http.HttpMethod;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.foxinmy.weixin4j.mp.type.EncryptType;
|
||||
import com.thoughtworks.xstream.annotations.XStreamAlias;
|
||||
|
||||
@XStreamAlias("xml")
|
||||
public class HttpWeixinMessage implements Serializable {
|
||||
private static final long serialVersionUID = -9157395300510879866L;
|
||||
|
||||
// 以下字段是加密方式为「安全模式」时的参数
|
||||
@XStreamAlias("ToUserName")
|
||||
private String toUserName;
|
||||
@XStreamAlias("Encrypt")
|
||||
private String encryptContent;
|
||||
private EncryptType encryptType;
|
||||
private String msgSignature;
|
||||
|
||||
// 以下字段每次被动消息时都会带上
|
||||
private String echoStr;
|
||||
private String timeStamp;
|
||||
private String nonce;
|
||||
private String signature;
|
||||
|
||||
private String token;
|
||||
|
||||
// xml消息主体
|
||||
private String xmlContent;
|
||||
|
||||
// request method
|
||||
private HttpMethod method;
|
||||
|
||||
public String getToUserName() {
|
||||
return toUserName;
|
||||
}
|
||||
|
||||
public void setToUserName(String toUserName) {
|
||||
this.toUserName = toUserName;
|
||||
}
|
||||
|
||||
public String getEncryptContent() {
|
||||
return encryptContent;
|
||||
}
|
||||
|
||||
public void setEncryptContent(String encryptContent) {
|
||||
this.encryptContent = encryptContent;
|
||||
}
|
||||
|
||||
public EncryptType getEncryptType() {
|
||||
return encryptType;
|
||||
}
|
||||
|
||||
public void setEncryptType(EncryptType encryptType) {
|
||||
this.encryptType = encryptType;
|
||||
}
|
||||
|
||||
public String getMsgSignature() {
|
||||
return msgSignature;
|
||||
}
|
||||
|
||||
public void setMsgSignature(String msgSignature) {
|
||||
this.msgSignature = msgSignature;
|
||||
}
|
||||
|
||||
public String getEchoStr() {
|
||||
return echoStr;
|
||||
}
|
||||
|
||||
public void setEchoStr(String echoStr) {
|
||||
this.echoStr = echoStr;
|
||||
}
|
||||
|
||||
public String getTimeStamp() {
|
||||
return timeStamp;
|
||||
}
|
||||
|
||||
public void setTimeStamp(String timeStamp) {
|
||||
this.timeStamp = timeStamp;
|
||||
}
|
||||
|
||||
public String getNonce() {
|
||||
return nonce;
|
||||
}
|
||||
|
||||
public void setNonce(String nonce) {
|
||||
this.nonce = nonce;
|
||||
}
|
||||
|
||||
public String getSignature() {
|
||||
return signature;
|
||||
}
|
||||
|
||||
public void setSignature(String signature) {
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
public String getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
public void setToken(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
public String getXmlContent() {
|
||||
return xmlContent;
|
||||
}
|
||||
|
||||
public void setXmlContent(String xmlContent) {
|
||||
this.xmlContent = xmlContent;
|
||||
}
|
||||
|
||||
public HttpMethod getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
public void setMethod(HttpMethod method) {
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
@Override
|
||||
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 + "]";
|
||||
}
|
||||
}
|
||||
+5
-1
@@ -1 +1,5 @@
|
||||
微信服务netty启动类
|
||||
WeixinMessageDecoder:对微信消息进行解码
|
||||
|
||||
WeixinMessageEncoder:对微信消息进行编码
|
||||
|
||||
WeixinServerHandler:微信请求处理类
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
package com.foxinmy.weixin4j.mp.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.QueryStringDecoder;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.http.Consts;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.foxinmy.weixin4j.model.WeixinAccount;
|
||||
import com.foxinmy.weixin4j.mp.model.HttpWeixinMessage;
|
||||
import com.foxinmy.weixin4j.mp.type.EncryptType;
|
||||
import com.foxinmy.weixin4j.util.ConfigUtil;
|
||||
import com.foxinmy.weixin4j.util.MessageUtil;
|
||||
import com.foxinmy.weixin4j.xml.XStream;
|
||||
|
||||
/**
|
||||
* 微信消息解码类
|
||||
*
|
||||
* @className WeixinMessageDecoder
|
||||
* @author jy
|
||||
* @date 2014年11月13日
|
||||
* @since JDK 1.7
|
||||
* @see <a
|
||||
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E6%8E%A5%E5%85%A5%E6%8C%87%E5%BC%95">加密接入指引</a>
|
||||
*/
|
||||
public class WeixinMessageDecoder extends
|
||||
MessageToMessageDecoder<FullHttpRequest> {
|
||||
private final Logger log = LoggerFactory.getLogger(getClass());
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, FullHttpRequest req,
|
||||
List<Object> out) throws Exception {
|
||||
WeixinAccount account = ConfigUtil.getWeixinAccount();
|
||||
String xmlContent = req.content().toString(Consts.UTF_8);
|
||||
HttpWeixinMessage message = new HttpWeixinMessage();
|
||||
if (StringUtils.isNotBlank(xmlContent)) {
|
||||
message = XStream.get(xmlContent, HttpWeixinMessage.class);
|
||||
}
|
||||
message.setMethod(req.getMethod());
|
||||
QueryStringDecoder queryDecoder = new QueryStringDecoder(req.getUri(),
|
||||
true);
|
||||
log.info("\n=================receive request=================");
|
||||
log.info("{}", req.getMethod());
|
||||
log.info("{}", req.getUri());
|
||||
log.info("{}", xmlContent);
|
||||
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);
|
||||
String timeStamp = parameters.containsKey("timestamp") ? parameters
|
||||
.get("timestamp").get(0) : "";
|
||||
message.setTimeStamp(timeStamp);
|
||||
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.setXmlContent(xmlContent);
|
||||
if (message.getEncryptType() == EncryptType.AES) {
|
||||
message.setXmlContent(MessageUtil.aesDecrypt(account.getAppId(),
|
||||
account.getEncodingAesKey(), message.getEncryptContent()));
|
||||
}
|
||||
message.setToken(account.getToken());
|
||||
out.add(message);
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
package com.foxinmy.weixin4j.mp.server;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToMessageEncoder;
|
||||
import io.netty.handler.codec.http.HttpResponse;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.foxinmy.weixin4j.model.WeixinAccount;
|
||||
import com.foxinmy.weixin4j.mp.response.BaseResponse;
|
||||
import com.foxinmy.weixin4j.mp.util.HttpUtil;
|
||||
import com.foxinmy.weixin4j.util.ConfigUtil;
|
||||
import com.foxinmy.weixin4j.util.DateUtil;
|
||||
import com.foxinmy.weixin4j.util.MessageUtil;
|
||||
import com.foxinmy.weixin4j.util.RandomUtil;
|
||||
import com.foxinmy.weixin4j.xml.Map2ObjectConverter;
|
||||
import com.foxinmy.weixin4j.xml.XStream;
|
||||
import com.thoughtworks.xstream.core.ClassLoaderReference;
|
||||
import com.thoughtworks.xstream.mapper.DefaultMapper;
|
||||
|
||||
/**
|
||||
* 微信消息编码类
|
||||
*
|
||||
* @className WeixinMessageEncoder
|
||||
* @author jy
|
||||
* @date 2014年11月13日
|
||||
* @since JDK 1.7
|
||||
* @see <a
|
||||
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E6%8E%A5%E5%85%A5%E6%8C%87%E5%BC%95">加密接入指引</a>
|
||||
*/
|
||||
public class WeixinMessageEncoder extends MessageToMessageEncoder<BaseResponse> {
|
||||
private final Logger log = LoggerFactory.getLogger(getClass());
|
||||
protected final static XStream mapXstream = XStream.get();
|
||||
static {
|
||||
mapXstream.alias("xml", Map.class);
|
||||
mapXstream.registerConverter(new Map2ObjectConverter(new DefaultMapper(
|
||||
new ClassLoaderReference(XStream.class.getClassLoader()))));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void encode(ChannelHandlerContext ctx, BaseResponse response,
|
||||
List<Object> out) throws Exception {
|
||||
WeixinAccount account = ConfigUtil.getWeixinAccount();
|
||||
String xmlContent = response.toXml();
|
||||
String nonce = RandomUtil.generateString(32);
|
||||
String timestamp = DateUtil.timestamp2string();
|
||||
String encrtypt = MessageUtil.aesEncrypt(account.getAppId(),
|
||||
account.getEncodingAesKey(), xmlContent);
|
||||
String msgSignature = MessageUtil.signature(account.getToken(), nonce,
|
||||
timestamp, encrtypt);
|
||||
Map<String, String> map = new HashMap<String, String>();
|
||||
map.put("Encrypt", encrtypt);
|
||||
map.put("MsgSignature", msgSignature);
|
||||
map.put("TimeStamp", timestamp);
|
||||
map.put("Nonce", nonce);
|
||||
|
||||
String content = mapXstream.toXML(map);
|
||||
HttpResponse httpResponse = HttpUtil
|
||||
.createWeixinMessageResponse(content);
|
||||
out.add(httpResponse);
|
||||
|
||||
log.info("\n=================aes encrtypt out=================");
|
||||
log.info("{}", map);
|
||||
log.info("{}", content);
|
||||
}
|
||||
}
|
||||
+58
-53
@@ -1,33 +1,26 @@
|
||||
package com.foxinmy.weixin4j.mp.server;
|
||||
|
||||
import static io.netty.handler.codec.http.HttpHeaders.Names.CONNECTION;
|
||||
import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_LENGTH;
|
||||
import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE;
|
||||
import static io.netty.handler.codec.http.HttpResponseStatus.CONTINUE;
|
||||
import static io.netty.handler.codec.http.HttpResponseStatus.OK;
|
||||
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.FullHttpResponse;
|
||||
import io.netty.handler.codec.http.HttpHeaders;
|
||||
import io.netty.handler.codec.http.HttpHeaders.Values;
|
||||
import io.netty.handler.codec.http.HttpMethod;
|
||||
import io.netty.handler.codec.http.HttpResponse;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.handler.codec.http.HttpVersion;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.dom4j.DocumentException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.foxinmy.weixin4j.mp.action.WeixinAction;
|
||||
import com.foxinmy.weixin4j.mp.mapping.ActionMapping;
|
||||
import com.foxinmy.weixin4j.mp.model.HttpWeixinMessage;
|
||||
import com.foxinmy.weixin4j.mp.response.BaseResponse;
|
||||
import com.foxinmy.weixin4j.mp.type.EncryptType;
|
||||
import com.foxinmy.weixin4j.mp.util.HttpUtil;
|
||||
import com.foxinmy.weixin4j.util.MessageUtil;
|
||||
|
||||
public class WeixinServerHandler extends ChannelInboundHandlerAdapter {
|
||||
public class WeixinServerHandler extends
|
||||
SimpleChannelInboundHandler<HttpWeixinMessage> {
|
||||
|
||||
private final Logger log = LoggerFactory.getLogger(getClass());
|
||||
|
||||
@@ -37,49 +30,61 @@ public class WeixinServerHandler extends ChannelInboundHandlerAdapter {
|
||||
this.actionMapping = actionMapping;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelReadComplete(ChannelHandlerContext ctx) {
|
||||
ctx.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg)
|
||||
throws DocumentException {
|
||||
if (msg instanceof FullHttpRequest) {
|
||||
FullHttpRequest req = (FullHttpRequest) msg;
|
||||
if (HttpHeaders.is100ContinueExpected(req)) {
|
||||
ctx.write(new DefaultFullHttpResponse(HTTP_1_1, CONTINUE));
|
||||
return;
|
||||
}
|
||||
String xmlMsg = req.content().toString(StandardCharsets.UTF_8);
|
||||
log.info("\n=================message in=================\n{}",
|
||||
xmlMsg);
|
||||
WeixinAction action = actionMapping.getAction(xmlMsg);
|
||||
if (action == null) {
|
||||
ctx.write(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
|
||||
HttpResponseStatus.NOT_FOUND));
|
||||
return;
|
||||
}
|
||||
String content = action.execute(xmlMsg);
|
||||
log.info("\n=================message out=================\n{}",
|
||||
content);
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1,
|
||||
OK, Unpooled.copiedBuffer(content, StandardCharsets.UTF_8));
|
||||
response.headers().set(CONTENT_TYPE, "text/plain;charset=utf-8");
|
||||
response.headers().set(CONTENT_LENGTH,
|
||||
response.content().readableBytes());
|
||||
if (!HttpHeaders.isKeepAlive(req)) {
|
||||
ctx.write(response).addListener(ChannelFutureListener.CLOSE);
|
||||
} else {
|
||||
response.headers().set(CONNECTION, Values.KEEP_ALIVE);
|
||||
ctx.write(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
cause.printStackTrace();
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx,
|
||||
HttpWeixinMessage httpMessage) throws Exception {
|
||||
log.info("\n=================message in=================\n{}",
|
||||
httpMessage);
|
||||
boolean validate = false;
|
||||
if (httpMessage.getMethod() == HttpMethod.GET
|
||||
|| httpMessage.getEncryptType() == EncryptType.RAW) {
|
||||
validate = MessageUtil.signature(httpMessage.getToken(),
|
||||
httpMessage.getTimeStamp(), httpMessage.getNonce()).equals(
|
||||
httpMessage.getSignature());
|
||||
if (httpMessage.getMethod() == HttpMethod.GET && validate) {
|
||||
HttpResponse httpResponse = HttpUtil
|
||||
.createWeixinMessageResponse(httpMessage.getEchoStr());
|
||||
ctx.write(httpResponse);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
validate = MessageUtil.signature(httpMessage.getToken(),
|
||||
httpMessage.getTimeStamp(), httpMessage.getNonce(),
|
||||
httpMessage.getEncryptContent()).equals(
|
||||
httpMessage.getMsgSignature());
|
||||
}
|
||||
if (!validate) {
|
||||
ctx.write(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
|
||||
HttpResponseStatus.FORBIDDEN));
|
||||
return;
|
||||
}
|
||||
|
||||
String xmlContent = httpMessage.getXmlContent();
|
||||
WeixinAction action = actionMapping.getAction(xmlContent);
|
||||
if (action == null) {
|
||||
ctx.write(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
|
||||
HttpResponseStatus.NOT_FOUND));
|
||||
return;
|
||||
}
|
||||
BaseResponse response = action.execute(xmlContent);
|
||||
log.info("\n=================message out=================\n{}",
|
||||
response);
|
||||
if (httpMessage.getEncryptType() == EncryptType.RAW) {
|
||||
HttpResponse httpResponse = HttpUtil
|
||||
.createWeixinMessageResponse(response.toXml());
|
||||
ctx.write(httpResponse);
|
||||
} else {
|
||||
ctx.write(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -22,6 +22,8 @@ public class WeixinServerInitializer extends ChannelInitializer<SocketChannel> {
|
||||
ChannelPipeline pipeline = channel.pipeline();
|
||||
pipeline.addLast(new HttpServerCodec());
|
||||
pipeline.addLast(new HttpObjectAggregator(65536));
|
||||
pipeline.addLast(new WeixinMessageDecoder());
|
||||
pipeline.addLast(new WeixinMessageEncoder());
|
||||
pipeline.addLast(new WeixinServerHandler(actionMapping));
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
package com.foxinmy.weixin4j.mp.statrup;
|
||||
package com.foxinmy.weixin4j.mp.startup;
|
||||
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
@@ -21,7 +21,7 @@ import com.foxinmy.weixin4j.mp.server.WeixinServerInitializer;
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
public final class WeixinServiceBootstrap {
|
||||
public final class WeixinServerBootstrap {
|
||||
|
||||
private final static int port;
|
||||
private final static int workerThreads;
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
package com.foxinmy.weixin4j.mp.util;
|
||||
|
||||
import static io.netty.handler.codec.http.HttpHeaders.Names.CONNECTION;
|
||||
import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_LENGTH;
|
||||
import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE;
|
||||
import static io.netty.handler.codec.http.HttpHeaders.Names.DATE;
|
||||
import static io.netty.handler.codec.http.HttpHeaders.Names.SERVER;
|
||||
import static io.netty.handler.codec.http.HttpResponseStatus.OK;
|
||||
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
||||
import io.netty.handler.codec.http.FullHttpResponse;
|
||||
import io.netty.handler.codec.http.HttpHeaders.Values;
|
||||
import io.netty.handler.codec.http.HttpResponse;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import org.apache.http.Consts;
|
||||
|
||||
/**
|
||||
* HTTP工具类
|
||||
*
|
||||
* @className HttpUtil
|
||||
* @author jy
|
||||
* @date 2014年11月15日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
public class HttpUtil {
|
||||
|
||||
public static HttpResponse createWeixinMessageResponse(String content) {
|
||||
FullHttpResponse httpResponse = new DefaultFullHttpResponse(HTTP_1_1,
|
||||
OK, Unpooled.copiedBuffer(content, Consts.UTF_8));
|
||||
httpResponse.headers().set(CONTENT_TYPE,
|
||||
"application/xml;encoding=utf-8");
|
||||
httpResponse.headers().set(CONTENT_LENGTH, content.getBytes().length);
|
||||
httpResponse.headers().set(CONNECTION, Values.KEEP_ALIVE);
|
||||
httpResponse.headers().set(DATE, new Date());
|
||||
httpResponse.headers().set(SERVER, "netty4");
|
||||
return httpResponse;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
# \u516c\u4f17\u53f7\u4fe1\u606f
|
||||
account={"appId":"wx4ab8f8de58159a57","appSecret":"1d4eb0f4bf556aaed539f30ed05ca795",\
|
||||
"token":"\u5f00\u653e\u8005\u7684token \u975e\u5fc5\u987b","openId":"\u516c\u4f17\u53f7\u7684openid \u975e\u5fc5\u987b",\
|
||||
"encodingAesKey":"\u516c\u4f17\u53f7\u8bbe\u7f6e\u4e86\u52a0\u5bc6\u65b9\u5f0f\u4e14\u4e3a\u300c\u5b89\u5168\u6a21\u5f0f\u300d\u9700\u8981\u586b\u5165",\
|
||||
"mchId":"V3.x\u7248\u672c\u4e0b\u7684\u5fae\u4fe1\u5546\u6237\u53f7",\
|
||||
"version":3,\
|
||||
"partnerId":"\u8d22\u4ed8\u901a\u7684\u5546\u6237\u53f7","partnerKey":"\u8d22\u4ed8\u901a\u5546\u6237\u6743\u9650\u5bc6\u94a5Key",\
|
||||
@@ -15,4 +16,4 @@ media_path=/tmp/weixin/media
|
||||
# \u5bf9\u8d26\u5355\u4fdd\u5b58\u8def\u5f84
|
||||
bill_path=/tmp/weixin/bill
|
||||
# ca\u8bc1\u4e66\u5b58\u653e\u7684\u5b8c\u6574\u8def\u5f84
|
||||
ca_file=/tmp/weixin/xxxxx.p12
|
||||
ca_file=/tmp/weixin/xxxxxx.p12
|
||||
@@ -6,10 +6,10 @@ JAVA_HOME="/usr/local/java/"
|
||||
RUNNING_USER=root
|
||||
|
||||
#Run home
|
||||
APP_HOME="/usr/local/weixin/weixin-service"
|
||||
APP_HOME="/usr/local/weixin/weixin-mp-server"
|
||||
|
||||
#main class
|
||||
APP_MAINCLASS=com.foxinmy.weixin4j.mp.startup.WeixinServiceBootstrap
|
||||
APP_MAINCLASS=com.foxinmy.weixin4j.mp.startup.WeixinServerBootstrap
|
||||
|
||||
#classpath
|
||||
CLASSPATH=$APP_HOME/classes
|
||||
@@ -42,9 +42,9 @@ start() {
|
||||
checkpid
|
||||
|
||||
if [ $psid -ne 0 ]; then
|
||||
echo "================================"
|
||||
echo "====================================================="
|
||||
echo "warn: $APP_MAINCLASS already started! (pid=$psid)"
|
||||
echo "================================"
|
||||
echo "====================================================="
|
||||
else
|
||||
echo -n "Starting $APP_MAINCLASS ..."
|
||||
# JAVA_CMD="nohup $JAVA_HOME/bin/java $JAVA_OPTS -classpath $CLASSPATH $APP_MAINCLASS >/dev/null 2>&1 &"
|
||||
@@ -79,9 +79,9 @@ stop() {
|
||||
stop
|
||||
fi
|
||||
else
|
||||
echo "================================"
|
||||
echo "====================================================="
|
||||
echo "warn: $APP_MAINCLASS is not running"
|
||||
echo "================================"
|
||||
echo "====================================================="
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user