一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|編程技術|正則表達式|

服務器之家 - 編程語言 - JAVA教程 - JAVA實現 springMVC方式的微信接入、實現消息自動回復實例

JAVA實現 springMVC方式的微信接入、實現消息自動回復實例

2020-07-11 12:02liliangel JAVA教程

本篇文章主要介紹了JAVA實現 springMVC方式的微信接入、實現消息自動回復,這里整理了詳細的代碼,有需要的小伙伴可以參考下。

前段時間小忙了一陣,微信公眾號的開發,從零開始看文檔,踩了不少坑,也算是熬過來了,最近考慮做一些總結,方便以后再開發的時候回顧,也給正在做相關項目的同學做個參考。

1.思路

微信接入:用戶消息和開發者需要的事件推送都會通過微信方服務器發起一個請求,轉發到你在公眾平臺配置的服務器url地址,微信方將帶上signature,timestamp,nonce,echostr四個參數,我們自己服務器通過拼接公眾平臺配置的token,以及傳上來的timestamp,nonce進行SHA1加密后匹配signature,返回ture說明接入成功。

JAVA實現 springMVC方式的微信接入、實現消息自動回復實例

消息回復:當用戶給公眾號發送消息時,微信服務器會將用戶消息以xml格式通過POST請求到我們配置好的服務器對應的接口,而我們要做的事情就是根據消息類型等做相應的邏輯處理,并將最后的返回結果也通過xml格式return給微信服務器,微信方再傳達給用戶的這樣一個過程。 

JAVA實現 springMVC方式的微信接入、實現消息自動回復實例

1.公眾平臺配置

JAVA實現 springMVC方式的微信接入、實現消息自動回復實例

2.Controller

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
@Controller
@RequestMapping("/wechat")
publicclass WechatController {
  @Value("${DNBX_TOKEN}")
  private String DNBX_TOKEN;
  
  private static final Logger LOGGER = LoggerFactory.getLogger(WechatController.class);
  
  @Resource
  WechatService wechatService;
  
  /**
   * 微信接入
   * @param wc
   * @return
   * @throws IOException
   */
  @RequestMapping(value="/connect",method = {RequestMethod.GET, RequestMethod.POST})
  @ResponseBody
  publicvoid connectWeixin(HttpServletRequest request, HttpServletResponse response) throws IOException{
    // 將請求、響應的編碼均設置為UTF-8(防止中文亂碼)
    request.setCharacterEncoding("UTF-8"); //微信服務器POST消息時用的是UTF-8編碼,在接收時也要用同樣的編碼,否則中文會亂碼;
    response.setCharacterEncoding("UTF-8"); //在響應消息(回復消息給用戶)時,也將編碼方式設置為UTF-8,原理同上;boolean isGet = request.getMethod().toLowerCase().equals("get");
   
    PrintWriter out = response.getWriter();
     
    try {
      if (isGet) {
        String signature = request.getParameter("signature");// 微信加密簽名
        String timestamp = request.getParameter("timestamp");// 時間戳
        String nonce = request.getParameter("nonce");// 隨機數
        String echostr = request.getParameter("echostr");//隨機字符串
        
        // 通過檢驗signature對請求進行校驗,若校驗成功則原樣返回echostr,表示接入成功,否則接入失敗 if (SignUtil.checkSignature(DNBX_TOKEN, signature, timestamp, nonce)) {
          LOGGER.info("Connect the weixin server is successful.");
          response.getWriter().write(echostr);
        } else {
          LOGGER.error("Failed to verify the signature!");
        }
      }else{
        String respMessage = "異常消息!";
        
        try {
          respMessage = wechatService.weixinPost(request);
          out.write(respMessage);
          LOGGER.info("The request completed successfully");
          LOGGER.info("to weixin server "+respMessage);
        } catch (Exception e) {
          LOGGER.error("Failed to convert the message from weixin!");
        }
        
      }
    } catch (Exception e) {
      LOGGER.error("Connect the weixin server is error.");
    }finally{
      out.close();
    }
  }
}

3.簽名驗證 checkSignature

