removed WeixinException class

This commit is contained in:
jinyu 2017-06-23 18:26:25 +08:00
parent 4c0f96fcfe
commit 3c92d75276
27 changed files with 1210 additions and 1457 deletions

View File

@ -66,16 +66,12 @@ public class Weixin4jServerStartupWithThread implements ApplicationContextAware
new Thread(new Runnable() { new Thread(new Runnable() {
@Override @Override
public void run() { public void run() {
try { bootstrap = new WeixinServerBootstrap(aesToken) // 指定开发者token信息
bootstrap = new WeixinServerBootstrap(aesToken) // 指定开发者token信息 .handlerPackagesToScan(handlerPackage) // 扫描处理消息的包
.handlerPackagesToScan(handlerPackage) // 扫描处理消息的包 .resolveBeanFactory(new SpringBeanFactory(applicationContext)) // 声明处理消息类由Spring容器去实例化
.resolveBeanFactory(new SpringBeanFactory(applicationContext)) // 声明处理消息类由Spring容器去实例化 .addHandler(DebugMessageHandler.global) // 当没有匹配到消息处理时输出调试信息开发环境打开
.addHandler(DebugMessageHandler.global) // 当没有匹配到消息处理时输出调试信息开发环境打开 .openAlwaysResponse(); // 当没有匹配到消息处理时输出空白回复(公众号不会出现该公众号无法提供服务的提示)正式环境打开
.openAlwaysResponse(); // 当没有匹配到消息处理时输出空白回复(公众号不会出现该公众号无法提供服务的提示)正式环境打开 bootstrap.startup(port); // 绑定服务的端口号即对外暴露(微信服务器URL地址)的服务端口
bootstrap.startup(port); // 绑定服务的端口号即对外暴露(微信服务器URL地址)的服务端口
} catch (WeixinException e) {
InternalLoggerFactory.getInstance(getClass()).error("weixin4j server startup:FAIL", e);
}
} }
}).start(); }).start();
} }

View File

@ -2,7 +2,6 @@ package com.foxinmy.weixin4j.example.server.handler;
import java.util.Set; import java.util.Set;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.handler.WeixinMessageHandler; import com.foxinmy.weixin4j.handler.WeixinMessageHandler;
import com.foxinmy.weixin4j.qy.chat.WeixinChatMessage; import com.foxinmy.weixin4j.qy.chat.WeixinChatMessage;
import com.foxinmy.weixin4j.request.WeixinMessage; import com.foxinmy.weixin4j.request.WeixinMessage;
@ -12,22 +11,19 @@ import com.foxinmy.weixin4j.response.WeixinResponse;
public class ChatMessageHandler implements WeixinMessageHandler { public class ChatMessageHandler implements WeixinMessageHandler {
@Override @Override
public boolean canHandle(WeixinRequest request, WeixinMessage message, public boolean canHandle(WeixinRequest request, WeixinMessage message, Set<String> nodeNames) {
Set<String> nodeNames) throws WeixinException { return nodeNames.contains("PackageId");
return nodeNames.contains("PackageId"); }
}
@Override @Override
public WeixinResponse doHandle(WeixinRequest request, public WeixinResponse doHandle(WeixinRequest request, WeixinMessage message, Set<String> nodeNames) {
WeixinMessage message, Set<String> nodeNames) WeixinChatMessage chatMessage = null; // 转换为实体
throws WeixinException { return BlankResponse.global;
WeixinChatMessage chatMessage = null; // 转换为实体 }
return BlankResponse.global;
}
@Override @Override
public int weight() { public int weight() {
return 0; return 0;
} }
} }

View File

@ -10,7 +10,8 @@ import com.foxinmy.weixin4j.response.TextResponse;
import com.foxinmy.weixin4j.response.WeixinResponse; import com.foxinmy.weixin4j.response.WeixinResponse;
/** /**
* 自定义处理消息 * 自定义处理消息
*
* @className CustomMessageHandler * @className CustomMessageHandler
* @author jinyu(foxinmy@gmail.com) * @author jinyu(foxinmy@gmail.com)
* @date 2017年1月19日 * @date 2017年1月19日
@ -19,22 +20,19 @@ import com.foxinmy.weixin4j.response.WeixinResponse;
*/ */
public class CustomMessageHandler implements WeixinMessageHandler { public class CustomMessageHandler implements WeixinMessageHandler {
@Override @Override
public boolean canHandle(WeixinRequest request, WeixinMessage message, public boolean canHandle(WeixinRequest request, WeixinMessage message, Set<String> nodeNames) {
Set<String> nodeNames) throws WeixinException { // 消息来源某个用户
// 消息来源某个用户 return message.getFromUserName().equals("xxx");
return message.getFromUserName().equals("xxx"); }
}
@Override @Override
public WeixinResponse doHandle(WeixinRequest request, public WeixinResponse doHandle(WeixinRequest request, WeixinMessage message, Set<String> nodeNames) {
WeixinMessage message, Set<String> nodeNames) return new TextResponse("是你,是你,还是你。");
throws WeixinException { }
return new TextResponse("是你,是你,还是你。");
}
@Override @Override
public int weight() { public int weight() {
return 0; return 0;
} }
} }

View File

@ -2,7 +2,6 @@ package com.foxinmy.weixin4j.example.server.handler;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.message.TextMessage; import com.foxinmy.weixin4j.message.TextMessage;
import com.foxinmy.weixin4j.request.WeixinRequest; import com.foxinmy.weixin4j.request.WeixinRequest;
import com.foxinmy.weixin4j.response.TextResponse; import com.foxinmy.weixin4j.response.TextResponse;
@ -10,7 +9,7 @@ import com.foxinmy.weixin4j.response.WeixinResponse;
/** /**
* 输入 hello 回复 world * 输入 hello 回复 world
* *
* @className HelloMessageHandler * @className HelloMessageHandler
* @author jinyu(foxinmy@gmail.com) * @author jinyu(foxinmy@gmail.com)
* @date 2015年12月27日 * @date 2015年12月27日
@ -19,30 +18,28 @@ import com.foxinmy.weixin4j.response.WeixinResponse;
@Component @Component
public class HelloMessageHandler extends TextMessageHandler { public class HelloMessageHandler extends TextMessageHandler {
@Override @Override
public boolean canHandle0(WeixinRequest request, TextMessage message) public boolean canHandle0(WeixinRequest request, TextMessage message) {
throws WeixinException { /**
/** * 用户输入hello时
* 用户输入hello时 */
*/ return message.getContent().equalsIgnoreCase("hello");
return message.getContent().equalsIgnoreCase("hello"); }
}
@Override @Override
public WeixinResponse doHandle0(WeixinRequest request, TextMessage message) public WeixinResponse doHandle0(WeixinRequest request, TextMessage message) {
throws WeixinException { /**
/** * 返回用户world文本
* 返回用户world文本 */
*/ return new TextResponse("world");
return new TextResponse("world"); }
}
/** /**
* 因为HelloMessageHandler和TextMessageHandler都会匹配到文本消息 * 因为HelloMessageHandler和TextMessageHandler都会匹配到文本消息
* 所以这里需要提高下权重(大于TextMessageHandler就行了) > TextMessageHandler * 所以这里需要提高下权重(大于TextMessageHandler就行了) > TextMessageHandler
*/ */
@Override @Override
public int weight() { public int weight() {
return super.weight() + 1; return super.weight() + 1;
} }
} }

View File

@ -2,7 +2,6 @@ package com.foxinmy.weixin4j.example.server.handler;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.handler.MessageHandlerAdapter; import com.foxinmy.weixin4j.handler.MessageHandlerAdapter;
import com.foxinmy.weixin4j.mp.event.ScribeEventMessage; import com.foxinmy.weixin4j.mp.event.ScribeEventMessage;
import com.foxinmy.weixin4j.request.WeixinRequest; import com.foxinmy.weixin4j.request.WeixinRequest;
@ -11,19 +10,17 @@ import com.foxinmy.weixin4j.response.WeixinResponse;
/** /**
* 处理关注消息 * 处理关注消息
* *
* @className SubscribeMessageHandler * @className SubscribeMessageHandler
* @author jinyu(foxinmy@gmail.com) * @author jinyu(foxinmy@gmail.com)
* @date 2015年12月3日 * @date 2015年12月3日
* @since JDK 1.6 * @since JDK 1.6
*/ */
@Component @Component
public class SubscribeMessageHandler extends public class SubscribeMessageHandler extends MessageHandlerAdapter<ScribeEventMessage> {
MessageHandlerAdapter<ScribeEventMessage> {
@Override @Override
public WeixinResponse doHandle0(WeixinRequest request, ScribeEventMessage message) public WeixinResponse doHandle0(WeixinRequest request, ScribeEventMessage message) {
throws WeixinException { return new TextResponse("欢迎关注~");
return new TextResponse("欢迎关注~"); }
}
} }

View File

@ -11,7 +11,7 @@ import com.foxinmy.weixin4j.response.WeixinResponse;
/** /**
* 文本消息处理 * 文本消息处理
* *
* @className TextMessageHandler * @className TextMessageHandler
* @author jinyu(foxinmy@gmail.com) * @author jinyu(foxinmy@gmail.com)
* @date 2015年11月18日 * @date 2015年11月18日
@ -19,9 +19,8 @@ import com.foxinmy.weixin4j.response.WeixinResponse;
*/ */
@Component @Component
public class TextMessageHandler extends MessageHandlerAdapter<TextMessage> { public class TextMessageHandler extends MessageHandlerAdapter<TextMessage> {
@Override @Override
public WeixinResponse doHandle0(WeixinRequest request, TextMessage message) public WeixinResponse doHandle0(WeixinRequest request, TextMessage message) {
throws WeixinException { return new TextResponse("收到了文本消息");
return new TextResponse("收到了文本消息"); }
}
} }

View File

@ -2,7 +2,6 @@ package com.foxinmy.weixin4j.example.server.handler;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.handler.MessageHandlerAdapter; import com.foxinmy.weixin4j.handler.MessageHandlerAdapter;
import com.foxinmy.weixin4j.message.VoiceMessage; import com.foxinmy.weixin4j.message.VoiceMessage;
import com.foxinmy.weixin4j.request.WeixinRequest; import com.foxinmy.weixin4j.request.WeixinRequest;
@ -11,7 +10,7 @@ import com.foxinmy.weixin4j.response.WeixinResponse;
/** /**
* 只处理语音消息 * 只处理语音消息
* *
* @className VoiceMessageHandler * @className VoiceMessageHandler
* @author jinyu(foxinmy@gmail.com) * @author jinyu(foxinmy@gmail.com)
* @date 2015年11月18日 * @date 2015年11月18日
@ -20,12 +19,11 @@ import com.foxinmy.weixin4j.response.WeixinResponse;
@Component @Component
public class VoiceMessageHandler extends MessageHandlerAdapter<VoiceMessage> { public class VoiceMessageHandler extends MessageHandlerAdapter<VoiceMessage> {
@Override @Override
public WeixinResponse doHandle0(WeixinRequest request, VoiceMessage message) public WeixinResponse doHandle0(WeixinRequest request, VoiceMessage message) {
throws WeixinException { /**
/** * 返回一段文字给用户
* 返回一段文字给用户 */
*/ return new TextResponse("你讲了一句话");
return new TextResponse("你讲了一句话"); }
}
} }

View File

@ -2,11 +2,9 @@ package com.foxinmy.weixin4j.dispatcher;
import java.util.Map; import java.util.Map;
import com.foxinmy.weixin4j.exception.WeixinException;
/** /**
* Bean构造 * Bean构造
* *
* @className BeanFactory * @className BeanFactory
* @author jinyu(foxinmy@gmail.com) * @author jinyu(foxinmy@gmail.com)
* @date 2015年5月7日 * @date 2015年5月7日
@ -14,11 +12,11 @@ import com.foxinmy.weixin4j.exception.WeixinException;
* @see * @see
*/ */
public interface BeanFactory { public interface BeanFactory {
Object getBean(String name) throws WeixinException; Object getBean(String name);
<T> T getBean(Class<T> clazz) throws WeixinException; <T> T getBean(Class<T> clazz);
<T> T getBean(String name, Class<T> clazz) throws WeixinException; <T> T getBean(String name, Class<T> clazz);
<T> Map<String, T> getBeans(Class<T> clazz) throws WeixinException; <T> Map<String, T> getBeans(Class<T> clazz);
} }

View File

