java实战实现网页版微信公众号实时聊天客户端
为了给微信公众号提供智能聊天功能,突然想到了网站上也可以放一个聊天窗口增加一个互动渠道。说干就干,下面是中间层的实现:
做过微信开发的都知道,微信公众号都有一个后端(第三方服务器),这里实现的是连接微信客户端(这里应该是浏览器)与微信公众号后端(第三方服务器)的中间层。
因业余时间少,实现过程比较潦草,见谅!
import java.io.IOException;
import java.io.StringReader;
import java.net.URLEncoder;
import java.util.Date;
import java.util.Iterator;
import java.util.Random;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import com.qq.weixin.mp.aes.AesException;
import com.qq.weixin.mp.aes.WXBizMsgCrypt;
import com.tomrrow.collect.Collecter;
import com.tomrrow.collect.config.CollectConfig;
import com.tomrrow.collect.config.RuleConfig;
import com.tomrrow.collect.util.HttpUtil;
import com.tomrrow.collect.util.IpUtil;
import com.tomrrow.collect.util.ParseString;
/**
* 聊天消息中转处理(连接客户端与机器人服务器的中间层)
* @author xujy
*
*/
public class ChatHandler extends HttpServlet{
private static final long serialVersionUID = -7651922695205287079L;
// 动态生成请求服务器时参数key,防止跨域请求
public static final String reqTokenKey = "token";
/** 是否代理模式*/
public static boolean isAgentMode = false;
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException{
System.out.println("非法请求"+IpUtil.getIpAddr(req));
//this.doPost(req, resp);
}
public void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException{
// 时间戳
String timestamp = System.currentTimeMillis() + "";
// 现时标志
String nonce = System.currentTimeMillis() + "";
// 网页版监控session
//HttpSession session = req.getSession();
String ip = IpUtil.getIpAddr(req);
String fromUsername = ip == null || ip.isEmpty() ? "unknown" : ip;
String toUsername = "xxxxx888888"; // 服务器公众号的openid
HttpSession session = SessionManger.getSession(fromUsername);
//Object token = null;
if (session!=null && session.getId()!=null){
// 阻止短时间连续提交
Object subtime = session.getAttribute("subtime");
String temp = null;
if ((temp = ParseString.nvl(subtime)).isEmpty()){
session.setAttribute("subtime", System.currentTimeMillis());
}else{
long t2 = System.currentTimeMillis();
session.setAttribute("subtime", t2);
long t1 = Long.parseLong(temp);
if (t2 – t1 < 2000){ //两次间隔不能少于2s
resp.setContentType("text/html;charset=utf-8");
resp.getWriter().print("您说话太快,请慢点!");
return;
}
}
//token = session.getAttribute(ChatHandler.reqTokenKey);
}
WXBizMsgCrypt pc = null;
String postStr = null;
String question = null;
String temp = null;
try{
// 读取用户输入
question = req.getParameter("content");
try {
if (question == null)
return;
postStr = ChatHandler.agentHandle(fromUsername, toUsername, question, resp);
temp = postStr; // 代理处理结果
if (postStr.isEmpty())
return;
pc = new WXBizMsgCrypt(WeiXinHandler.TOKEN, WeiXinHandler.encodingAesKey, WeiXinHandler.appId);
} catch (AesException e2) {
e2.printStackTrace();
}
// 加密 并 转发
postStr = this.handleMsgText(postStr, fromUsername, toUsername, timestamp, WeiXinHandler.msgTypeText, timestamp);
postStr = pc.encryptMsg(postStr, timestamp, nonce);
Document document=null;
try{
document = DocumentHelper.parseText(postStr);
}catch(Exception e){
e.printStackTrace();
}
if(null==document){
resp.getWriter().print("");
return;
}
Element root = null;
// 解密
try {
root = document.getRootElement();
// 安全签名
String msg_signature = root.elementTextTrim("MsgSignature");
// 密文封装
postStr = secretHandle(toUsername, postStr);
//String msg_signature = WeiXinHandler.generateSignature(timestamp, nonce);
// 相当于微信平台里设置的服务器ip,这里虽然是同一个服务器仍然采用远程访问的方式以支持分布式部署
String strUrl = "https://yourserver/wx-interface?msg_signature="+msg_signature+"×tamp="+timestamp+"&nonce="+nonce;
postStr = HttpUtil.sendRequest(strUrl, postStr);
if (isCommandMode || !question.equals(temp))
return; // 代理模式这里返回
} catch (Exception e){
e.printStackTrace();
}
}catch(Exception e){
e.printStackTrace();
}
// 接收反馈
if (null!=postStr&&!postStr.isEmpty()){
Document document=null;
try{
document = DocumentHelper.parseText(postStr);
}catch(Exception e){
e.printStackTrace();
}
if(null==document){
resp.getWriter().print("");
return;
}
Element root = null;
// 解密
try {
root = document.getRootElement();
// 安全签名
String msgSignature = root.elementTextTrim("MsgSignature");
// 回馈密文封装
try {
postStr = secretHandle(fromUsername, postStr);
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
}
if (postStr==null || postStr.isEmpty())
return;
// 解密并发给客户端
postStr = pc.decryptMsg(msgSignature, timestamp, nonce, postStr);
try{
document = DocumentHelper.parseText(postStr);
root = document.getRootElement();
}catch(Exception e){
e.printStackTrace();
}
if(null==document){
resp.getWriter().print("");
return;
}
String respStr = "";
String msgType = root.elementText("MsgType");
if (WeiXinHandler.msgTypeText.equals(msgType))
respStr = root.elementTextTrim("Content");
else if (WeiXinHandler.msgTypeArticle.equals(msgType)){
Element articles = root.element("Articles");
Iterator iter = articles.elementIterator();
StringBuilder sb = new StringBuilder();
while (iter.hasNext()){
Element item = (Element) iter.next();
for (Iterator it = item.elementIterator(); it.hasNext();){
Element at = (Element) it.next();
String name = at.getName();
String value = at.getText();
if ("Url".equals(name)){
sb.append("<a href=\"javascript:void(0);\" onclick=\"javascript: window.open(‘").append(value).append("’,’newwindow’,’height=500,width=700′);return false;\" >详情</a><br>");
}else{
sb.append(value).append("<br>");
}
}
}
respStr = sb.toString();
}else{
respStr = "暂未实现";
}
resp.setContentType("text/html;charset=utf-8");
resp.getWriter().print(respStr);
} catch (AesException e1) {
e1.printStackTrace();
}
}else{
resp.setContentType("text/html;charset=utf-8");
resp.getWriter().print("sorry, 网络超时^^");
}
}
/**
* 密文封装后才能发给第三方服务器解密
* @param toUser
* @param postStr
* @return
* @throws ParserConfigurationException
* @throws SAXException
* @throws IOException
*/
private String secretHandle(String toUser, String postStr) throws ParserConfigurationException, SAXException, IOException {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
StringReader sr = new StringReader(postStr);
InputSource is = new InputSource(sr);
org.w3c.dom.Document document = db.parse(is);
org.w3c.dom.Element root = document.getDocumentElement();
NodeList nodelist1 = root.getElementsByTagName("Encrypt");
String encrypt = nodelist1.item(0).getTextContent();
String format = "<xml><ToUserName><![CDATA[%1$s]]></ToUserName><Encrypt><![CDATA[%2$s]]></Encrypt></xml>";
String fromXML = String.format(format, toUser, encrypt);
return fromXML;
}
/**
* 处理用户发来的文本消息成给服务器的格式
* @param contentStr
* @param fromUsername
* @param toUsername
* @param time
* @param msgType
* @return
*/
private String handleMsgText(String contentStr, String fromUsername, String toUsername, String time, String msgType, String msgId){
// 与响应用户的消息格式区别在于 touser 和 fromuser的顺序不同
String textTpl = "<xml><ToUserName><![CDATA[%1$s]]></ToUserName><FromUserName><![CDATA[%2$s]]></FromUserName><CreateTime>%3$s</CreateTime><MsgType><![CDATA[%4$s]]></MsgType><Content><![CDATA[%5$s]]></Content><MsgId>%6$s</MsgId></xml>";
String resultStr = String.format(textTpl, toUsername, fromUsername, time, msgType, contentStr, msgId);
return resultStr;
}
/**
* 代理处理
* @param question
* @param resp
* @return
* @throws IOException
*/
public static String agentHandle(String fromUsername, String toUsername, String question, HttpServletResponse resp) throws IOException{
String postStr = question;
resp.setContentType("text/html;charset=utf-8");
if (isAgentMode && postStr.equals("*****")){
System.out.println("ok, 退出代理模式!");
isAgentMode = false;
isCommandMode = true;
resp.getWriter().print("My lord!已退出,你懂得!^^");
return "";
}
if (postStr.equals("*####")){
System.out.println("ok, 开启代理模式!");
isAgentMode = true;
isCommandMode = true;
resp.getWriter().print("My lord!已开启,你懂得!^^");
return "";
}
isCommandMode = false;
if (WeiXinHandler.isStudyComma(question))
return question;
if (isAgentMode){
String keyword = URLEncoder.encode(postStr, "UTF-8");
String strUrl = "https://核心处理服务url?keyword="+keyword;
if (!postStr.replaceAll(" ", "").matches("^[a-zA-Z]+[‘‘\\.-|!!]*[a-zA-Z]*$")){// 这个代理不会英文
try{
postStr = Collecter.readStream(strUrl, true, "UTF-8").toString();
if (postStr!=null && !postStr.isEmpty()){
resp.getWriter().print(postStr);
// 记录聊天日志
ChatLog.logRecord(new Object[]{fromUsername,"",toUsername,question,postStr});
postStr = "!"+question + "@" + postStr;
}
}catch (Exception e){
e.printStackTrace();
}
}
}
return postStr.isEmpty() ? question : postStr;
}
/** 表示当前是命令会话模式,不切入机器人*/
public static boolean isCommandMode = false;
/**
* 代理处理
* @param question
* @param resp
* @return
* @throws IOException
*/
public static String agentHandle(String question) throws IOException{
String postStr = question;
if (isAgentMode && postStr.equals("*****")){
System.out.println("ok, 退出代理模式!");
isAgentMode = false;
isCommandMode = true;
return "My lord!已退出,你懂得!^^";
}
if (postStr.equals("*####")){
System.out.println("ok, 开启代理模式!");
isAgentMode = true;
isCommandMode = true;
return "My lord!已开启,你懂得!^^";
}
isCommandMode = false;
if (WeiXinHandler.isStudyComma(question))
return question;
if (isAgentMode){
String keyword = URLEncoder.encode(postStr, "UTF-8");
String strUrl = "https://核心处理服务url?keyword="+keyword;
if (!postStr.replaceAll(" ", "").matches("^[a-zA-Z]+[‘‘\\.-|!!]*[a-zA-Z]*$")){// 这个代理不会英文
try{
postStr = Collecter.readStream(strUrl, true, "UTF-8").toString();
if (postStr!=null && !postStr.isEmpty()){
postStr = "!"+question + "@" + postStr;
}
} catch (Exception e){
e.printStackTrace();
}
}
}
return postStr.isEmpty() ? question : postStr;
}
}
关注一下看看效果(也可以微信搜账号:致儒先生):
声明: 除非转自他站(如有侵权,请联系处理)外,本文采用 BY-NC-SA 协议进行授权 | 嗅谱网
转载请注明:转自《java实战实现网页版微信公众号实时聊天客户端》
本文地址:http://www.xiupu.net/archives-7120.html
关注公众号:
微信赞赏
支付宝赞赏