從上面的controller我們可以看到,我封裝了一個工具類SignUtil,調用了里面的一個叫checkSignature,傳入了四個值,DNBX_TOKEN, signature, timestamp, nonce。這個過程非常重要,其實我們可以理解為將微信傳過來的值進行一個加解密的過程,很多大型的項目所有的接口為保證安全性都會有這樣一個驗證的過程。DNBX_TOKEN我們在微信公眾平臺配置的一個token字符串,主意保密哦!其他三個都是微信服務器發送get請求傳過來的參數,我們進行一層sha1加密:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class SignUtil {
 
  /**
   * 驗證簽名
   *
   * @param token 微信服務器token,在env.properties文件中配置的和在開發者中心配置的必須一致
   * @param signature 微信服務器傳過來sha1加密的證書簽名
   * @param timestamp 時間戳
   * @param nonce 隨機數
   * @return
   */
  public static boolean checkSignature(String token,String signature, String timestamp, String nonce) {
    String[] arr = new String[] { token, timestamp, nonce };
    // 將token、timestamp、nonce三個參數進行字典序排序
    Arrays.sort(arr);
    
    // 將三個參數字符串拼接成一個字符串進行sha1加密
    String tmpStr = SHA1.encode(arr[0] + arr[1] + arr[2]);
    
    // 將sha1加密后的字符串可與signature對比,標識該請求來源于微信
    return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false;
  }
  
}

SHA1:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/**
 * 微信公眾平臺(JAVA) SDK
 *
 * SHA1算法
 *
 * @author helijun 2016/06/15 19:49
 */
public final class SHA1 {
 
  private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5',
              '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
 
  /**
   * Takes the raw bytes from the digest and formats them correct.
   *
   * @param bytes the raw bytes from the digest.
   * @return the formatted bytes.
   */
  private static String getFormattedText(byte[] bytes) {
    int len = bytes.length;
    StringBuilder buf = new StringBuilder(len * 2);
    // 把密文轉換成十六進制的字符串形式
    for (int j = 0; j < len; j++) {
      buf.append(HEX_DIGITS[(bytes[j] >> 4) & 0x0f]);
      buf.append(HEX_DIGITS[bytes[j] & 0x0f]);
    }
    return buf.toString();
  }
 