@ -1,19 +1,18 @@
package com.foxinmy.weixin4j.dispatcher; package com.foxinmy.weixin4j.dispatcher;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.handler.WeixinMessageHandler; import com.foxinmy.weixin4j.handler.WeixinMessageHandler;
import com.foxinmy.weixin4j.interceptor.WeixinMessageInterceptor; import com.foxinmy.weixin4j.interceptor.WeixinMessageInterceptor;
import com.foxinmy.weixin4j.request.WeixinMessage; import com.foxinmy.weixin4j.request.WeixinMessage;
import com.foxinmy.weixin4j.request.WeixinRequest; import com.foxinmy.weixin4j.request.WeixinRequest;
import com.foxinmy.weixin4j.response.WeixinResponse; import com.foxinmy.weixin4j.response.WeixinResponse;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
/** /**
* 微信消息的处理执行 * 微信消息的处理执行
* *
* @className MessageHandlerExecutor * @className MessageHandlerExecutor
* @author jinyu(foxinmy@gmail.com) * @author jinyu(foxinmy@gmail.com)
* @date 2015年5月7日 * @date 2015年5月7日
@ -55,7 +54,7 @@ public class MessageHandlerExecutor {
/** /**
* 执行预拦截动作 * 执行预拦截动作
* *
* @param request * @param request
* 微信请求信息 * 微信请求信息
* @param message * @param message
@ -63,8 +62,7 @@ public class MessageHandlerExecutor {
* @return true则继续执行往下执行 * @return true则继续执行往下执行
* @throws WeixinException * @throws WeixinException
*/ */
public boolean applyPreHandle(WeixinRequest request, WeixinMessage message) public boolean applyPreHandle(WeixinRequest request, WeixinMessage message){
throws WeixinException {
if (messageInterceptors != null) { if (messageInterceptors != null) {
for (int i = 0; i < messageInterceptors.length; i++) { for (int i = 0; i < messageInterceptors.length; i++) {
WeixinMessageInterceptor interceptor = messageInterceptors[i]; WeixinMessageInterceptor interceptor = messageInterceptors[i];
@ -81,7 +79,7 @@ public class MessageHandlerExecutor {
/** /**
* MessageHandler处理玩请求后的动作 * MessageHandler处理玩请求后的动作
* *
* @param request * @param request
* 微信请求 * 微信请求
* @param response * @param response
@ -91,7 +89,7 @@ public class MessageHandlerExecutor {
* @throws WeixinException * @throws WeixinException
*/ */
public void applyPostHandle(WeixinRequest request, WeixinResponse response, public void applyPostHandle(WeixinRequest request, WeixinResponse response,
WeixinMessage message) throws WeixinException { WeixinMessage message){
if (messageInterceptors == null) { if (messageInterceptors == null) {
return; return;
} }
@ -104,7 +102,7 @@ public class MessageHandlerExecutor {
/** /**
* 全部执行完毕后触发 * 全部执行完毕后触发
* *
* @param request * @param request
* 微信请求 * 微信请求
* @param response * @param response
@ -117,7 +115,7 @@ public class MessageHandlerExecutor {
*/ */
public void triggerAfterCompletion(WeixinRequest request, public void triggerAfterCompletion(WeixinRequest request,
WeixinResponse response, WeixinMessage message, Exception exception) WeixinResponse response, WeixinMessage message, Exception exception)
throws WeixinException { {
if (messageInterceptors == null) { if (messageInterceptors == null) {
return; return;
} }
@ -126,7 +124,7 @@ public class MessageHandlerExecutor {
try { try {
interceptor.afterCompletion(context, request, response, interceptor.afterCompletion(context, request, response,
message, messageHandler, exception); message, messageHandler, exception);
} catch (WeixinException e) { } catch (Exception e) {
logger.error( logger.error(
"MessageInterceptor.afterCompletion threw exception", e); "MessageInterceptor.afterCompletion threw exception", e);
} }

View File

@ -1,12 +1,6 @@
package com.foxinmy.weixin4j.dispatcher; package com.foxinmy.weixin4j.dispatcher;
import static io.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND; import static io.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
@ -26,7 +20,6 @@ import javax.xml.bind.Unmarshaller;
import javax.xml.transform.Source; import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource; import javax.xml.transform.stream.StreamSource;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.handler.WeixinMessageHandler; import com.foxinmy.weixin4j.handler.WeixinMessageHandler;
import com.foxinmy.weixin4j.interceptor.WeixinMessageInterceptor; import com.foxinmy.weixin4j.interceptor.WeixinMessageInterceptor;
import com.foxinmy.weixin4j.request.WeixinMessage; import com.foxinmy.weixin4j.request.WeixinMessage;
@ -39,9 +32,16 @@ import com.foxinmy.weixin4j.util.HttpUtil;
import com.foxinmy.weixin4j.util.ServerToolkits; import com.foxinmy.weixin4j.util.ServerToolkits;
import com.foxinmy.weixin4j.xml.MessageTransferHandler; import com.foxinmy.weixin4j.xml.MessageTransferHandler;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
/** /**
* 微信消息分发器 * 微信消息分发器
* *
* @className WeixinMessageDispatcher * @className WeixinMessageDispatcher
* @author jinyu(foxinmy@gmail.com) * @author jinyu(foxinmy@gmail.com)
* @date 2015年5月7日 * @date 2015年5月7日
@ -54,438 +54,372 @@ import com.foxinmy.weixin4j.xml.MessageTransferHandler;
*/ */
public class WeixinMessageDispatcher { public class WeixinMessageDispatcher {
private final InternalLogger logger = InternalLoggerFactory private final InternalLogger logger = InternalLoggerFactory.getInstance(getClass());
.getInstance(getClass());
/** /**
* 消息处理器 * 消息处理器
*/ */
private List<WeixinMessageHandler> messageHandlerList; private List<WeixinMessageHandler> messageHandlerList;
private WeixinMessageHandler[] messageHandlers; private WeixinMessageHandler[] messageHandlers;
/** /**
* 消息处理器所在的包 * 消息处理器所在的包
*/ */
private String[] messageHandlerPackages; private String[] messageHandlerPackages;
/** /**
* 消息拦截器 * 消息拦截器
*/ */
private List<WeixinMessageInterceptor> messageInterceptorList; private List<WeixinMessageInterceptor> messageInterceptorList;
private WeixinMessageInterceptor[] messageInterceptors; private WeixinMessageInterceptor[] messageInterceptors;
/** /**
* 消息拦截器所在的包 * 消息拦截器所在的包
*/ */
private String[] messageInterceptorPackages; private String[] messageInterceptorPackages;
/** /**
* Bean构造 * Bean构造
*/ */
private BeanFactory beanFactory; private BeanFactory beanFactory;
/** /**
* 消息匹配 * 消息匹配
*/ */
private WeixinMessageMatcher messageMatcher; private WeixinMessageMatcher messageMatcher;
/** /**
* 消息转换 * 消息转换
*/ */
private Map<Class<? extends WeixinMessage>, Unmarshaller> messageUnmarshaller; private Map<Class<? extends WeixinMessage>, Unmarshaller> messageUnmarshaller;
/** /**
* 是否总是响应请求,如未匹配到MessageHandler时回复空白消息 * 是否总是响应请求,如未匹配到MessageHandler时回复空白消息
*/ */
private boolean alwaysResponse; private boolean alwaysResponse;
public WeixinMessageDispatcher() { public WeixinMessageDispatcher() {
this(new DefaultMessageMatcher()); this(new DefaultMessageMatcher());
} }
public WeixinMessageDispatcher(WeixinMessageMatcher messageMatcher) { public WeixinMessageDispatcher(WeixinMessageMatcher messageMatcher) {
this.messageMatcher = messageMatcher; this.messageMatcher = messageMatcher;
this.messageUnmarshaller = new ConcurrentHashMap<Class<? extends WeixinMessage>, Unmarshaller>(); this.messageUnmarshaller = new ConcurrentHashMap<Class<? extends WeixinMessage>, Unmarshaller>();
} }
/** /**
* 对消息进行一系列的处理,包括 拦截匹配分发等动作 * 对消息进行一系列的处理,包括 拦截匹配分发等动作
* *
* @param context * @param context
* 上下文环境 * 上下文环境
* @param request * @param request
* 微信请求 * 微信请求
* @param messageTransfer * @param messageTransfer
* 微信消息 * 微信消息 @
* @throws WeixinException */
*/ public void doDispatch(final ChannelHandlerContext context, final WeixinRequest request) {
public void doDispatch(final ChannelHandlerContext context, WeixinMessageTransfer messageTransfer = MessageTransferHandler.parser(request);
final WeixinRequest request) throws WeixinException { context.channel().attr(ServerToolkits.MESSAGE_TRANSFER_KEY).set(messageTransfer);
WeixinMessageTransfer messageTransfer = MessageTransferHandler WeixinMessageKey messageKey = defineMessageKey(messageTransfer, request);
.parser(request); Class<? extends WeixinMessage> targetClass = messageMatcher.match(messageKey);
context.channel().attr(ServerToolkits.MESSAGE_TRANSFER_KEY) WeixinMessage message = messageRead(request.getOriginalContent(), targetClass);
.set(messageTransfer); logger.info("define '{}' matched '{}'", messageKey, targetClass);
WeixinMessageKey messageKey = defineMessageKey(messageTransfer, request); MessageHandlerExecutor handlerExecutor = getHandlerExecutor(context, request, messageKey, message,
Class<? extends WeixinMessage> targetClass = messageMatcher messageTransfer.getNodeNames());
.match(messageKey); if (handlerExecutor == null || handlerExecutor.getMessageHandler() == null) {
WeixinMessage message = messageRead(request.getOriginalContent(), noHandlerFound(context, request, message);
targetClass); return;
logger.info("define '{}' matched '{}'", messageKey, targetClass); }
MessageHandlerExecutor handlerExecutor = getHandlerExecutor(context, if (!handlerExecutor.applyPreHandle(request, message)) {
request, messageKey, message, messageTransfer.getNodeNames()); return;
if (handlerExecutor == null }
|| handlerExecutor.getMessageHandler() == null) { Exception exception = null;
noHandlerFound(context, request, message); WeixinResponse response = null;
return; try {
} response = handlerExecutor.getMessageHandler().doHandle(request, message, messageTransfer.getNodeNames());
if (!handlerExecutor.applyPreHandle(request, message)) { handlerExecutor.applyPostHandle(request, response, message);
return; context.writeAndFlush(response);
} } catch (Exception e) {
Exception exception = null; exception = e;
WeixinResponse response = null; }
try { handlerExecutor.triggerAfterCompletion(request, response, message, exception);
response = handlerExecutor.getMessageHandler().doHandle(request, }
message, messageTransfer.getNodeNames());
handlerExecutor.applyPostHandle(request, response, message);
context.writeAndFlush(response);
} catch (Exception e) {
exception = e;
}
handlerExecutor.triggerAfterCompletion(request, response, message,
exception);
}
/** /**
* 声明messagekey * 声明messagekey
* *
* @param messageTransfer * @param messageTransfer
* 基础消息 * 基础消息
* @param request * @param request
* 请求信息 * 请求信息
* @return * @return
*/ */
protected WeixinMessageKey defineMessageKey( protected WeixinMessageKey defineMessageKey(WeixinMessageTransfer messageTransfer, WeixinRequest request) {
WeixinMessageTransfer messageTransfer, WeixinRequest request) { return new WeixinMessageKey(messageTransfer.getMsgType(), messageTransfer.getEventType(),
return new WeixinMessageKey(messageTransfer.getMsgType(), messageTransfer.getAccountType());
messageTransfer.getEventType(), }
messageTransfer.getAccountType());
}
/** /**
* 未匹配到handler时触发 * 未匹配到handler时触发
* *
* @param context * @param context
* 上下文环境 * 上下文环境
* @param request * @param request
* 微信请求 * 微信请求
* @param message * @param message
* 微信消息 * 微信消息
*/ */
protected void noHandlerFound(ChannelHandlerContext context, protected void noHandlerFound(ChannelHandlerContext context, WeixinRequest request, WeixinMessage message) {
WeixinRequest request, WeixinMessage message) { logger.warn("no handler found for {}", request);
logger.warn("no handler found for {}", request); if (alwaysResponse) {
if (alwaysResponse) { context.write(BlankResponse.global);
context.write(BlankResponse.global); } else {
} else { FullHttpResponse response = new DefaultFullHttpResponse(request.getProtocolVersion(), NOT_FOUND);
FullHttpResponse response = new DefaultFullHttpResponse( HttpUtil.resolveHeaders(response);
request.getProtocolVersion(), NOT_FOUND); context.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
HttpUtil.resolveHeaders(response); }
context.writeAndFlush(response).addListener( }
ChannelFutureListener.CLOSE);
}
}
/** /**
* MessageHandlerExecutor * MessageHandlerExecutor
* *
* @param context * @param context
* 上下文环境 * 上下文环境
* @param request * @param request
* 微信请求 * 微信请求
* @param messageKey * @param messageKey
* 消息的key * 消息的key
* @param message * @param message
* 微信消息 * 微信消息
* @param nodeNames * @param nodeNames
* 节点名称集合 * 节点名称集合
* @return MessageHandlerExecutor * @return MessageHandlerExecutor
* @see MessageHandlerExecutor * @see MessageHandlerExecutor @
* @throws WeixinException */
*/ protected MessageHandlerExecutor getHandlerExecutor(ChannelHandlerContext context, WeixinRequest request,
protected MessageHandlerExecutor getHandlerExecutor( WeixinMessageKey messageKey, WeixinMessage message, Set<String> nodeNames) {
ChannelHandlerContext context, WeixinRequest request, WeixinMessageHandler[] messageHandlers = getMessageHandlers();
WeixinMessageKey messageKey, WeixinMessage message, if (messageHandlers == null) {
Set<String> nodeNames) throws WeixinException { return null;
WeixinMessageHandler[] messageHandlers = getMessageHandlers(); }
if (messageHandlers == null) { logger.info("resolve message handlers '{}'", this.messageHandlerList);
return null; List<WeixinMessageHandler> matchedMessageHandlers = new ArrayList<WeixinMessageHandler>();
} for (WeixinMessageHandler handler : messageHandlers) {
logger.info("resolve message handlers '{}'", this.messageHandlerList); if (handler.canHandle(request, message, nodeNames)) {
List<WeixinMessageHandler> matchedMessageHandlers = new ArrayList<WeixinMessageHandler>(); matchedMessageHandlers.add(handler);
for (WeixinMessageHandler handler : messageHandlers) { }
if (handler.canHandle(request, message, nodeNames)) { }
matchedMessageHandlers.add(handler); if (matchedMessageHandlers.isEmpty()) {
} return null;
} }
if (matchedMessageHandlers.isEmpty()) { Collections.sort(matchedMessageHandlers, new Comparator<WeixinMessageHandler>() {
return null; @Override
} public int compare(WeixinMessageHandler m1, WeixinMessageHandler m2) {
Collections.sort(matchedMessageHandlers, return m2.weight() - m1.weight();
new Comparator<WeixinMessageHandler>() { }
@Override });
public int compare(WeixinMessageHandler m1, logger.info("matched message handlers '{}'", matchedMessageHandlers);
WeixinMessageHandler m2) { return new MessageHandlerExecutor(context, matchedMessageHandlers.get(0), getMessageInterceptors());
return m2.weight() - m1.weight(); }
}
});
logger.info("matched message handlers '{}'", matchedMessageHandlers);
return new MessageHandlerExecutor(context,
matchedMessageHandlers.get(0), getMessageInterceptors());
}
/** /**
* 获取所有的handler * 获取所有的handler
* *
* @return handler集合 * @return handler集合
* @see com.foxinmy.weixin4j.handler.WeixinMessageHandler * @see com.foxinmy.weixin4j.handler.WeixinMessageHandler @
* @throws WeixinException */
*/ public WeixinMessageHandler[] getMessageHandlers() {
public WeixinMessageHandler[] getMessageHandlers() throws WeixinException { if (this.messageHandlers == null) {
if (this.messageHandlers == null) { if (messageHandlerPackages != null) {
if (messageHandlerPackages != null) { List<Class<?>> messageHandlerClass = new ArrayList<Class<?>>();
List<Class<?>> messageHandlerClass = new ArrayList<Class<?>>(); for (String packageName : messageHandlerPackages) {
for (String packageName : messageHandlerPackages) { messageHandlerClass.addAll(ClassUtil.getClasses(packageName));
messageHandlerClass.addAll(ClassUtil }
.getClasses(packageName)); if (beanFactory != null) {
} for (Class<?> clazz : messageHandlerClass) {
if (beanFactory != null) { if (clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers())
for (Class<?> clazz : messageHandlerClass) { || !WeixinMessageHandler.class.isAssignableFrom(clazz)) {
if (clazz.isInterface() continue;
|| Modifier.isAbstract(clazz.getModifiers()) }
|| !WeixinMessageHandler.class try {
.isAssignableFrom(clazz)) { messageHandlerList.add((WeixinMessageHandler) beanFactory.getBean(clazz));
continue; } catch (RuntimeException ex) { // multiple
} for (Object o : beanFactory.getBeans(clazz).values()) {
try { if (o.getClass() == clazz) {
messageHandlerList messageHandlerList.add((WeixinMessageHandler) o);
.add((WeixinMessageHandler) beanFactory break;
.getBean(clazz)); }
} catch (RuntimeException ex) { // multiple }
for (Object o : beanFactory.getBeans(clazz) }
.values()) { }
if (o.getClass() == clazz) { } else {
messageHandlerList for (Class<?> clazz : messageHandlerClass) {
.add((WeixinMessageHandler) o); if (clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers())
break; || !WeixinMessageHandler.class.isAssignableFrom(clazz)) {
} continue;
} }
} try {
} Constructor<?> ctor = clazz.getDeclaredConstructor();
} else { ServerToolkits.makeConstructorAccessible(ctor);
for (Class<?> clazz : messageHandlerClass) { messageHandlerList.add((WeixinMessageHandler) ctor.newInstance((Object[]) null));
if (clazz.isInterface() } catch (Exception ex) {
|| Modifier.isAbstract(clazz.getModifiers()) throw new RuntimeException(clazz.getName() + " instantiate fail", ex);
|| !WeixinMessageHandler.class }
.isAssignableFrom(clazz)) { }
continue; }
} }
try { if (messageHandlerList != null && !this.messageHandlerList.isEmpty()) {
Constructor<?> ctor = clazz this.messageHandlers = this.messageHandlerList
.getDeclaredConstructor(); .toArray(new WeixinMessageHandler[this.messageHandlerList.size()]);
ServerToolkits.makeConstructorAccessible(ctor); }
messageHandlerList.add((WeixinMessageHandler) ctor }
.newInstance((Object[]) null)); return this.messageHandlers;
} catch (Exception ex) { }
throw new WeixinException(clazz.getName()
+ " instantiate fail", ex);
}
}
}
}
if (messageHandlerList != null
&& !this.messageHandlerList.isEmpty()) {
this.messageHandlers = this.messageHandlerList
.toArray(new WeixinMessageHandler[this.messageHandlerList
.size()]);
}
}
return this.messageHandlers;
}
/** /**
* 获取所有的interceptor * 获取所有的interceptor
* *
* @return interceptor集合 * @return interceptor集合
* @throws WeixinException * @ @see com.foxinmy.weixin4j.interceptor.WeixinMessageInterceptor
* @see com.foxinmy.weixin4j.interceptor.WeixinMessageInterceptor */
*/ public WeixinMessageInterceptor[] getMessageInterceptors() {
public WeixinMessageInterceptor[] getMessageInterceptors() if (this.messageInterceptors == null) {
throws WeixinException { if (this.messageInterceptorPackages != null) {
if (this.messageInterceptors == null) { List<Class<?>> messageInterceptorClass = new ArrayList<Class<?>>();
if (this.messageInterceptorPackages != null) { for (String packageName : messageInterceptorPackages) {
List<Class<?>> messageInterceptorClass = new ArrayList<Class<?>>(); messageInterceptorClass.addAll(ClassUtil.getClasses(packageName));
for (String packageName : messageInterceptorPackages) { }
messageInterceptorClass.addAll(ClassUtil if (beanFactory != null) {
.getClasses(packageName)); for (Class<?> clazz : messageInterceptorClass) {
} if (clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers())
if (beanFactory != null) { || !WeixinMessageInterceptor.class.isAssignableFrom(clazz)) {
for (Class<?> clazz : messageInterceptorClass) { continue;
if (clazz.isInterface() }
|| Modifier.isAbstract(clazz.getModifiers()) try {
|| !WeixinMessageInterceptor.class messageInterceptorList.add((WeixinMessageInterceptor) beanFactory.getBean(clazz));
.isAssignableFrom(clazz)) { } catch (RuntimeException ex) { // multiple
continue; for (Object o : beanFactory.getBeans(clazz).values()) {
} if (o.getClass() == clazz) {
try { messageInterceptorList.add((WeixinMessageInterceptor) o);
messageInterceptorList break;
.add((WeixinMessageInterceptor) beanFactory }
.getBean(clazz)); }
} catch (RuntimeException ex) { // multiple }
for (Object o : beanFactory.getBeans(clazz) }
.values()) { } else {
if (o.getClass() == clazz) { for (Class<?> clazz : messageInterceptorClass) {
messageInterceptorList if (clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers())
.add((WeixinMessageInterceptor) o); || !WeixinMessageInterceptor.class.isAssignableFrom(clazz)) {
break; continue;
} }
} try {
} Constructor<?> ctor = clazz.getDeclaredConstructor();
} ServerToolkits.makeConstructorAccessible(ctor);
} else { messageInterceptorList.add((WeixinMessageInterceptor) ctor.newInstance((Object[]) null));
for (Class<?> clazz : messageInterceptorClass) { } catch (Exception ex) {
if (clazz.isInterface() throw new RuntimeException(clazz.getName() + " instantiate fail", ex);
|| Modifier.isAbstract(clazz.getModifiers()) }
|| !WeixinMessageInterceptor.class }
.isAssignableFrom(clazz)) { }
continue; }
} if (this.messageInterceptorList != null && !this.messageInterceptorList.isEmpty()) {
try { Collections.sort(messageInterceptorList, new Comparator<WeixinMessageInterceptor>() {
Constructor<?> ctor = clazz @Override
.getDeclaredConstructor(); public int compare(WeixinMessageInterceptor m1, WeixinMessageInterceptor m2) {
ServerToolkits.makeConstructorAccessible(ctor); return m2.weight() - m1.weight();
messageInterceptorList }
.add((WeixinMessageInterceptor) ctor });
.newInstance((Object[]) null)); this.messageInterceptors = this.messageInterceptorList
} catch (Exception ex) { .toArray(new WeixinMessageInterceptor[this.messageInterceptorList.size()]);
throw new WeixinException(clazz.getName() }
+ " instantiate fail", ex); }
} logger.info("resolve message interceptors '{}'", this.messageInterceptorList);
} return this.messageInterceptors;
} }
}
if (this.messageInterceptorList != null
&& !this.messageInterceptorList.isEmpty()) {
Collections.sort(messageInterceptorList,
new Comparator<WeixinMessageInterceptor>() {
@Override
public int compare(WeixinMessageInterceptor m1,
WeixinMessageInterceptor m2) {
return m2.weight() - m1.weight();
}
});
this.messageInterceptors = this.messageInterceptorList
.toArray(new WeixinMessageInterceptor[this.messageInterceptorList
.size()]);
}
}
logger.info("resolve message interceptors '{}'",
this.messageInterceptorList);
return this.messageInterceptors;
}
/** /**
* jaxb读取微信消息 * jaxb读取微信消息
* *
* @param message * @param message
* xml消息 * xml消息
* @param clazz * @param clazz
* 消息类型 * 消息类型
* @return 消息对象 * @return 消息对象 @
* @throws WeixinException */
*/ protected <M extends WeixinMessage> M messageRead(String message, Class<M> clazz) {
protected <M extends WeixinMessage> M messageRead(String message, if (clazz == null) {
Class<M> clazz) throws WeixinException { return null;
if (clazz == null) { }
return null; try {
} Source source = new StreamSource(new ByteArrayInputStream(ServerToolkits.getBytesUtf8(message)));
try { JAXBElement<M> jaxbElement = getUnmarshaller(clazz).unmarshal(source, clazz);
Source source = new StreamSource(new ByteArrayInputStream( return jaxbElement.getValue();
ServerToolkits.getBytesUtf8(message))); } catch (JAXBException e) {
JAXBElement<M> jaxbElement = getUnmarshaller(clazz).unmarshal( throw new RuntimeException(e);
source, clazz); }
return jaxbElement.getValue(); }
} catch (JAXBException e) {
throw new WeixinException(e);
}
}
/** /**
* xml消息转换器 * xml消息转换器
* *
* @param clazz * @param clazz
* 消息类型 * 消息类型
* @return 消息转换器 * @return 消息转换器 @
* @throws WeixinException */
*/ protected Unmarshaller getUnmarshaller(Class<? extends WeixinMessage> clazz) {
protected Unmarshaller getUnmarshaller(Class<? extends WeixinMessage> clazz) Unmarshaller unmarshaller = messageUnmarshaller.get(clazz);
throws WeixinException { if (unmarshaller == null) {
Unmarshaller unmarshaller = messageUnmarshaller.get(clazz); try {
if (unmarshaller == null) { JAXBContext jaxbContext = JAXBContext.newInstance(clazz);
try { unmarshaller = jaxbContext.createUnmarshaller();
JAXBContext jaxbContext = JAXBContext.newInstance(clazz); messageUnmarshaller.put(clazz, unmarshaller);
unmarshaller = jaxbContext.createUnmarshaller(); } catch (JAXBException e) {
messageUnmarshaller.put(clazz, unmarshaller); throw new RuntimeException(e);
} catch (JAXBException e) { }
throw new WeixinException(e); }
} return unmarshaller;
} }
return unmarshaller;
}
public void setMessageHandlerList( public void setMessageHandlerList(List<WeixinMessageHandler> messageHandlerList) {
List<WeixinMessageHandler> messageHandlerList) { this.messageHandlerList = messageHandlerList;
this.messageHandlerList = messageHandlerList; }
}
public void setMessageInterceptorList( public void setMessageInterceptorList(List<WeixinMessageInterceptor> messageInterceptorList) {
List<WeixinMessageInterceptor> messageInterceptorList) { this.messageInterceptorList = messageInterceptorList;
this.messageInterceptorList = messageInterceptorList; }
}
public String[] getMessageHandlerPackages() { public String[] getMessageHandlerPackages() {
return messageHandlerPackages; return messageHandlerPackages;
} }
public String[] getMessageInterceptorPackages() { public String[] getMessageInterceptorPackages() {
return messageInterceptorPackages; return messageInterceptorPackages;
} }
public void setMessageHandlerPackages(String... messageHandlerPackages) { public void setMessageHandlerPackages(String... messageHandlerPackages) {
this.messageHandlerPackages = messageHandlerPackages; this.messageHandlerPackages = messageHandlerPackages;
} }
public void setMessageInterceptorPackages( public void setMessageInterceptorPackages(String... messageInterceptorPackages) {
String... messageInterceptorPackages) { this.messageInterceptorPackages = messageInterceptorPackages;
this.messageInterceptorPackages = messageInterceptorPackages; }
}
public BeanFactory getBeanFactory() { public BeanFactory getBeanFactory() {
return beanFactory; return beanFactory;
} }
public void setBeanFactory(BeanFactory beanFactory) { public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory; this.beanFactory = beanFactory;
} }
public void registMessageClass(WeixinMessageKey messageKey, public void registMessageClass(WeixinMessageKey messageKey, Class<? extends WeixinMessage> messageClass) {
Class<? extends WeixinMessage> messageClass) { messageMatcher.regist(messageKey, messageClass);
messageMatcher.regist(messageKey, messageClass); }
}
public WeixinMessageMatcher getMessageMatcher() { public WeixinMessageMatcher getMessageMatcher() {
return this.messageMatcher; return this.messageMatcher;
} }
/** /**
* 打开总是响应开关,如未匹配到MessageHandler时回复空白消息 * 打开总是响应开关,如未匹配到MessageHandler时回复空白消息
*/ */
public void openAlwaysResponse() { public void openAlwaysResponse() {
this.alwaysResponse = true; this.alwaysResponse = true;
} }
} }

View File

@ -1,50 +0,0 @@
package com.foxinmy.weixin4j.exception;
/**
* 微信异常
*
* @className WeixinException
* @author jinyu(foxinmy@gmail.com)
* @date 2014年4月10日
* @since JDK 1.6
* @see
*/
public class WeixinException extends Exception {
private static final long serialVersionUID = 7148145661883468514L;
private String errorCode;
private String errorMsg;
public WeixinException(String errorCode, String errorMsg) {
this.errorCode = errorCode;
this.errorMsg = errorMsg;
}
public WeixinException(String errorMsg) {
this.errorCode = "-1";
this.errorMsg = errorMsg;
}
public WeixinException(Exception e) {
super(e);
}
public WeixinException(String errorMsg, Exception e) {
super(e);
this.errorMsg = errorMsg;
}
public String getErrorCode() {
return errorCode;
}
public String getErrorMsg() {
return errorMsg;
}
@Override
public String getMessage() {
return this.errorCode + "," + this.errorMsg;
}
}

View File

@ -2,7 +2,6 @@ package com.foxinmy.weixin4j.handler;
import java.util.Set; import java.util.Set;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.request.WeixinMessage; import com.foxinmy.weixin4j.request.WeixinMessage;
import com.foxinmy.weixin4j.request.WeixinRequest; import com.foxinmy.weixin4j.request.WeixinRequest;
import com.foxinmy.weixin4j.response.TextResponse; import com.foxinmy.weixin4j.response.TextResponse;
@ -10,7 +9,7 @@ import com.foxinmy.weixin4j.response.WeixinResponse;
/** /**
* 调试消息处理器 * 调试消息处理器
* *
* @className DebugMessageHandler * @className DebugMessageHandler
* @author jinyu(foxinmy@gmail.com) * @author jinyu(foxinmy@gmail.com)
* @date 2015年5月17日 * @date 2015年5月17日
@ -19,29 +18,27 @@ import com.foxinmy.weixin4j.response.WeixinResponse;
*/ */
public class DebugMessageHandler implements WeixinMessageHandler { public class DebugMessageHandler implements WeixinMessageHandler {
public static final DebugMessageHandler global = new DebugMessageHandler(); public static final DebugMessageHandler global = new DebugMessageHandler();
private DebugMessageHandler() { private DebugMessageHandler() {
} }
@Override @Override
public boolean canHandle(WeixinRequest request, WeixinMessage message, public boolean canHandle(WeixinRequest request, WeixinMessage message, Set<String> nodeNames) {
Set<String> nodeNames) throws WeixinException { return true;
return true; }
}
@Override @Override
public WeixinResponse doHandle(WeixinRequest request, WeixinMessage message, public WeixinResponse doHandle(WeixinRequest request, WeixinMessage message, Set<String> nodeNames) {
Set<String> nodeNames) throws WeixinException { String content = message == null
String content = message == null ? request.getOriginalContent() ? request.getOriginalContent().replaceAll("\\!\\[CDATA\\[", "").replaceAll("\\]\\]", "")
.replaceAll("\\!\\[CDATA\\[", "").replaceAll("\\]\\]", "") : message.toString();
: message.toString(); return new TextResponse(content);
return new TextResponse(content); }
}
@Override @Override
public int weight() { public int weight() {
return 0; return 0;
} }
} }

View File

@ -2,7 +2,6 @@ package com.foxinmy.weixin4j.handler;
import java.util.Set; import java.util.Set;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.request.WeixinMessage; import com.foxinmy.weixin4j.request.WeixinMessage;
import com.foxinmy.weixin4j.request.WeixinRequest; import com.foxinmy.weixin4j.request.WeixinRequest;
import com.foxinmy.weixin4j.response.WeixinResponse; import com.foxinmy.weixin4j.response.WeixinResponse;
@ -10,7 +9,7 @@ import com.foxinmy.weixin4j.util.ClassUtil;
/** /**
* 消息适配器:对于特定的消息类型进行适配,如text文本voice语音消息 * 消息适配器:对于特定的消息类型进行适配,如text文本voice语音消息
* *
* @className MessageHandlerAdapter * @className MessageHandlerAdapter
* @author jinyu(foxinmy@gmail.com) * @author jinyu(foxinmy@gmail.com)
* @date 2015年5月17日 * @date 2015年5月17日
@ -18,56 +17,48 @@ import com.foxinmy.weixin4j.util.ClassUtil;
* @see com.foxinmy.weixin4j.request.WeixinMessage * @see com.foxinmy.weixin4j.request.WeixinMessage
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public abstract class MessageHandlerAdapter<M extends WeixinMessage> implements public abstract class MessageHandlerAdapter<M extends WeixinMessage> implements WeixinMessageHandler {
WeixinMessageHandler {
@Override @Override
public boolean canHandle(WeixinRequest request, WeixinMessage message, public boolean canHandle(WeixinRequest request, WeixinMessage message, Set<String> nodeNames) {
Set<String> nodeNames) throws WeixinException { return message != null && message.getClass() == ClassUtil.getGenericType(getClass())
return message != null && canHandle0(request, (M) message);
&& message.getClass() == ClassUtil.getGenericType(getClass()) }
&& canHandle0(request, (M) message);
}
/** /**
* 能否处理请求 * 能否处理请求
* *
* @param request * @param request
* 微信请求 * 微信请求
* @param message * @param message
* 微信消息 * 微信消息
* @return true则执行doHandler0 * @return true则执行doHandler0 @
* @throws WeixinException */
*/ public boolean canHandle0(WeixinRequest request, M message) {
public boolean canHandle0(WeixinRequest request, M message) return true;
throws WeixinException { }
return true;
}
@Override @Override
public WeixinResponse doHandle(WeixinRequest request, public WeixinResponse doHandle(WeixinRequest request, WeixinMessage message, Set<String> nodeNames) {
WeixinMessage message, Set<String> nodeNames) return doHandle0(request, (M) message);
throws WeixinException { }
return doHandle0(request, (M) message);
}
/** /**
* 处理请求 * 处理请求
* *
* @param request * @param request
* 微信请求 * 微信请求
* @param message * @param message
* 微信消息 * 微信消息
* @return * @return
*/ */
public abstract WeixinResponse doHandle0(WeixinRequest request, M message) public abstract WeixinResponse doHandle0(WeixinRequest request, M message);
throws WeixinException;
/** /**
* 缺省值为1,存在多个匹配到的MessageHandler则比较weight大小 * 缺省值为1,存在多个匹配到的MessageHandler则比较weight大小
*/ */
@Override @Override
public int weight() { public int weight() {
return 1; return 1;
} }
} }

View File

@ -3,13 +3,12 @@ package com.foxinmy.weixin4j.handler;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.request.WeixinMessage; import com.foxinmy.weixin4j.request.WeixinMessage;
import com.foxinmy.weixin4j.request.WeixinRequest; import com.foxinmy.weixin4j.request.WeixinRequest;
/** /**
* 多个消息类型适配 * 多个消息类型适配
* *
* @className MultipleMessageHandlerAdapter * @className MultipleMessageHandlerAdapter
* @author jinyu(foxinmy@gmail.com) * @author jinyu(foxinmy@gmail.com)
* @date 2016年3月12日 * @date 2016年3月12日
@ -18,41 +17,40 @@ import com.foxinmy.weixin4j.request.WeixinRequest;
*/ */
public abstract class MultipleMessageHandlerAdapter implements WeixinMessageHandler { public abstract class MultipleMessageHandlerAdapter implements WeixinMessageHandler {
private final Set<Class<? extends WeixinMessage>> messageClasses; private final Set<Class<? extends WeixinMessage>> messageClasses;
public MultipleMessageHandlerAdapter(Class<? extends WeixinMessage>... messageClasses) { public MultipleMessageHandlerAdapter(Class<? extends WeixinMessage>... messageClasses) {
if (messageClasses == null) { if (messageClasses == null) {
throw new IllegalArgumentException("messageClasses not be empty"); throw new IllegalArgumentException("messageClasses not be empty");
} }
this.messageClasses = new HashSet<Class<? extends WeixinMessage>>( this.messageClasses = new HashSet<Class<? extends WeixinMessage>>(
Math.max((int) (messageClasses.length / .75f) + 1, 16)); Math.max((int) (messageClasses.length / .75f) + 1, 16));
for (Class<? extends WeixinMessage> clazz : messageClasses) { for (Class<? extends WeixinMessage> clazz : messageClasses) {
this.messageClasses.add(clazz); this.messageClasses.add(clazz);
} }
} }
@Override @Override
public boolean canHandle(WeixinRequest request, WeixinMessage message, Set<String> nodeNames) public boolean canHandle(WeixinRequest request, WeixinMessage message, Set<String> nodeNames) {
throws WeixinException { return message != null && messageClasses.contains(message.getClass()) && canHandle0(request, message);
return message != null && messageClasses.contains(message.getClass()) && canHandle0(request, message); }
}
/** /**
* 能否处理请求 * 能否处理请求
* *
* @param request * @param request
* 微信请求 * 微信请求
* @param message * @param message
* 微信消息 * 微信消息
* @return true则执行doHandler * @return true则执行doHandler
* @throws WeixinException * @throws WeixinException
*/ */
public boolean canHandle0(WeixinRequest request, WeixinMessage message) throws WeixinException { public boolean canHandle0(WeixinRequest request, WeixinMessage message) {
return true; return true;
} }
@Override @Override
public int weight() { public int weight() {
return 1; return 1;
} }
} }

View File

@ -2,14 +2,13 @@ package com.foxinmy.weixin4j.handler;
import java.util.Set; import java.util.Set;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.request.WeixinMessage; import com.foxinmy.weixin4j.request.WeixinMessage;
import com.foxinmy.weixin4j.request.WeixinRequest; import com.foxinmy.weixin4j.request.WeixinRequest;
import com.foxinmy.weixin4j.response.WeixinResponse; import com.foxinmy.weixin4j.response.WeixinResponse;
/** /**
* 微信消息处理器 * 微信消息处理器
* *
* @className WeixinMessageHandler * @className WeixinMessageHandler
* @author jinyu(foxinmy@gmail.com) * @author jinyu(foxinmy@gmail.com)
* @date 2015年5月7日 * @date 2015年5月7日
@ -19,38 +18,36 @@ import com.foxinmy.weixin4j.response.WeixinResponse;
*/ */
public interface WeixinMessageHandler { public interface WeixinMessageHandler {
/** /**
* 能否处理请求 * 能否处理请求
* *
* @param request * @param request
* 微信请求 * 微信请求
* @param message * @param message
* 微信消息 * 微信消息
* @param nodeNames * @param nodeNames
* 节点名称集合 * 节点名称集合
* @return true则执行doHandle * @return true则执行doHandle
*/ */
public boolean canHandle(WeixinRequest request, WeixinMessage message, public boolean canHandle(WeixinRequest request, WeixinMessage message, Set<String> nodeNames);
Set<String> nodeNames) throws WeixinException;
/** /**
* 处理请求 * 处理请求
* *
* @param request * @param request
* 微信请求 * 微信请求
* @param message * @param message
* 微信消息 * 微信消息
* @param nodeNames * @param nodeNames
* 节点名称集合 * 节点名称集合
* @return 回复内容 * @return 回复内容
*/ */
public WeixinResponse doHandle(WeixinRequest request, WeixinMessage message, public WeixinResponse doHandle(WeixinRequest request, WeixinMessage message, Set<String> nodeNames);
Set<String> nodeNames) throws WeixinException;
/** /**
* 用于匹配到多个MessageHandler时权重降序排列,数字越大优先级越高 * 用于匹配到多个MessageHandler时权重降序排列,数字越大优先级越高
* *
* @return 权重 * @return 权重
*/ */
public int weight(); public int weight();
} }

View File

@ -1,47 +1,41 @@
package com.foxinmy.weixin4j.interceptor; package com.foxinmy.weixin4j.interceptor;
import io.netty.channel.ChannelHandlerContext;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.handler.WeixinMessageHandler; import com.foxinmy.weixin4j.handler.WeixinMessageHandler;
import com.foxinmy.weixin4j.request.WeixinMessage; import com.foxinmy.weixin4j.request.WeixinMessage;
import com.foxinmy.weixin4j.request.WeixinRequest; import com.foxinmy.weixin4j.request.WeixinRequest;
import com.foxinmy.weixin4j.response.WeixinResponse; import com.foxinmy.weixin4j.response.WeixinResponse;
import io.netty.channel.ChannelHandlerContext;
/** /**
* 消息拦截适配 * 消息拦截适配
* *
* @className MessageInterceptorAdapter * @className MessageInterceptorAdapter
* @author jinyu(foxinmy@gmail.com) * @author jinyu(foxinmy@gmail.com)
* @date 2015年5月14日 * @date 2015年5月14日
* @since JDK 1.6 * @since JDK 1.6
* @see * @see
*/ */
public abstract class MessageInterceptorAdapter implements public abstract class MessageInterceptorAdapter implements WeixinMessageInterceptor {
WeixinMessageInterceptor {
@Override @Override
public boolean preHandle(ChannelHandlerContext context, public boolean preHandle(ChannelHandlerContext context, WeixinRequest request, WeixinMessage message,
WeixinRequest request, WeixinMessage message, WeixinMessageHandler handler) WeixinMessageHandler handler) {
throws WeixinException { return true;
return true; }
}
@Override @Override
public void postHandle(ChannelHandlerContext context, public void postHandle(ChannelHandlerContext context, WeixinRequest request, WeixinResponse response,
WeixinRequest request, WeixinResponse response, WeixinMessage message, WeixinMessage message, WeixinMessageHandler handler) {
WeixinMessageHandler handler) throws WeixinException { }
}
@Override @Override
public void afterCompletion(ChannelHandlerContext context, public void afterCompletion(ChannelHandlerContext context, WeixinRequest request, WeixinResponse response,
WeixinRequest request, WeixinResponse response, WeixinMessage message, WeixinMessage message, WeixinMessageHandler handler, Exception exception) {
WeixinMessageHandler handler, Exception exception) }
throws WeixinException {
}
@Override @Override
public int weight() { public int weight() {
return 0; return 0;
} }
} }

View File

@ -1,16 +1,15 @@
package com.foxinmy.weixin4j.interceptor; package com.foxinmy.weixin4j.interceptor;
import io.netty.channel.ChannelHandlerContext;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.handler.WeixinMessageHandler; import com.foxinmy.weixin4j.handler.WeixinMessageHandler;
import com.foxinmy.weixin4j.request.WeixinMessage; import com.foxinmy.weixin4j.request.WeixinMessage;
import com.foxinmy.weixin4j.request.WeixinRequest; import com.foxinmy.weixin4j.request.WeixinRequest;
import com.foxinmy.weixin4j.response.WeixinResponse; import com.foxinmy.weixin4j.response.WeixinResponse;
import io.netty.channel.ChannelHandlerContext;
/** /**
* 微信消息拦截器 * 微信消息拦截器
* *
* @className WeixinMessageInterceptor * @className WeixinMessageInterceptor
* @author jinyu(foxinmy@gmail.com) * @author jinyu(foxinmy@gmail.com)
* @date 2015年5月7日 * @date 2015年5月7日
@ -21,7 +20,7 @@ public interface WeixinMessageInterceptor {
/** /**
* 执行handler前 * 执行handler前
* *
* @param context * @param context
* 通道环境 * 通道环境
* @param request * @param request
@ -34,12 +33,11 @@ public interface WeixinMessageInterceptor {
* @throws WeixinException * @throws WeixinException
*/ */
boolean preHandle(ChannelHandlerContext context, WeixinRequest request, boolean preHandle(ChannelHandlerContext context, WeixinRequest request,
WeixinMessage message, WeixinMessageHandler handler) WeixinMessage message, WeixinMessageHandler handler);
throws WeixinException;
/** /**
* 执行handler后 * 执行handler后
* *
* @param context * @param context
* 通道环境 * 通道环境
* @param request * @param request
@ -54,11 +52,11 @@ public interface WeixinMessageInterceptor {
*/ */
void postHandle(ChannelHandlerContext context, WeixinRequest request, void postHandle(ChannelHandlerContext context, WeixinRequest request,
WeixinResponse response, WeixinMessage message, WeixinResponse response, WeixinMessage message,
WeixinMessageHandler handler) throws WeixinException; WeixinMessageHandler handler);
/** /**
* 全部执行后 * 全部执行后
* *
* @param context * @param context
* 通道环境 * 通道环境
* @param request * @param request
@ -73,12 +71,11 @@ public interface WeixinMessageInterceptor {
*/ */
void afterCompletion(ChannelHandlerContext context, WeixinRequest request, void afterCompletion(ChannelHandlerContext context, WeixinRequest request,
WeixinResponse response, WeixinMessage message, WeixinResponse response, WeixinMessage message,
WeixinMessageHandler handler, Exception exception) WeixinMessageHandler handler, Exception exception);
throws WeixinException;
/** /**
* 用于匹配到多个MessageHandler时权重降序排列,数字越大优先级越高 * 用于匹配到多个MessageHandler时权重降序排列,数字越大优先级越高
* *
* @return 权重 * @return 权重
*/ */
int weight(); int weight();

View File

@ -1,21 +1,20 @@
package com.foxinmy.weixin4j.socket; package com.foxinmy.weixin4j.socket;
import java.util.List;
import com.foxinmy.weixin4j.response.SingleResponse;
import com.foxinmy.weixin4j.util.HttpUtil;
import com.foxinmy.weixin4j.util.ServerToolkits;
import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageEncoder; import io.netty.handler.codec.MessageToMessageEncoder;
import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory; import io.netty.util.internal.logging.InternalLoggerFactory;
import java.util.List;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.response.SingleResponse;
import com.foxinmy.weixin4j.util.HttpUtil;
import com.foxinmy.weixin4j.util.ServerToolkits;
/** /**
* 单一回复编码类 * 单一回复编码类
* *
* @className SingleResponseEncoder * @className SingleResponseEncoder
* @author jinyu(foxinmy@gmail.com) * @author jinyu(foxinmy@gmail.com)
* @date 2015年08月02日 * @date 2015年08月02日
@ -23,18 +22,14 @@ import com.foxinmy.weixin4j.util.ServerToolkits;
* @see com.foxinmy.weixin4j.response.SingleResponse * @see com.foxinmy.weixin4j.response.SingleResponse
*/ */
@ChannelHandler.Sharable @ChannelHandler.Sharable
public class SingleResponseEncoder extends public class SingleResponseEncoder extends MessageToMessageEncoder<SingleResponse> {
MessageToMessageEncoder<SingleResponse> {
private final InternalLogger logger = InternalLoggerFactory private final InternalLogger logger = InternalLoggerFactory.getInstance(getClass());
.getInstance(getClass());
@Override @Override
protected void encode(ChannelHandlerContext ctx, SingleResponse response, protected void encode(ChannelHandlerContext ctx, SingleResponse response, List<Object> out) {
List<Object> out) throws WeixinException { String content = response.toContent();
String content = response.toContent(); ctx.writeAndFlush(HttpUtil.createHttpResponse(content, ServerToolkits.CONTENTTYPE$TEXT_PLAIN));
ctx.writeAndFlush(HttpUtil.createHttpResponse(content, logger.info("encode single response:{}", content);
ServerToolkits.CONTENTTYPE$TEXT_PLAIN)); }
logger.info("encode single response:{}", content);
}
} }

View File

@ -5,7 +5,6 @@ import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.request.WeixinRequest; import com.foxinmy.weixin4j.request.WeixinRequest;
import com.foxinmy.weixin4j.type.EncryptType; import com.foxinmy.weixin4j.type.EncryptType;
import com.foxinmy.weixin4j.util.AesToken; import com.foxinmy.weixin4j.util.AesToken;
@ -50,7 +49,7 @@ public class WeixinMessageDecoder extends MessageToMessageDecoder<FullHttpReques
} }
@Override @Override
protected void decode(ChannelHandlerContext ctx, FullHttpRequest req, List<Object> out) throws WeixinException { protected void decode(ChannelHandlerContext ctx, FullHttpRequest req, List<Object> out) {
String messageContent = req.content().toString(ServerToolkits.UTF_8); String messageContent = req.content().toString(ServerToolkits.UTF_8);
QueryStringDecoder queryDecoder = new QueryStringDecoder(req.uri(), true); QueryStringDecoder queryDecoder = new QueryStringDecoder(req.uri(), true);
HttpMethod method = req.method(); HttpMethod method = req.method();
@ -68,7 +67,7 @@ public class WeixinMessageDecoder extends MessageToMessageDecoder<FullHttpReques
String encryptContent = null; String encryptContent = null;
if (!ServerToolkits.isBlank(messageContent) && encryptType == EncryptType.AES) { if (!ServerToolkits.isBlank(messageContent) && encryptType == EncryptType.AES) {
if (ServerToolkits.isBlank(aesToken.getAesKey())) { if (ServerToolkits.isBlank(aesToken.getAesKey())) {
throw new WeixinException("EncodingAESKey not be empty in safety(AES) mode"); throw new RuntimeException("EncodingAESKey not be empty in safety(AES) mode");
} }
EncryptMessageHandler encryptHandler = EncryptMessageHandler.parser(messageContent); EncryptMessageHandler encryptHandler = EncryptMessageHandler.parser(messageContent);
encryptContent = encryptHandler.getEncryptContent(); encryptContent = encryptHandler.getEncryptContent();

View File

@ -3,6 +3,16 @@ package com.foxinmy.weixin4j.socket;
import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST; import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST;
import static io.netty.handler.codec.http.HttpResponseStatus.FORBIDDEN; import static io.netty.handler.codec.http.HttpResponseStatus.FORBIDDEN;
import static io.netty.handler.codec.http.HttpResponseStatus.METHOD_NOT_ALLOWED; import static io.netty.handler.codec.http.HttpResponseStatus.METHOD_NOT_ALLOWED;
import com.foxinmy.weixin4j.dispatcher.WeixinMessageDispatcher;
import com.foxinmy.weixin4j.request.WeixinRequest;
import com.foxinmy.weixin4j.response.SingleResponse;
import com.foxinmy.weixin4j.type.EncryptType;
import com.foxinmy.weixin4j.util.AesToken;
import com.foxinmy.weixin4j.util.HttpUtil;
import com.foxinmy.weixin4j.util.MessageUtil;
import com.foxinmy.weixin4j.util.ServerToolkits;
import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.SimpleChannelInboundHandler;
@ -13,119 +23,92 @@ import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory; import io.netty.util.internal.logging.InternalLoggerFactory;
import com.foxinmy.weixin4j.dispatcher.WeixinMessageDispatcher;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.request.WeixinRequest;
import com.foxinmy.weixin4j.response.SingleResponse;
import com.foxinmy.weixin4j.type.EncryptType;
import com.foxinmy.weixin4j.util.AesToken;
import com.foxinmy.weixin4j.util.HttpUtil;
import com.foxinmy.weixin4j.util.MessageUtil;
import com.foxinmy.weixin4j.util.ServerToolkits;
/** /**
* 微信请求处理类 * 微信请求处理类
* *
* @className WeixinRequestHandler * @className WeixinRequestHandler
* @author jinyu(foxinmy@gmail.com) * @author jinyu(foxinmy@gmail.com)
* @date 2014年11月16日 * @date 2014年11月16日
* @since JDK 1.6 * @since JDK 1.6
* @see com.foxinmy.weixin4j.dispatcher.WeixinMessageDispatcher * @see com.foxinmy.weixin4j.dispatcher.WeixinMessageDispatcher
*/ */
public class WeixinRequestHandler extends public class WeixinRequestHandler extends SimpleChannelInboundHandler<WeixinRequest> {
SimpleChannelInboundHandler<WeixinRequest> { private final InternalLogger logger = InternalLoggerFactory.getInstance(getClass());
private final InternalLogger logger = InternalLoggerFactory
.getInstance(getClass());
private final WeixinMessageDispatcher messageDispatcher; private final WeixinMessageDispatcher messageDispatcher;
public WeixinRequestHandler(WeixinMessageDispatcher messageDispatcher) { public WeixinRequestHandler(WeixinMessageDispatcher messageDispatcher) {
this.messageDispatcher = messageDispatcher; this.messageDispatcher = messageDispatcher;
} }
public void channelReadComplete(ChannelHandlerContext ctx) { public void channelReadComplete(ChannelHandlerContext ctx) {
ctx.flush(); ctx.flush();
} }
@Override @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
ctx.close(); ctx.close();
logger.error(cause); logger.error(cause);
} }
@Override @Override
protected void channelRead0(ChannelHandlerContext ctx, WeixinRequest request) protected void channelRead0(ChannelHandlerContext ctx, WeixinRequest request) {
throws WeixinException { AesToken aesToken = request.getAesToken();
AesToken aesToken = request.getAesToken(); // 消息字段不完整返回400
// 消息字段不完整返回400 if (aesToken == null || (ServerToolkits.isBlank(request.getSignature())
if (aesToken == null && ServerToolkits.isBlank(request.getMsgSignature()))) {
|| (ServerToolkits.isBlank(request.getSignature()) && ServerToolkits ctx.writeAndFlush(resolveResponse(BAD_REQUEST, request)).addListener(ChannelFutureListener.CLOSE);
.isBlank(request.getMsgSignature()))) { return;
ctx.writeAndFlush(resolveResponse(BAD_REQUEST, request)) }
.addListener(ChannelFutureListener.CLOSE); /**
return; * 公众平台:无论Get,Post都带signature参数,当开启aes模式时带msg_signature参数
} * 企业号:无论Get,Post都带msg_signature参数
/** * 一般来说signature验证url上的参数签名msg_signature验证消息体签名
* 公众平台:无论Get,Post都带signature参数,当开启aes模式时带msg_signature参数 **/
* 企业号:无论Get,Post都带msg_signature参数 if (request.getMethod() == HttpMethod.GET) {
* 一般来说signature验证url上的参数签名msg_signature验证消息体签名 // URL参数签名验证
**/ if (!ServerToolkits.isBlank(request.getSignature())
if (request.getMethod() == HttpMethod.GET) { && MessageUtil.signature(aesToken.getToken(), request.getTimeStamp(), request.getNonce())
// URL参数签名验证 .equals(request.getSignature())) {
if (!ServerToolkits.isBlank(request.getSignature()) ctx.writeAndFlush(new SingleResponse(request.getEchoStr()));
&& MessageUtil.signature(aesToken.getToken(), return;
request.getTimeStamp(), request.getNonce()).equals( }
request.getSignature())) { // XML消息签名验证
ctx.writeAndFlush(new SingleResponse(request.getEchoStr())); if (!ServerToolkits.isBlank(request.getMsgSignature()) && MessageUtil
return; .signature(aesToken.getToken(), request.getTimeStamp(), request.getNonce(), request.getEchoStr())
} .equals(request.getMsgSignature())) {
// XML消息签名验证 ctx.writeAndFlush(
if (!ServerToolkits.isBlank(request.getMsgSignature()) new SingleResponse(MessageUtil.aesDecrypt(null, aesToken.getAesKey(), request.getEchoStr())));
&& MessageUtil.signature(aesToken.getToken(), return;
request.getTimeStamp(), request.getNonce(), }
request.getEchoStr()).equals( ctx.writeAndFlush(resolveResponse(FORBIDDEN, request)).addListener(ChannelFutureListener.CLOSE);
request.getMsgSignature())) { return;
ctx.writeAndFlush(new SingleResponse(MessageUtil.aesDecrypt( } else if (request.getMethod() == HttpMethod.POST) {
null, aesToken.getAesKey(), request.getEchoStr()))); // URL参数签名验证
return; if (!ServerToolkits.isBlank(request.getSignature())
} && !MessageUtil.signature(aesToken.getToken(), request.getTimeStamp(), request.getNonce())
ctx.writeAndFlush(resolveResponse(FORBIDDEN, request)).addListener( .equals(request.getSignature())) {
ChannelFutureListener.CLOSE); ctx.writeAndFlush(resolveResponse(FORBIDDEN, request)).addListener(ChannelFutureListener.CLOSE);
return; return;
} else if (request.getMethod() == HttpMethod.POST) { }
// URL参数签名验证 // XML消息签名验证
if (!ServerToolkits.isBlank(request.getSignature()) if (request.getEncryptType() == EncryptType.AES
&& !MessageUtil.signature(aesToken.getToken(), && !MessageUtil.signature(aesToken.getToken(), request.getTimeStamp(), request.getNonce(),
request.getTimeStamp(), request.getNonce()).equals( request.getEncryptContent()).equals(request.getMsgSignature())) {
request.getSignature())) { ctx.writeAndFlush(resolveResponse(FORBIDDEN, request)).addListener(ChannelFutureListener.CLOSE);
ctx.writeAndFlush(resolveResponse(FORBIDDEN, request)) return;
.addListener(ChannelFutureListener.CLOSE); }
return; } else {
} // 访问其它URL
// XML消息签名验证 ctx.writeAndFlush(resolveResponse(METHOD_NOT_ALLOWED, request)).addListener(ChannelFutureListener.CLOSE);
if (request.getEncryptType() == EncryptType.AES return;
&& !MessageUtil.signature(aesToken.getToken(), }
request.getTimeStamp(), request.getNonce(), messageDispatcher.doDispatch(ctx, request);
request.getEncryptContent()).equals( }
request.getMsgSignature())) {
ctx.writeAndFlush(resolveResponse(FORBIDDEN, request))
.addListener(ChannelFutureListener.CLOSE);
return;
}
} else {
// 访问其它URL
ctx.writeAndFlush(resolveResponse(METHOD_NOT_ALLOWED, request))
.addListener(ChannelFutureListener.CLOSE);
return;
}
messageDispatcher.doDispatch(ctx, request);
}
private FullHttpResponse resolveResponse(HttpResponseStatus responseStatus, private FullHttpResponse resolveResponse(HttpResponseStatus responseStatus, WeixinRequest request) {
WeixinRequest request) { FullHttpResponse response = new DefaultFullHttpResponse(request.getProtocolVersion(), responseStatus);
FullHttpResponse response = new DefaultFullHttpResponse( HttpUtil.resolveHeaders(response);
request.getProtocolVersion(), responseStatus); return response;
HttpUtil.resolveHeaders(response); }
return response;
}
} }

View File

@ -1,14 +1,7 @@
package com.foxinmy.weixin4j.socket; package com.foxinmy.weixin4j.socket;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageEncoder;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import java.util.List; import java.util.List;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.response.WeixinResponse; import com.foxinmy.weixin4j.response.WeixinResponse;
import com.foxinmy.weixin4j.type.EncryptType; import com.foxinmy.weixin4j.type.EncryptType;
import com.foxinmy.weixin4j.util.AesToken; import com.foxinmy.weixin4j.util.AesToken;
@ -16,6 +9,12 @@ import com.foxinmy.weixin4j.util.HttpUtil;
import com.foxinmy.weixin4j.util.MessageUtil; import com.foxinmy.weixin4j.util.MessageUtil;
import com.foxinmy.weixin4j.util.ServerToolkits; import com.foxinmy.weixin4j.util.ServerToolkits;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageEncoder;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
/** /**
* 微信回复编码类 * 微信回复编码类
* *
@ -23,66 +22,55 @@ import com.foxinmy.weixin4j.util.ServerToolkits;
* @author jinyu(foxinmy@gmail.com) * @author jinyu(foxinmy@gmail.com)
* @date 2014年11月13日 * @date 2014年11月13日
* @since JDK 1.6 * @since JDK 1.6
* @see <a * @see <a href=
* href="http://mp.weixin.qq.com/wiki/0/61c3a8b9d50ac74f18bdf2e54ddfc4e0.html">加密接入指引</a> * "http://mp.weixin.qq.com/wiki/0/61c3a8b9d50ac74f18bdf2e54ddfc4e0.html">加密接入指引</a>
* @see com.foxinmy.weixin4j.response.WeixinResponse * @see com.foxinmy.weixin4j.response.WeixinResponse
*/ */
@ChannelHandler.Sharable @ChannelHandler.Sharable
public class WeixinResponseEncoder extends public class WeixinResponseEncoder extends MessageToMessageEncoder<WeixinResponse> {
MessageToMessageEncoder<WeixinResponse> {
protected final InternalLogger logger = InternalLoggerFactory protected final InternalLogger logger = InternalLoggerFactory.getInstance(getClass());
.getInstance(getClass());
private final String XML_START = "<xml>"; private final String XML_START = "<xml>";
// ---------------明文节点 // ---------------明文节点
private final String ELEMENT_TOUSERNAME = "<ToUserName><![CDATA[%s]]></ToUserName>"; private final String ELEMENT_TOUSERNAME = "<ToUserName><![CDATA[%s]]></ToUserName>";
private final String ELEMENT_FROMUSERNAME = "<FromUserName><![CDATA[%s]]></FromUserName>"; private final String ELEMENT_FROMUSERNAME = "<FromUserName><![CDATA[%s]]></FromUserName>";
private final String ELEMENT_CREATETIME = "<CreateTime><![CDATA[%d]]></CreateTime>"; private final String ELEMENT_CREATETIME = "<CreateTime><![CDATA[%d]]></CreateTime>";
private final String ELEMENT_MSGTYPE = "<MsgType><![CDATA[%s]]></MsgType>"; private final String ELEMENT_MSGTYPE = "<MsgType><![CDATA[%s]]></MsgType>";
// ---------------密文节点 // ---------------密文节点
private final String ELEMENT_MSGSIGNATURE = "<MsgSignature><![CDATA[%s]]></MsgSignature>"; private final String ELEMENT_MSGSIGNATURE = "<MsgSignature><![CDATA[%s]]></MsgSignature>";
private final String ELEMENT_ENCRYPT = "<Encrypt><![CDATA[%s]]></Encrypt>"; private final String ELEMENT_ENCRYPT = "<Encrypt><![CDATA[%s]]></Encrypt>";
private final String ELEMENT_TIMESTAMP = "<TimeStamp><![CDATA[%s]]></TimeStamp>"; private final String ELEMENT_TIMESTAMP = "<TimeStamp><![CDATA[%s]]></TimeStamp>";
private final String ELEMENT_NONCE = "<Nonce><![CDATA[%s]]></Nonce>"; private final String ELEMENT_NONCE = "<Nonce><![CDATA[%s]]></Nonce>";
private final String XML_END = "</xml>"; private final String XML_END = "</xml>";
@Override @Override
protected void encode(ChannelHandlerContext ctx, WeixinResponse response, protected void encode(ChannelHandlerContext ctx, WeixinResponse response, List<Object> out) {
List<Object> out) throws WeixinException { WeixinMessageTransfer messageTransfer = ctx.channel().attr(ServerToolkits.MESSAGE_TRANSFER_KEY).get();
WeixinMessageTransfer messageTransfer = ctx.channel() EncryptType encryptType = messageTransfer.getEncryptType();
.attr(ServerToolkits.MESSAGE_TRANSFER_KEY).get(); StringBuilder content = new StringBuilder();
EncryptType encryptType = messageTransfer.getEncryptType(); content.append(XML_START);
StringBuilder content = new StringBuilder(); content.append(String.format(ELEMENT_TOUSERNAME, messageTransfer.getFromUserName()));
content.append(XML_START); content.append(String.format(ELEMENT_FROMUSERNAME, messageTransfer.getToUserName()));
content.append(String.format(ELEMENT_TOUSERNAME, content.append(String.format(ELEMENT_CREATETIME, System.currentTimeMillis() / 1000l));
messageTransfer.getFromUserName())); content.append(String.format(ELEMENT_MSGTYPE, response.getMsgType()));
content.append(String.format(ELEMENT_FROMUSERNAME, content.append(response.toContent());
messageTransfer.getToUserName())); content.append(XML_END);
content.append(String.format(ELEMENT_CREATETIME, if (encryptType == EncryptType.AES) {
System.currentTimeMillis() / 1000l)); AesToken aesToken = messageTransfer.getAesToken();
content.append(String.format(ELEMENT_MSGTYPE, response.getMsgType())); String nonce = ServerToolkits.generateRandomString(32);
content.append(response.toContent()); String timestamp = Long.toString(System.currentTimeMillis() / 1000l);
content.append(XML_END); String encrtypt = MessageUtil.aesEncrypt(aesToken.getWeixinId(), aesToken.getAesKey(), content.toString());
if (encryptType == EncryptType.AES) { String msgSignature = MessageUtil.signature(aesToken.getToken(), nonce, timestamp, encrtypt);
AesToken aesToken = messageTransfer.getAesToken(); content.delete(0, content.length());
String nonce = ServerToolkits.generateRandomString(32); content.append(XML_START);
String timestamp = Long content.append(String.format(ELEMENT_NONCE, nonce));
.toString(System.currentTimeMillis() / 1000l); content.append(String.format(ELEMENT_TIMESTAMP, timestamp));
String encrtypt = MessageUtil.aesEncrypt(aesToken.getWeixinId(), content.append(String.format(ELEMENT_MSGSIGNATURE, msgSignature));
aesToken.getAesKey(), content.toString()); content.append(String.format(ELEMENT_ENCRYPT, encrtypt));
String msgSignature = MessageUtil.signature(aesToken.getToken(), content.append(XML_END);
nonce, timestamp, encrtypt); }
content.delete(0, content.length()); ctx.writeAndFlush(HttpUtil.createHttpResponse(content.toString(), ServerToolkits.CONTENTTYPE$APPLICATION_XML));
content.append(XML_START); logger.info("{} encode weixin response:{}", encryptType, content);
content.append(String.format(ELEMENT_NONCE, nonce)); }
content.append(String.format(ELEMENT_TIMESTAMP, timestamp));
content.append(String.format(ELEMENT_MSGSIGNATURE, msgSignature));
content.append(String.format(ELEMENT_ENCRYPT, encrtypt));
content.append(XML_END);
}
ctx.writeAndFlush(HttpUtil.createHttpResponse(content.toString(),
ServerToolkits.CONTENTTYPE$APPLICATION_XML));
logger.info("{} encode weixin response:{}", encryptType, content);
}
} }

View File

@ -5,11 +5,10 @@ import java.util.Map;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import com.foxinmy.weixin4j.dispatcher.BeanFactory; import com.foxinmy.weixin4j.dispatcher.BeanFactory;
import com.foxinmy.weixin4j.exception.WeixinException;
/** /**
* 使用spring容器获取bean * 使用spring容器获取bean
* *
* @className SpringBeanFactory * @className SpringBeanFactory
* @author jinyu(foxinmy@gmail.com) * @author jinyu(foxinmy@gmail.com)
* @date 2015年8月11日 * @date 2015年8月11日
@ -18,29 +17,29 @@ import com.foxinmy.weixin4j.exception.WeixinException;
*/ */
public class SpringBeanFactory implements BeanFactory { public class SpringBeanFactory implements BeanFactory {
private ApplicationContext context; private ApplicationContext context;
public SpringBeanFactory(ApplicationContext context) { public SpringBeanFactory(ApplicationContext context) {
this.context = context; this.context = context;
} }
@Override @Override
public Object getBean(String name) throws WeixinException { public Object getBean(String name) {
return context.getBean(name); return context.getBean(name);
} }
@Override @Override
public <T> T getBean(Class<T> classType) throws WeixinException { public <T> T getBean(Class<T> classType) {
return context.getBean(classType); return context.getBean(classType);
} }
@Override @Override
public <T> T getBean(String name, Class<T> classType) throws WeixinException { public <T> T getBean(String name, Class<T> classType) {
return context.getBean(name, classType); return context.getBean(name, classType);
} }
@Override @Override
public <T> Map<String, T> getBeans(Class<T> clazz) throws WeixinException { public <T> Map<String, T> getBeans(Class<T> clazz) {
return context.getBeansOfType(clazz); return context.getBeansOfType(clazz);
} }
} }

View File

@ -11,7 +11,6 @@ import com.foxinmy.weixin4j.dispatcher.DefaultMessageMatcher;
import com.foxinmy.weixin4j.dispatcher.WeixinMessageDispatcher; import com.foxinmy.weixin4j.dispatcher.WeixinMessageDispatcher;
import com.foxinmy.weixin4j.dispatcher.WeixinMessageKey; import com.foxinmy.weixin4j.dispatcher.WeixinMessageKey;
import com.foxinmy.weixin4j.dispatcher.WeixinMessageMatcher; import com.foxinmy.weixin4j.dispatcher.WeixinMessageMatcher;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.handler.WeixinMessageHandler; import com.foxinmy.weixin4j.handler.WeixinMessageHandler;
import com.foxinmy.weixin4j.interceptor.WeixinMessageInterceptor; import com.foxinmy.weixin4j.interceptor.WeixinMessageInterceptor;
import com.foxinmy.weixin4j.request.WeixinMessage; import com.foxinmy.weixin4j.request.WeixinMessage;
@ -173,7 +172,7 @@ public final class WeixinServerBootstrap {
* 默认端口(30000)启动服务 * 默认端口(30000)启动服务
* *
*/ */
public void startup() throws WeixinException { public void startup() {
startup(DEFAULT_SERVERPORT); startup(DEFAULT_SERVERPORT);
} }
@ -181,7 +180,7 @@ public final class WeixinServerBootstrap {
* 指定端口启动服务 * 指定端口启动服务
* *
*/ */
public void startup(int serverPort) throws WeixinException { public void startup(int serverPort) {
startup(DEFAULT_BOSSTHREADS, DEFAULT_WORKERTHREADS, serverPort); startup(DEFAULT_BOSSTHREADS, DEFAULT_WORKERTHREADS, serverPort);
} }
@ -197,7 +196,7 @@ public final class WeixinServerBootstrap {
* @return * @return
* @throws WeixinException * @throws WeixinException
*/ */
public void startup(int bossThreads, int workerThreads, final int serverPort) throws WeixinException { public void startup(int bossThreads, int workerThreads, final int serverPort) {
messageDispatcher.setMessageHandlerList(messageHandlerList); messageDispatcher.setMessageHandlerList(messageHandlerList);
messageDispatcher.setMessageInterceptorList(messageInterceptorList); messageDispatcher.setMessageInterceptorList(messageInterceptorList);
try { try {
@ -218,7 +217,7 @@ public final class WeixinServerBootstrap {
}).sync().channel(); }).sync().channel();
ch.closeFuture().sync(); ch.closeFuture().sync();
} catch (InterruptedException e) { } catch (InterruptedException e) {
throw new WeixinException("netty server startup FAIL", e); throw new RuntimeException(e);
} finally { } finally {
shutdown(); shutdown();
} }

View File

@ -18,11 +18,9 @@ import java.util.List;
import java.util.jar.JarEntry; import java.util.jar.JarEntry;
import java.util.jar.JarFile; import java.util.jar.JarFile;
import com.foxinmy.weixin4j.exception.WeixinException;
/** /**
* 对class的获取 * 对class的获取
* *
* @className ClassUtil * @className ClassUtil
* @author jinyu(foxinmy@gmail.com) * @author jinyu(foxinmy@gmail.com)
* @date 2014年10月31日 * @date 2014年10月31日
@ -30,189 +28,180 @@ import com.foxinmy.weixin4j.exception.WeixinException;
* @see * @see
*/ */
public final class ClassUtil { public final class ClassUtil {
private final static String POINT = "."; private final static String POINT = ".";
private final static String CLASS = ".class"; private final static String CLASS = ".class";
/** /**
* 获取某个包下所有的class信息 * 获取某个包下所有的class信息
* *
* @param packageName * @param packageName
* 包名 * 包名
* @return * @return
*/ */
public static List<Class<?>> getClasses(String packageName) public static List<Class<?>> getClasses(String packageName) {
throws WeixinException { String packageFileName = packageName.replace(POINT, File.separator);
String packageFileName = packageName.replace(POINT, File.separator); URL fullPath = getDefaultClassLoader().getResource(packageFileName);
URL fullPath = getDefaultClassLoader().getResource(packageFileName); String protocol = fullPath.getProtocol();
String protocol = fullPath.getProtocol(); if (protocol.equals(ServerToolkits.PROTOCOL_FILE)) {
if (protocol.equals(ServerToolkits.PROTOCOL_FILE)) { try {
try { File dir = new File(fullPath.toURI());
File dir = new File(fullPath.toURI()); return findClassesByFile(dir, packageName);
return findClassesByFile(dir, packageName); } catch (URISyntaxException e) {
} catch (URISyntaxException e) { throw new RuntimeException(e);
throw new WeixinException(e); }
} } else if (protocol.equals(ServerToolkits.PROTOCOL_JAR)) {
} else if (protocol.equals(ServerToolkits.PROTOCOL_JAR)) { try {
try { return findClassesByJar(((JarURLConnection) fullPath.openConnection()).getJarFile(), packageName);
return findClassesByJar( } catch (IOException e) {
((JarURLConnection) fullPath.openConnection()) throw new RuntimeException(e);
.getJarFile(), }
packageName); }
} catch (IOException e) { return null;
throw new WeixinException(e); }
}
}
return null;
}
/** /**
* 扫描目录下所有的class对象 * 扫描目录下所有的class对象
* *
* @param dir * @param dir
* 文件目录 * 文件目录
* @param packageName * @param packageName
* 包的全限类名 * 包的全限类名
* @return * @return
*/ */
private static List<Class<?>> findClassesByFile(File dir, String packageName) { private static List<Class<?>> findClassesByFile(File dir, String packageName) {
List<Class<?>> classes = new ArrayList<Class<?>>(); List<Class<?>> classes = new ArrayList<Class<?>>();
File[] files = dir.listFiles(new FilenameFilter() { File[] files = dir.listFiles(new FilenameFilter() {
@Override @Override
public boolean accept(File file, String name) { public boolean accept(File file, String name) {
return file.isDirectory() || file.getName().endsWith(CLASS); return file.isDirectory() || file.getName().endsWith(CLASS);
} }
}); });
if (files != null) { if (files != null) {
for (File file : files) { for (File file : files) {
if (file.isDirectory()) { if (file.isDirectory()) {
classes.addAll(findClassesByFile(file, packageName + POINT classes.addAll(findClassesByFile(file, packageName + POINT + file.getName()));
+ file.getName())); } else {
} else { try {
try { classes.add(Class.forName(packageName + POINT + file.getName().replace(CLASS, "")));
classes.add(Class.forName(packageName + POINT } catch (ClassNotFoundException e) {
+ file.getName().replace(CLASS, ""))); ;
} catch (ClassNotFoundException e) { }
; }
} }
} }
} return classes;
} }
return classes;
}
/** /**
* 扫描jar包下所有的class对象 * 扫描jar包下所有的class对象
* *
* @param jar * @param jar
* jar包对象 * jar包对象
* @param packageName * @param packageName
* 包的全限类名 * 包的全限类名
* @return * @return
*/ */
private static List<Class<?>> findClassesByJar(JarFile jar, private static List<Class<?>> findClassesByJar(JarFile jar, String packageName) {
String packageName) { List<Class<?>> classes = new ArrayList<Class<?>>();
List<Class<?>> classes = new ArrayList<Class<?>>(); Enumeration<JarEntry> jarEntries = jar.entries();
Enumeration<JarEntry> jarEntries = jar.entries(); while (jarEntries.hasMoreElements()) {
while (jarEntries.hasMoreElements()) { JarEntry jarEntry = jarEntries.nextElement();
JarEntry jarEntry = jarEntries.nextElement(); if (jarEntry.isDirectory()) {
if (jarEntry.isDirectory()) { continue;
continue; }
} String className = jarEntry.getName().replace(File.separator, POINT);
String className = jarEntry.getName() if (!className.startsWith(packageName) || !className.endsWith(CLASS)) {
.replace(File.separator, POINT); continue;
if (!className.startsWith(packageName) }
|| !className.endsWith(CLASS)) { try {
continue; classes.add(Class.forName(className.replace(CLASS, "")));
} } catch (ClassNotFoundException e) {
try { ;
classes.add(Class.forName(className.replace(CLASS, ""))); }
} catch (ClassNotFoundException e) { }
; return classes;
} }
}
return classes;
}
public static Object deepClone(Object obj) throws WeixinException { public static Object deepClone(Object obj) {
ByteArrayOutputStream bos = null; ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null; ObjectOutputStream oos = null;
ByteArrayInputStream bis = null; ByteArrayInputStream bis = null;
ObjectInputStream ois = null; ObjectInputStream ois = null;
try { try {
bos = new ByteArrayOutputStream(); bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos); oos = new ObjectOutputStream(bos);
oos.writeObject(obj); oos.writeObject(obj);
bis = new ByteArrayInputStream(bos.toByteArray()); bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis); ois = new ObjectInputStream(bis);
return ois.readObject(); return ois.readObject();
} catch (IOException e) { } catch (IOException e) {
throw new WeixinException(e); throw new RuntimeException(e);
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
throw new WeixinException(e); throw new RuntimeException(e);
} finally { } finally {
try { try {
if (bos != null) { if (bos != null) {
bos.close(); bos.close();
} }
if (oos != null) { if (oos != null) {
oos.close(); oos.close();
} }
if (bis != null) { if (bis != null) {
bis.close(); bis.close();
} }
if (ois != null) { if (ois != null) {
ois.close(); ois.close();
} }
} catch (IOException e) { } catch (IOException e) {
;// ignore ;// ignore
} }
} }
} }
/** /**
* 获得泛型类型 * 获得泛型类型
* *
* @param object * @param object
* @return * @return
*/ */
public static Class<?> getGenericType(Class<?> clazz) { public static Class<?> getGenericType(Class<?> clazz) {
if(clazz == Object.class){ if (clazz == Object.class) {
return null; return null;
} }
Type type = clazz.getGenericSuperclass(); Type type = clazz.getGenericSuperclass();
if (type instanceof ParameterizedType) { if (type instanceof ParameterizedType) {
ParameterizedType ptype = ((ParameterizedType) type); ParameterizedType ptype = ((ParameterizedType) type);
Type[] args = ptype.getActualTypeArguments(); Type[] args = ptype.getActualTypeArguments();
return (Class<?>) args[0]; return (Class<?>) args[0];
} }
return getGenericType(clazz.getSuperclass()); return getGenericType(clazz.getSuperclass());
} }
public static ClassLoader getDefaultClassLoader() { public static ClassLoader getDefaultClassLoader() {
ClassLoader cl = null; ClassLoader cl = null;
try { try {
cl = Thread.currentThread().getContextClassLoader(); cl = Thread.currentThread().getContextClassLoader();
} catch (Throwable ex) { } catch (Throwable ex) {
// Cannot access thread context ClassLoader - falling back... // Cannot access thread context ClassLoader - falling back...
} }
if (cl == null) { if (cl == null) {
// No thread context class loader -> use class loader of this class. // No thread context class loader -> use class loader of this class.
cl = ClassUtil.class.getClassLoader(); cl = ClassUtil.class.getClassLoader();
if (cl == null) { if (cl == null) {
// getClassLoader() returning null indicates the bootstrap // getClassLoader() returning null indicates the bootstrap
// ClassLoader // ClassLoader
try { try {
cl = ClassLoader.getSystemClassLoader(); cl = ClassLoader.getSystemClassLoader();
} catch (Throwable ex) { } catch (Throwable ex) {
// Cannot access system ClassLoader - oh well, maybe the // Cannot access system ClassLoader - oh well, maybe the
// caller can live with null... // caller can live with null...
} }
} }
} }
return cl; return cl;
} }
public static void main(String[] args) throws WeixinException { public static void main(String[] args) {
System.err.println(getClasses("com.foxinmy.weixin4j.qy.event")); System.err.println(getClasses("com.foxinmy.weixin4j.qy.event"));
} }
} }

View File

@ -7,11 +7,10 @@ import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.SecretKeySpec;
import com.foxinmy.weixin4j.base64.Base64; import com.foxinmy.weixin4j.base64.Base64;
import com.foxinmy.weixin4j.exception.WeixinException;
/** /**
* 消息工具类 * 消息工具类
* *
* @className MessageUtil * @className MessageUtil
* @author jinyu(foxinmy@gmail.com) * @author jinyu(foxinmy@gmail.com)
* @date 2014年10月31日 * @date 2014年10月31日
@ -19,150 +18,137 @@ import com.foxinmy.weixin4j.exception.WeixinException;
* @see * @see
*/ */
public final class MessageUtil { public final class MessageUtil {
/** /**
* 验证微信签名 * 验证微信签名
* *
* @param signature * @param signature
* 微信加密签名signature结合了开发者填写的token参数和请求中的timestamp参数nonce参数 * 微信加密签名signature结合了开发者填写的token参数和请求中的timestamp参数nonce参数
* @return 开发者通过检验signature对请求进行相关校验若确认此次GET请求来自微信服务器 * @return 开发者通过检验signature对请求进行相关校验若确认此次GET请求来自微信服务器
* 请原样返回echostr参数内容则接入生效 成为开发者成功否则接入失败 * 请原样返回echostr参数内容则接入生效 成为开发者成功否则接入失败
* @see <a * @see <a href=
* href="https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421135319&token=&lang=zh_CN">接入指南</a> * "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421135319&token=&lang=zh_CN">接入指南</a>
*/ */
public static String signature(String... para) { public static String signature(String... para) {
Arrays.sort(para); Arrays.sort(para);
StringBuffer sb = new StringBuffer(); StringBuffer sb = new StringBuffer();
for (String str : para) { for (String str : para) {
sb.append(str); sb.append(str);
} }
return ServerToolkits.digestSHA1(sb.toString()); return ServerToolkits.digestSHA1(sb.toString());
} }
/** /**
* 对xml消息加密 * 对xml消息加密
* *
* @param appId * @param appId
* 应用ID * 应用ID
* @param encodingAesKey * @param encodingAesKey
* 加密密钥 * 加密密钥
* @param xmlContent * @param xmlContent
* 原始消息体 * 原始消息体
* @return aes加密后的消息体 * @return aes加密后的消息体
* @throws WeixinException * @throws WeixinException
*/ */
public static String aesEncrypt(String appId, String encodingAesKey, public static String aesEncrypt(String appId, String encodingAesKey, String xmlContent) {
String xmlContent) throws WeixinException { /**
/** * 其中msg_encrypt=Base64_Encode(AES_Encrypt [random(16B)+ msg_len(4B) +
* 其中msg_encrypt=Base64_Encode(AES_Encrypt [random(16B)+ msg_len(4B) + * msg + $AppId])
* msg + $AppId]) *
* * random(16B)为16字节的随机字符串msg_len为msg长度占4个字节(网络字节序)$AppId为公众账号的AppId
* random(16B)为16字节的随机字符串msg_len为msg长度占4个字节(网络字节序)$AppId为公众账号的AppId */
*/ byte[] randomBytes = ServerToolkits.getBytesUtf8(ServerToolkits.generateRandomString(16));
byte[] randomBytes = ServerToolkits.getBytesUtf8(ServerToolkits byte[] xmlBytes = ServerToolkits.getBytesUtf8(xmlContent);
.generateRandomString(16)); int xmlLength = xmlBytes.length;
byte[] xmlBytes = ServerToolkits.getBytesUtf8(xmlContent); byte[] orderBytes = new byte[4];
int xmlLength = xmlBytes.length; orderBytes[3] = (byte) (xmlLength & 0xFF);
byte[] orderBytes = new byte[4]; orderBytes[2] = (byte) (xmlLength >> 8 & 0xFF);
orderBytes[3] = (byte) (xmlLength & 0xFF); orderBytes[1] = (byte) (xmlLength >> 16 & 0xFF);
orderBytes[2] = (byte) (xmlLength >> 8 & 0xFF); orderBytes[0] = (byte) (xmlLength >> 24 & 0xFF);
orderBytes[1] = (byte) (xmlLength >> 16 & 0xFF); byte[] appidBytes = ServerToolkits.getBytesUtf8(appId);
orderBytes[0] = (byte) (xmlLength >> 24 & 0xFF);
byte[] appidBytes = ServerToolkits.getBytesUtf8(appId);
int byteLength = randomBytes.length + xmlLength + orderBytes.length int byteLength = randomBytes.length + xmlLength + orderBytes.length + appidBytes.length;
+ appidBytes.length; // ... + pad: 使用自定义的填充方式对明文进行补位填充
// ... + pad: 使用自定义的填充方式对明文进行补位填充 byte[] padBytes = PKCS7Encoder.encode(byteLength);
byte[] padBytes = PKCS7Encoder.encode(byteLength); // random + endian + xml + appid + pad 获得最终的字节流
// random + endian + xml + appid + pad 获得最终的字节流 byte[] unencrypted = new byte[byteLength + padBytes.length];
byte[] unencrypted = new byte[byteLength + padBytes.length]; byteLength = 0;
byteLength = 0; // src:源数组;srcPos:源数组要复制的起始位置;dest:目的数组;destPos:目的数组放置的起始位置;length:复制的长度
// src:源数组;srcPos:源数组要复制的起始位置;dest:目的数组;destPos:目的数组放置的起始位置;length:复制的长度 System.arraycopy(randomBytes, 0, unencrypted, byteLength, randomBytes.length);
System.arraycopy(randomBytes, 0, unencrypted, byteLength, byteLength += randomBytes.length;
randomBytes.length); System.arraycopy(orderBytes, 0, unencrypted, byteLength, orderBytes.length);
byteLength += randomBytes.length; byteLength += orderBytes.length;
System.arraycopy(orderBytes, 0, unencrypted, byteLength, System.arraycopy(xmlBytes, 0, unencrypted, byteLength, xmlBytes.length);
orderBytes.length); byteLength += xmlBytes.length;
byteLength += orderBytes.length; System.arraycopy(appidBytes, 0, unencrypted, byteLength, appidBytes.length);
System.arraycopy(xmlBytes, 0, unencrypted, byteLength, xmlBytes.length); byteLength += appidBytes.length;
byteLength += xmlBytes.length; System.arraycopy(padBytes, 0, unencrypted, byteLength, padBytes.length);
System.arraycopy(appidBytes, 0, unencrypted, byteLength, try {
appidBytes.length); byte[] aesKey = Base64.decodeBase64(encodingAesKey + "=");
byteLength += appidBytes.length; // 设置加密模式为AES的CBC模式
System.arraycopy(padBytes, 0, unencrypted, byteLength, padBytes.length); Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
try { SecretKeySpec keySpec = new SecretKeySpec(aesKey, ServerToolkits.AES);
byte[] aesKey = Base64.decodeBase64(encodingAesKey + "="); IvParameterSpec iv = new IvParameterSpec(aesKey, 0, 16);
// 设置加密模式为AES的CBC模式 cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); // 加密
SecretKeySpec keySpec = new SecretKeySpec(aesKey, ServerToolkits.AES); byte[] encrypted = cipher.doFinal(unencrypted);
IvParameterSpec iv = new IvParameterSpec(aesKey, 0, 16); // 使用BASE64对加密后的字符串进行编码
cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv); // return Base64.encodeBase64String(encrypted);
// 加密 return Base64.encodeBase64String(encrypted);
byte[] encrypted = cipher.doFinal(unencrypted); } catch (Exception e) {
// 使用BASE64对加密后的字符串进行编码 throw new RuntimeException("-40006,AES加密失败:" + e.getMessage());
// return Base64.encodeBase64String(encrypted); }
return Base64 }
.encodeBase64String(encrypted);
} catch (Exception e) {
throw new WeixinException("-40006", "AES加密失败:" + e.getMessage());
}
}
/** /**
* 对AES消息解密 * 对AES消息解密
* *
* @param appId * @param appId
* @param encodingAesKey * @param encodingAesKey
* aes加密的密钥 * aes加密的密钥
* @param encryptContent * @param encryptContent
* 加密的消息体 * 加密的消息体
* @return 解密后的字符 * @return 解密后的字符
* @throws WeixinException * @throws WeixinException
*/ */
public static String aesDecrypt(String appId, String encodingAesKey, public static String aesDecrypt(String appId, String encodingAesKey, String encryptContent) {
String encryptContent) throws WeixinException { byte[] aesKey = Base64.decodeBase64(encodingAesKey + "=");
byte[] aesKey = Base64.decodeBase64(encodingAesKey + "="); byte[] original;
byte[] original; try {
try { // 设置解密模式为AES的CBC模式
// 设置解密模式为AES的CBC模式 Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); SecretKeySpec key_spec = new SecretKeySpec(aesKey, ServerToolkits.AES);
SecretKeySpec key_spec = new SecretKeySpec(aesKey, ServerToolkits.AES); IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16));
IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, cipher.init(Cipher.DECRYPT_MODE, key_spec, iv);
0, 16)); // 使用BASE64对密文进行解码
cipher.init(Cipher.DECRYPT_MODE, key_spec, iv); byte[] encrypted = Base64.decodeBase64(encryptContent);
// 使用BASE64对密文进行解码 // 解密
byte[] encrypted = Base64.decodeBase64(encryptContent); original = cipher.doFinal(encrypted);
// 解密 } catch (Exception e) {
original = cipher.doFinal(encrypted); throw new RuntimeException("-40007,AES解密失败:" + e.getMessage());
} catch (Exception e) { }
throw new WeixinException("-40007", "AES解密失败:" + e.getMessage()); String xmlContent, fromAppId;
} try {
String xmlContent, fromAppId; // 去除补位字符
try { byte[] bytes = PKCS7Encoder.decode(original);
// 去除补位字符 /**
byte[] bytes = PKCS7Encoder.decode(original); * AES加密的buf由16个字节的随机字符串4个字节的msg_len(网络字节序)msg和$AppId组成
/** * 其中msg_len为msg的长度$AppId为公众帐号的AppId
* AES加密的buf由16个字节的随机字符串4个字节的msg_len(网络字节序)msg和$AppId组成 */
* 其中msg_len为msg的长度$AppId为公众帐号的AppId // 获取表示xml长度的字节数组
*/ byte[] lengthByte = Arrays.copyOfRange(bytes, 16, 20);
// 获取表示xml长度的字节数组 // 获取xml消息主体的长度(byte[]2int)
byte[] lengthByte = Arrays.copyOfRange(bytes, 16, 20); // http://my.oschina.net/u/169390/blog/97495
// 获取xml消息主体的长度(byte[]2int) int xmlLength = lengthByte[3] & 0xff | (lengthByte[2] & 0xff) << 8 | (lengthByte[1] & 0xff) << 16
// http://my.oschina.net/u/169390/blog/97495 | (lengthByte[0] & 0xff) << 24;
int xmlLength = lengthByte[3] & 0xff | (lengthByte[2] & 0xff) << 8 xmlContent = ServerToolkits.newStringUtf8(Arrays.copyOfRange(bytes, 20, 20 + xmlLength));
| (lengthByte[1] & 0xff) << 16 fromAppId = ServerToolkits.newStringUtf8(Arrays.copyOfRange(bytes, 20 + xmlLength, bytes.length));
| (lengthByte[0] & 0xff) << 24; } catch (Exception e) {
xmlContent = ServerToolkits.newStringUtf8(Arrays.copyOfRange(bytes, 20, throw new RuntimeException("-40008,xml内容不合法:" + e.getMessage());
20 + xmlLength)); }
fromAppId = ServerToolkits.newStringUtf8(Arrays.copyOfRange(bytes, // 校验appId是否一致
20 + xmlLength, bytes.length)); if (appId != null && !fromAppId.trim().equals(appId)) {
} catch (Exception e) { throw new RuntimeException("-40005,校验AppID失败,expect " + appId + ",but actual is " + fromAppId);
throw new WeixinException("-40008", "xml内容不合法:" + e.getMessage()); }
} return xmlContent;
// 校验appId是否一致 }
if (appId != null && !fromAppId.trim().equals(appId)) {
throw new WeixinException("-40005", "校验AppID失败,expect " + appId
+ ",but actual is " + fromAppId);
}
return xmlContent;
}
} }

View File

@ -2,7 +2,6 @@ package com.foxinmy.weixin4j.server.ext;
import java.util.Set; import java.util.Set;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.handler.WeixinMessageHandler; import com.foxinmy.weixin4j.handler.WeixinMessageHandler;
import com.foxinmy.weixin4j.qy.suite.SuiteEventType; import com.foxinmy.weixin4j.qy.suite.SuiteEventType;
import com.foxinmy.weixin4j.qy.suite.SuiteMessage; import com.foxinmy.weixin4j.qy.suite.SuiteMessage;
@ -13,7 +12,7 @@ import com.foxinmy.weixin4j.response.WeixinResponse;
/** /**
* 企业号套件消息处理 * 企业号套件消息处理
* *
* @className SuiteMessageHandler * @className SuiteMessageHandler
* @author jy * @author jy
* @date 2015年6月25日 * @date 2015年6月25日
@ -21,29 +20,27 @@ import com.foxinmy.weixin4j.response.WeixinResponse;
*/ */
public class SuiteMessageHandler implements WeixinMessageHandler { public class SuiteMessageHandler implements WeixinMessageHandler {
@Override @Override
public boolean canHandle(WeixinRequest request, WeixinMessage message, Set<String> nodeNames) public boolean canHandle(WeixinRequest request, WeixinMessage message, Set<String> nodeNames) {
throws WeixinException { return nodeNames.contains("suiteid");
return nodeNames.contains("suiteid"); }
}
@Override @Override
public WeixinResponse doHandle(WeixinRequest request, WeixinMessage message, Set<String> nodeNames) public WeixinResponse doHandle(WeixinRequest request, WeixinMessage message, Set<String> nodeNames) {
throws WeixinException { SuiteMessage suiteMessage = null; // 转换为 SuiteMessage
SuiteMessage suiteMessage = null; // 转换为 SuiteMessage SuiteEventType eventType = suiteMessage.getFormatEventType();
SuiteEventType eventType = suiteMessage.getFormatEventType(); if (eventType == SuiteEventType.suite_ticket) {
if (eventType == SuiteEventType.suite_ticket) { // do something
// do something } else if (eventType == SuiteEventType.change_auth) {
} else if (eventType == SuiteEventType.change_auth) { // do something
// do something } else if (eventType == SuiteEventType.cancel_auth) {
} else if (eventType == SuiteEventType.cancel_auth) { // do something
// do something }
} return BlankResponse.global;
return BlankResponse.global; }
}
@Override @Override
public int weight() { public int weight() {
return 0; return 0;
} }
} }

View File

@ -1,13 +1,10 @@
package com.foxinmy.weixin4j.server.test; package com.foxinmy.weixin4j.server.test;
import io.netty.channel.ChannelHandlerContext;
import java.util.Set; import java.util.Set;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import com.foxinmy.weixin4j.dispatcher.BeanFactory; import com.foxinmy.weixin4j.dispatcher.BeanFactory;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.handler.DebugMessageHandler; import com.foxinmy.weixin4j.handler.DebugMessageHandler;
import com.foxinmy.weixin4j.handler.MessageHandlerAdapter; import com.foxinmy.weixin4j.handler.MessageHandlerAdapter;
import com.foxinmy.weixin4j.handler.MultipleMessageHandlerAdapter; import com.foxinmy.weixin4j.handler.MultipleMessageHandlerAdapter;
@ -23,6 +20,8 @@ import com.foxinmy.weixin4j.response.WeixinResponse;
import com.foxinmy.weixin4j.spring.SpringBeanFactory; import com.foxinmy.weixin4j.spring.SpringBeanFactory;
import com.foxinmy.weixin4j.startup.WeixinServerBootstrap; import com.foxinmy.weixin4j.startup.WeixinServerBootstrap;
import io.netty.channel.ChannelHandlerContext;
/** /**
* 服务启动测试类 * 服务启动测试类
* *
@ -34,139 +33,123 @@ import com.foxinmy.weixin4j.startup.WeixinServerBootstrap;
*/ */
public class MessageServerStartup { public class MessageServerStartup {
// 公众号ID // 公众号ID
final String weixinId = "wx4ab8f8de58159a57"; final String weixinId = "wx4ab8f8de58159a57";
// 开发者token // 开发者token
final String token = "weixin4j"; final String token = "weixin4j";
// AES密钥(安全模式) // AES密钥(安全模式)
final String aesKey = ""; final String aesKey = "";
/** /**
* 调试输出用户发来的消息 * 调试输出用户发来的消息
* *
* @throws WeixinException */
*/ public void test1() {
public void test1() throws WeixinException { // 明文模式
// 明文模式 new WeixinServerBootstrap(token).addHandler(DebugMessageHandler.global).startup();
new WeixinServerBootstrap(token).addHandler(DebugMessageHandler.global) // 密文模式
.startup(); // new WeixinServerBootstrap(weixinId, token, aesKey).addHandler(
// 密文模式 // DebugMessageHandler.global).startup();
// new WeixinServerBootstrap(weixinId, token, aesKey).addHandler( }
// DebugMessageHandler.global).startup();
}
/** /**
* 针对特定消息类型 * 针对特定消息类型
* *
* @throws WeixinException */
*/ public void test2() {
public void test2() throws WeixinException { // 针对文本消息回复
// 针对文本消息回复 WeixinMessageHandler textMessageHandler = new MessageHandlerAdapter<TextMessage>() {
WeixinMessageHandler textMessageHandler = new MessageHandlerAdapter<TextMessage>() { @Override
@Override public WeixinResponse doHandle0(WeixinRequest request, TextMessage message) {
public WeixinResponse doHandle0(WeixinRequest request, return new TextResponse("HelloWorld!");
TextMessage message) throws WeixinException { }
return new TextResponse("HelloWorld!"); };
} // 针对语音消息回复
}; WeixinMessageHandler voiceMessageHandler = new MessageHandlerAdapter<VoiceMessage>() {
// 针对语音消息回复 @Override
WeixinMessageHandler voiceMessageHandler = new MessageHandlerAdapter<VoiceMessage>() { public WeixinResponse doHandle0(WeixinRequest request, VoiceMessage message) {
@Override return new TextResponse("HelloWorld!");
public WeixinResponse doHandle0(WeixinRequest request, }
VoiceMessage message) throws WeixinException { };
return new TextResponse("HelloWorld!"); // 当消息类型为文本(text)或者语音时回复HelloWorld, 否则回复调试消息
} new WeixinServerBootstrap(weixinId, token, aesKey)
}; .addHandler(textMessageHandler, voiceMessageHandler, DebugMessageHandler.global).startup();
// 当消息类型为文本(text)或者语音时回复HelloWorld, 否则回复调试消息 }
new WeixinServerBootstrap(weixinId, token, aesKey).addHandler(
textMessageHandler, voiceMessageHandler,
DebugMessageHandler.global).startup();
}
/** /**
* 多种消息类型处理 * 多种消息类型处理
* *
* @throws WeixinException */
*/ public void test3() {
public void test3() throws WeixinException { @SuppressWarnings("unchecked")
@SuppressWarnings("unchecked") MultipleMessageHandlerAdapter messageHandler = new MultipleMessageHandlerAdapter(ScanEventMessage.class,
MultipleMessageHandlerAdapter messageHandler = new MultipleMessageHandlerAdapter( TextMessage.class) {
ScanEventMessage.class, TextMessage.class) { @Override
@Override public WeixinResponse doHandle(WeixinRequest request, WeixinMessage message, Set<String> nodeNames) {
public WeixinResponse doHandle(WeixinRequest request, return new TextResponse("处理了扫描和文字消息");
WeixinMessage message, Set<String> nodeNames) }
throws WeixinException { };
return new TextResponse("处理了扫描和文字消息"); new WeixinServerBootstrap(token).addHandler(messageHandler, DebugMessageHandler.global).startup();
} }
};
new WeixinServerBootstrap(token).addHandler(messageHandler,
DebugMessageHandler.global).startup();
}
/** /**
* 扫描包添加handler * 扫描包添加handler
* *
* @throws WeixinException * @
*/ */
public void test4() throws WeixinException { public void test4() {
// handler处理所在的包名(子包也会扫描) // handler处理所在的包名(子包也会扫描)
String packageToScan = "com.foxinmy.weixin4j.handler"; String packageToScan = "com.foxinmy.weixin4j.handler";
// handler默认使用 Class.newInstance // handler默认使用 Class.newInstance
// 方式实例化,如果handler中含有service等类需要注入,可以声明一个BeanFactory,如SpringBeanFactory // 方式实例化,如果handler中含有service等类需要注入,可以声明一个BeanFactory,如SpringBeanFactory
ApplicationContext applicationContext = null; // spring容器 ApplicationContext applicationContext = null; // spring容器
BeanFactory beanFactory = new SpringBeanFactory(applicationContext); BeanFactory beanFactory = new SpringBeanFactory(applicationContext);
new WeixinServerBootstrap(token).handlerPackagesToScan(packageToScan) new WeixinServerBootstrap(token).handlerPackagesToScan(packageToScan).openAlwaysResponse()
.openAlwaysResponse().resolveBeanFactory(beanFactory).startup(); .resolveBeanFactory(beanFactory).startup();
} }
/** /**
* 拦截器应用 * 拦截器应用
* *
* @throws WeixinException * @
*/ */
public void test5() throws WeixinException { public void test5() {
// 拦截所有请求 // 拦截所有请求
WeixinMessageInterceptor interceptor = new WeixinMessageInterceptor() { WeixinMessageInterceptor interceptor = new WeixinMessageInterceptor() {
@Override @Override
public boolean preHandle(ChannelHandlerContext context, public boolean preHandle(ChannelHandlerContext context, WeixinRequest request, WeixinMessage message,
WeixinRequest request, WeixinMessage message, WeixinMessageHandler handler) {
WeixinMessageHandler handler) throws WeixinException { context.writeAndFlush(new TextResponse("所有消息被拦截了!"));
context.writeAndFlush(new TextResponse("所有消息被拦截了!")); return false;
return false; }
}
@Override @Override
public void postHandle(ChannelHandlerContext context, public void postHandle(ChannelHandlerContext context, WeixinRequest request, WeixinResponse response,
WeixinRequest request, WeixinResponse response, WeixinMessage message, WeixinMessageHandler handler) {
WeixinMessage message, WeixinMessageHandler handler) System.err.println("preHandle返回为true,执行handler后");
throws WeixinException { }
System.err.println("preHandle返回为true,执行handler后");
}
@Override @Override
public void afterCompletion(ChannelHandlerContext context, public void afterCompletion(ChannelHandlerContext context, WeixinRequest request, WeixinResponse response,
WeixinRequest request, WeixinResponse response, WeixinMessage message, WeixinMessageHandler handler, Exception exception) {
WeixinMessage message, WeixinMessageHandler handler, System.err.println("请求处理完毕");
Exception exception) throws WeixinException { }
System.err.println("请求处理完毕");
}
@Override @Override
public int weight() { public int weight() {
return 0; return 0;
} }
}; };
new WeixinServerBootstrap(token).addInterceptor(interceptor) new WeixinServerBootstrap(token).addInterceptor(interceptor).openAlwaysResponse().startup();
.openAlwaysResponse().startup(); }
}
/** /**
* main方法入口 * main方法入口
* *
* @param args * @param args
* @throws Exception * @throws Exception
*/ */
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
new MessageServerStartup().test1(); new MessageServerStartup().test1();
} }
} }