  public static String encode(String str) {
    if (str == null) {
      return null;
    }
    try {
      MessageDigest messageDigest = MessageDigest.getInstance("SHA1");
      messageDigest.update(str.getBytes());
      return getFormattedText(messageDigest.digest());
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }
}

當你在公眾平臺提交保存,并且看到綠色的提示“接入成功”之后,恭喜你已經完成微信接入。這個過程需要細心一點,注意加密算法里的大小寫,如果接入不成功,大多數情況都是加密算法的問題,多檢查檢查。

4. 實現消息自動回復service

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/**
   * 處理微信發來的請求
   *
   * @param request
   * @return
   */
  public String weixinPost(HttpServletRequest request) {
    String respMessage = null;
    try {
 
      // xml請求解析
      Map<String, String> requestMap = MessageUtil.xmlToMap(request);
 
      // 發送方帳號(open_id)
      String fromUserName = requestMap.get("FromUserName");
      // 公眾帳號
      String toUserName = requestMap.get("ToUserName");
      // 消息類型
      String msgType = requestMap.get("MsgType");
      // 消息內容
      String content = requestMap.get("Content");
      
      LOGGER.info("FromUserName is:" + fromUserName + ", ToUserName is:" + toUserName + ", MsgType is:" + msgType);
 
      // 文本消息
      if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_TEXT)) {
        //這里根據關鍵字執行相應的邏輯,只有你想不到的,沒有做不到的
        if(content.equals("xxx")){
          
        }
        
        //自動回復
        TextMessage text = new TextMessage();
        text.setContent("the text is" + content);
        text.setToUserName(fromUserName);
        text.setFromUserName(toUserName);
        text.setCreateTime(new Date().getTime() + "");
        text.setMsgType(msgType);
        
        respMessage = MessageUtil.textMessageToXml(text);
        
      } /*else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_EVENT)) {// 事件推送
        String eventType = requestMap.get("Event");// 事件類型
        
        if (eventType.equals(MessageUtil.EVENT_TYPE_SUBSCRIBE)) {// 訂閱
          respContent = "歡迎關注xxx公眾號!";
          return MessageResponse.getTextMessage(fromUserName , toUserName , respContent);
        } else if (eventType.equals(MessageUtil.EVENT_TYPE_CLICK)) {// 自定義菜單點擊事件
          String eventKey = requestMap.get("EventKey");// 事件KEY值,與創建自定義菜單時指定的KEY值對應
          logger.info("eventKey is:" +eventKey);
          return xxx;
        }
      }
      //開啟微信聲音識別測試 2015-3-30
      else if(msgType.equals("voice"))
      {
        String recvMessage = requestMap.get("Recognition");
        //respContent = "收到的語音解析結果:"+recvMessage;
        if(recvMessage!=null){
          respContent = TulingApiProcess.getTulingResult(recvMessage);
        }else{
          respContent = "您說的太模糊了,能不能重新說下呢?";
        }
        return MessageResponse.getTextMessage(fromUserName , toUserName , respContent);
      }
      //拍照功能
      else if(msgType.equals("pic_sysphoto"))
      {
        
      }
      else
      {
        return MessageResponse.getTextMessage(fromUserName , toUserName , "返回為空");
      }*/
      // 事件推送
      else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_EVENT)) {
        String eventType = requestMap.get("Event");// 事件類型
        // 訂閱
        if (eventType.equals(MessageUtil.EVENT_TYPE_SUBSCRIBE)) {
          
          TextMessage text = new TextMessage();
          text.setContent("歡迎關注,xxx");
          text.setToUserName(fromUserName);
          text.setFromUserName(toUserName);
          text.setCreateTime(new Date().getTime() + "");
          text.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT);
          
          respMessage = MessageUtil.textMessageToXml(text);
        }
        // TODO 取消訂閱后用戶再收不到公眾號發送的消息,因此不需要回復消息
        else if (eventType.equals(MessageUtil.EVENT_TYPE_UNSUBSCRIBE)) {// 取消訂閱
          
          
        }
        // 自定義菜單點擊事件
        else if (eventType.equals(MessageUtil.EVENT_TYPE_CLICK)) {
          String eventKey = requestMap.get("EventKey");// 事件KEY值,與創建自定義菜單時指定的KEY值對應
          if (eventKey.equals("customer_telephone")) {
            TextMessage text = new TextMessage();
            text.setContent("0755-86671980");
            text.setToUserName(fromUserName);
            text.setFromUserName(toUserName);
            text.setCreateTime(new Date().getTime() + "");
            text.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT);
            
            respMessage = MessageUtil.textMessageToXml(text);
          }
        }
      }
    }
    catch (Exception e) {
      Logger.error("error......")
    }
    return respMessage;
  }

先貼代碼如上,大多都有注釋,讀一遍基本語義也懂了不需要多解釋。

 有一個地方格外需要注意:

上面標紅的fromUserName和toUserName剛好相反,這也是坑之一,還記得我當時調了很久,明明都沒有問題就是不通,最后把這兩個一換消息就收到了!其實回過頭想也對,返回給微信服務器這時本身角色就變了,所以發送和接收方也肯定是相反的。

5.MessageUtil

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
public class MessageUtil {
  
  /**
   * 返回消息類型:文本
   */
  public static final String RESP_MESSAGE_TYPE_TEXT = "text";
 
  /**
   * 返回消息類型:音樂
   */
  public static final String RESP_MESSAGE_TYPE_MUSIC = "music";
 
  /**
   * 返回消息類型:圖文
   */
  public static final String RESP_MESSAGE_TYPE_NEWS = "news";
 
  /**
   * 請求消息類型:文本
   */
  public static final String REQ_MESSAGE_TYPE_TEXT = "text";
 
  /**
   * 請求消息類型:圖片
   */
  public static final String REQ_MESSAGE_TYPE_IMAGE = "image";
 
  /**
   * 請求消息類型:鏈接
   */
  public static final String REQ_MESSAGE_TYPE_LINK = "link";
 
  /**
   * 請求消息類型:地理位置
   */
  public static final String REQ_MESSAGE_TYPE_LOCATION = "location";
 
  /**
   * 請求消息類型:音頻
   */
  public static final String REQ_MESSAGE_TYPE_VOICE = "voice";
 
  /**
   * 請求消息類型:推送
   */
  public static final String REQ_MESSAGE_TYPE_EVENT = "event";
 
  /**
   * 事件類型:subscribe(訂閱)
   */
  public static final String EVENT_TYPE_SUBSCRIBE = "subscribe";
 
  /**
   * 事件類型:unsubscribe(取消訂閱)
   */
  public static final String EVENT_TYPE_UNSUBSCRIBE = "unsubscribe";
 
  /**
   * 事件類型:CLICK(自定義菜單點擊事件)
   */
  public static final String EVENT_TYPE_CLICK = "CLICK";
}

JAVA實現 springMVC方式的微信接入、實現消息自動回復實例

這里為了程序可讀性、擴展性更好一點,我做了一些封裝,定義了幾個常量,以及將微信傳過來的一些參數封裝成java bean持久化對象,核心代碼如上。重點講下xml和map之間的轉換

其實這個問題要歸咎于微信是用xml通訊,而我們平時一般是用json,所以可能短時間內會有點不適應

1.引入jar包

?
1
2
3
4
5
6
7
8
9
10
11
12
<!-- 解析xml -->
    <dependency>
      <groupId>dom4j</groupId>
      <artifactId>dom4j</artifactId>
      <version>1.6.1</version>
    </dependency>
    
    <dependency>
      <groupId>com.thoughtworks.xstream</groupId>
      <artifactId>xstream</artifactId>
      <version>1.4.9</version>
    </dependency>

2.xml轉map集合對象

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/**
   * xml轉換為map
   * @param request
   * @return
   * @throws IOException
   */
  @SuppressWarnings("unchecked")
  public static Map<String, String> xmlToMap(HttpServletRequest request) throws IOException{
    Map<String, String> map = new HashMap<String, String>();
    SAXReader reader = new SAXReader();
    
    InputStream ins = null;
    try {
      ins = request.getInputStream();
    } catch (IOException e1) {
      e1.printStackTrace();
    }
    Document doc = null;
    try {
      doc = reader.read(ins);
      Element root = doc.getRootElement();
      
      List<Element> list = root.elements();
      
      for (Element e : list) {
        map.put(e.getName(), e.getText());
      }
      
      return map;
    } catch (DocumentException e1) {
      e1.printStackTrace();
    }finally{
      ins.close();
    }
    
    return null;
  }

3.文本消息對象轉換成xml

?
1
2
3
4
5
6
7
8
9
10
11
/**
   * 文本消息對象轉換成xml
   *
   * @param textMessage 文本消息對象
   * @return xml
   */
  public static String textMessageToXml(TextMessage textMessage){
    XStream xstream = new XStream();
    xstream.alias("xml", textMessage.getClass());
    return xstream.toXML(textMessage);
  }

到此為止已經大功告成了,這個時候可以在公眾號里嘗試發送“測試”,你會收到微信回復的“the text is 測試”,這也是上面代碼里做的回復處理,當然你也可以發揮你的想象用他做所有你想做的事了,比如回復1查天氣,2查違章等等....

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。

原文鏈接:http://www.cnblogs.com/liliangel/p/6036627.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 99精品在线视频 | 91制片厂制作果冻传媒2021 | 金牛网155755水心论坛黄大父母 | 免费观看视频在线播放 | 国产一区二区三区免费在线视频 | 精品9e精品视频在线观看 | 嫩草影院永久一二三入口 | 无套白浆 | 欧美日韩精品一区二区三区视频在线 | 色99视频 | 91大神亚洲影视在线 | 王小军怎么了最新消息 | 拍拍叫痛的无挡视频免费 | 性做久久久久免费观看 | videos护士有奶水 | 青青99 | 四虎影音先锋 | 国产精品nv在线观看 | dasd817黑人在线播放 | 日本一在线中文字幕天堂 | 国产视频一区在线观看 | 1024国产精品视频观看 | 国产精品视频一区二区三区经 | 性印度freehd | 免费国产在线视频 | 精品视频一区二区观看 | 牛牛色婷婷在线视频播放 | 天天快乐在线观看 | 欧美亚洲高清日韩成人 | 国产成人盗拍精品免费视频 | 日日日操 | 日韩视频在线精品视频免费观看 | 色欧美在线 | 农村妇女野战bbxxx农村妇女 | 国产精品福利在线观看入口 | 免费观看日本人成影片 | 性直播免费| 亚洲欧美日韩成人一区在线 | 97大香伊在人人线色 | 嫩模被黑人粗大挺进 | 日剧整部剧护妻狂魔免费观看全集 |