<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          手把手教你開(kāi)發(fā)微信公眾號(hào)后臺(tái)

          共 20873字,需瀏覽 42分鐘

           ·

          2020-08-28 21:04




          • 1.緣起

          • 2.實(shí)現(xiàn)思路

          • 3.公眾號(hào)后臺(tái)配置

          • 4.開(kāi)發(fā)

            • 4.1 服務(wù)器有效性校驗(yàn)

            • 4.2 消息接收接口

          • 5.消息分類

          • 6.返回消息類型定義

          • 7.返回消息生成

          • 8.返回消息分發(fā)


          Hello 各位小伙伴,松哥今天要和大家聊一個(gè)有意思的話題,就是使用 Spring Boot 開(kāi)發(fā)微信公眾號(hào)后臺(tái)。

          很多小伙伴可能注意到松哥的個(gè)人網(wǎng)站(http://www.javaboy.org)前一陣子上線了一個(gè)公眾號(hào)內(nèi)回復(fù)口令解鎖網(wǎng)站文章的功能,還有之前就有的公眾號(hào)內(nèi)回復(fù)口令獲取超 2TB 免費(fèi)視頻教程的功能(公號(hào)后臺(tái)回復(fù) 2TB),這兩個(gè)都是松哥基于 Spring Boot 來(lái)做的,最近松哥打算通過(guò)一個(gè)系列的文章,來(lái)向小伙伴們介紹下如何通過(guò) Spring Boot 來(lái)開(kāi)發(fā)公眾號(hào)后臺(tái)。

          1.緣起

          今年 5 月份的時(shí)候,我想把我自己之前收集到的一些視頻教程分享給公眾號(hào)上的小伙伴,可是這些視頻教程大太了,無(wú)法一次分享,單次分享分享鏈接立馬就失效了,為了把這些視頻分享給大家,我把視頻拆分成了很多份,然后設(shè)置了不同的口令,小伙伴們?cè)诠娞?hào)后臺(tái)通過(guò)回復(fù)口令就可以獲取到這些視頻,口令前前后后有 100 多個(gè),我一個(gè)一個(gè)手動(dòng)的在微信后臺(tái)進(jìn)行配置。這么搞工作量很大,前前后后大概花了三個(gè)晚上才把這些東西搞定。

          于是我就在想,該寫(xiě)點(diǎn)代碼了。

          上個(gè)月買了服務(wù)器,也備案了,該有的都有了,于是就打算把這些資源用代碼實(shí)現(xiàn)下,因?yàn)榇髮W(xué)時(shí)候搞過(guò)公眾號(hào)開(kāi)發(fā),倒也沒(méi)什么難度,于是說(shuō)干就干。

          2.實(shí)現(xiàn)思路

          其實(shí)松哥這個(gè)回復(fù)口令獲取視頻鏈接的實(shí)現(xiàn)原理很簡(jiǎn)單,說(shuō)白了,就是一個(gè)數(shù)據(jù)查詢操作而已,回復(fù)的口令是查詢關(guān)鍵字,回復(fù)的內(nèi)容則是查詢結(jié)果。這個(gè)原理很簡(jiǎn)單。

          另一方面大家需要明白微信公眾號(hào)后臺(tái)開(kāi)發(fā)消息發(fā)送的一個(gè)流程,大家看下面這張圖:

          這是大家在公眾號(hào)后臺(tái)回復(fù)關(guān)鍵字的情況。那么這個(gè)消息是怎么樣一個(gè)傳遞流程呢?我們來(lái)看看下面這張圖:

          這張圖,我給大家稍微解釋下:

          1. 首先 javaboy4096 這個(gè)字符從公眾號(hào)上發(fā)送到了微信服務(wù)器
          2. 接下來(lái)微信服務(wù)器會(huì)把 javaboy4096 轉(zhuǎn)發(fā)到我自己的服務(wù)器上
          3. 我收到 javaboy4096 這個(gè)字符之后,就去數(shù)據(jù)庫(kù)中查詢,將查詢的結(jié)果,按照騰訊要求的 XML 格式進(jìn)行返回
          4. 微信服務(wù)器把從我的服務(wù)器收到的信息,再發(fā)回到微信上,于是小伙伴們就看到了返回結(jié)果了

          大致的流程就是這個(gè)樣子。

          接下來(lái)我們就來(lái)看一下實(shí)現(xiàn)細(xì)節(jié)。

          3.公眾號(hào)后臺(tái)配置

          開(kāi)發(fā)的第一步,是微信服務(wù)器要驗(yàn)證我們自己的服務(wù)器是否有效。

          首先我們登錄微信公眾平臺(tái)官網(wǎng)后,在公眾平臺(tái)官網(wǎng)的 「開(kāi)發(fā)-基本設(shè)置」 頁(yè)面,勾選協(xié)議成為開(kāi)發(fā)者,然后點(diǎn)擊“修改配置”按鈕,填寫(xiě):

          • 服務(wù)器地址(URL)
          • Token
          • EncodingAESKey

          這里的 URL 配置好之后,我們需要針對(duì)這個(gè) URL 開(kāi)發(fā)兩個(gè)接口,一個(gè)是 GET 請(qǐng)求的接口,這個(gè)接口用來(lái)做服務(wù)器有效性驗(yàn)證,另一個(gè)則是 POST 請(qǐng)求的接口,這個(gè)用來(lái)接收微信服務(wù)器發(fā)送來(lái)的消息。也就是說(shuō),微信服務(wù)器的消息都是通過(guò) POST 請(qǐng)求發(fā)給我的。

          Token 可由開(kāi)發(fā)者可以任意填寫(xiě),用作生成簽名(該 Token 會(huì)和接口 URL 中包含的 Token 進(jìn)行比對(duì),從而驗(yàn)證安全性)。

          EncodingAESKey 由開(kāi)發(fā)者手動(dòng)填寫(xiě)或隨機(jī)生成,將用作消息體加解密密鑰。

          同時(shí),開(kāi)發(fā)者可選擇消息加解密方式:明文模式、兼容模式和安全模式。明文模式就是我們自己的服務(wù)器收到微信服務(wù)器發(fā)來(lái)的消息是明文字符串,直接就可以讀取并且解析,安全模式則是我們收到微信服務(wù)器發(fā)來(lái)的消息是加密的消息,需要我們手動(dòng)解析后才能使用。

          4.開(kāi)發(fā)

          公眾號(hào)后臺(tái)配置完成后,接下來(lái)我們就可以寫(xiě)代碼了。

          4.1 服務(wù)器有效性校驗(yàn)

          我們首先來(lái)創(chuàng)建一個(gè)普通的 Spring Boot 項(xiàng)目,創(chuàng)建時(shí)引入 spring-boot-starter-web 依賴,項(xiàng)目創(chuàng)建成功后,我們創(chuàng)建一個(gè) Controller ,添加如下接口:

          @GetMapping("/verify_wx_token")
          public void login(HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException {
              request.setCharacterEncoding("UTF-8");
              String signature = request.getParameter("signature");
              String timestamp = request.getParameter("timestamp");
              String nonce = request.getParameter("nonce");
              String echostr = request.getParameter("echostr");
              PrintWriter out = null;
              try {
                  out = response.getWriter();
                  if (CheckUtil.checkSignature(signature, timestamp, nonce)) {
                      out.write(echostr);
                  }
              } catch (IOException e) {
                  e.printStackTrace();
              } finally {
                  out.close();
              }
          }

          關(guān)于這段代碼,我做如下解釋:

          1. 首先通過(guò) request.getParameter 方法獲取到微信服務(wù)器發(fā)來(lái)的 signature、timestamp、nonce 以及 echostr 四個(gè)參數(shù),這四個(gè)參數(shù)中:signature 表示微信加密簽名,signature 結(jié)合了開(kāi)發(fā)者填寫(xiě)的 token 參數(shù)和請(qǐng)求中的timestamp參數(shù)、nonce參數(shù);timestamp 表示時(shí)間戳;nonce 表示隨機(jī)數(shù);echostr 則表示一個(gè)隨機(jī)字符串。
          2. 開(kāi)發(fā)者通過(guò)檢驗(yàn) signature 對(duì)請(qǐng)求進(jìn)行校驗(yàn),如果確認(rèn)此次 GET 請(qǐng)求來(lái)自微信服務(wù)器,則原樣返回 echostr 參數(shù)內(nèi)容,則接入生效,成為開(kāi)發(fā)者成功,否則接入失敗。
          3. 具體的校驗(yàn)就是松哥這里的 CheckUtil.checkSignature 方法,在這個(gè)方法中,首先將token、timestamp、nonce 三個(gè)參數(shù)進(jìn)行字典序排序,然后將三個(gè)參數(shù)字符串拼接成一個(gè)字符串進(jìn)行 sha1 加密,最后開(kāi)發(fā)者獲得加密后的字符串可與 signature 對(duì)比,標(biāo)識(shí)該請(qǐng)求來(lái)源于微信。

          校驗(yàn)代碼如下:

          public class CheckUtil {
              private static final String token = "123456";
              public static boolean checkSignature(String signature, String timestamp, String nonce) {
                  String[] str = new String[]{token, timestamp, nonce};
                  //排序
                  Arrays.sort(str);
                  //拼接字符串
                  StringBuffer buffer = new StringBuffer();
                  for (int i = 0; i < str.length; i++) {
                      buffer.append(str[i]);
                  }
                  //進(jìn)行sha1加密
                  String temp = SHA1.encode(buffer.toString());
                  //與微信提供的signature進(jìn)行匹對(duì)
                  return signature.equals(temp);
              }
          }
          public class SHA1 {
              private static final char[] HEX_DIGITS = {'0''1''2''3''4''5',
                      '6''7''8''9''a''b''c''d''e''f'};
              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);
                  }
              }
          }

          OK,完成之后,我們的校驗(yàn)接口就算是開(kāi)發(fā)完成了。接下來(lái)就可以開(kāi)發(fā)消息接收接口了。

          4.2 消息接收接口

          接下來(lái)我們來(lái)開(kāi)發(fā)消息接收接口,消息接收接口和上面的服務(wù)器校驗(yàn)接口地址是一樣的,都是我們一開(kāi)始在公眾號(hào)后臺(tái)配置的地址。只不過(guò)消息接收接口是一個(gè) POST 請(qǐng)求。

          我在公眾號(hào)后臺(tái)配置的時(shí)候,消息加解密方式選擇了明文模式,這樣我在后臺(tái)收到的消息直接就可以處理了。微信服務(wù)器給我發(fā)來(lái)的普通文本消息格式如下:

          <xml>
            <ToUserName>ToUserName>
            <FromUserName>FromUserName>
            <CreateTime>1348831860CreateTime>
            <MsgType>MsgType>
            <Content>Content>
            <MsgId>1234567890123456MsgId>
          xml>

          這些參數(shù)含義如下:

          看到這里,大家心里大概就有數(shù)了,當(dāng)我們收到微信服務(wù)器發(fā)來(lái)的消息之后,我們就進(jìn)行 XML 解析,提取出來(lái)我們需要的信息,去做相關(guān)的查詢操作,再將查到的結(jié)果返回給微信服務(wù)器。

          這里我們先來(lái)個(gè)簡(jiǎn)單的,我們將收到的消息解析并打印出來(lái):

          @PostMapping("/verify_wx_token")
          public void handler(HttpServletRequest request, HttpServletResponse response) throws Exception {
              request.setCharacterEncoding("UTF-8");
              response.setCharacterEncoding("UTF-8");
              PrintWriter out = response.getWriter();
              Map  parseXml = MessageUtil.parseXml(request);
              String msgType = parseXml.get( "MsgType");
              String content = parseXml.get( "Content");
              String fromusername = parseXml.get( "FromUserName");
              String tousername = parseXml.get( "ToUserName");
              System.out.println(msgType);
              System.out.println(content);
              System.out.println(fromusername);
              System.out.println(tousername);
          }
          public static Map   parseXml (HttpServletRequest request)  throws Exception  {
              Map  map =  new HashMap ();
              InputStream inputStream = request.getInputStream();
              SAXReader reader =  new SAXReader();
              Document document = reader.read(inputStream);
              Element root = document.getRootElement();
              List  elementList = root.elements();
               for (Element e : elementList)
                  map.put(e.getName(), e.getText());
              inputStream.close();
              inputStream =  null;
               return map;
          }

          大家看到其實(shí)都是一些常規(guī)代碼,沒(méi)有什么難度。

          做完這些之后,我們將項(xiàng)目打成 jar 包在服務(wù)器上部署啟動(dòng)。啟動(dòng)成功之后,確認(rèn)微信的后臺(tái)配置也沒(méi)問(wèn)題,我們就可以在公眾號(hào)上發(fā)一條消息了,這樣我們自己的服務(wù)端就會(huì)打印出來(lái)剛剛消息的信息。

          5.消息分類

          在討論如何給微信服務(wù)器回復(fù)消息之前,我們需要先來(lái)了解下微信服務(wù)器發(fā)來(lái)的消息主要有哪些類型以及我們回復(fù)給微信的消息都有哪些類型。

          在上文中大家了解到,微信發(fā)送來(lái)的 xml 消息中有一個(gè) MsgType 字段,這個(gè)字段就是用來(lái)標(biāo)記消息的類型。這個(gè)類型可以標(biāo)記出這條消息是普通消息還是事件消息還是圖文消息等。

          普通消息主要是指:

          • 文本消息
          • 圖片消息
          • 語(yǔ)音消息
          • 視頻消息
          • 小視頻消息
          • 地址位置消息
          • 鏈接消息

          不同的消息類型,對(duì)應(yīng)不同的 MsgType,這里我還是以普通消息為例,如下:

          消息類型 MsgType
          文本消息 text
          圖片消息 image
          語(yǔ)音消息 voice
          視頻消息 video
          小視頻消息 shortvideo
          地址位置消息 location
          鏈接消息 link

          大家千萬(wàn)不要以為不同類型消息的格式是一樣的,其實(shí)是不一樣的,也就是說(shuō),MsgType 為 text 的消息和 MsgType 為 image 的消息,微信服務(wù)器發(fā)給我們的消息內(nèi)容是不一樣的,這樣帶來(lái)一個(gè)問(wèn)題就是我無(wú)法使用一個(gè) Bean 去接收不同類型的數(shù)據(jù),因此這里我們一般使用 Map 接收即可。

          這是消息的接收,除了消息的接收之外,還有一個(gè)消息的回復(fù),我們回復(fù)的消息也有很多類型,可以回復(fù)普通消息,也可以回復(fù)圖片消息,回復(fù)語(yǔ)音消息等,不同的回復(fù)消息我們可以進(jìn)行相應(yīng)的封裝。因?yàn)椴煌姆祷叵?shí)例也是有一些共同的屬性的,例如消息是誰(shuí)發(fā)來(lái)的,發(fā)給誰(shuí),消息類型,消息 id 等,所以我們可以將這些共同的屬性定義成一個(gè)父類,然后不同的消息再去繼承這個(gè)父類。

          6.返回消息類型定義

          首先我們來(lái)定義一個(gè)公共的消息類型:

          public class BaseMessage {
              private String ToUserName;
              private String FromUserName;
              private long CreateTime;
              private String MsgType;
              private long MsgId;
              //省略 getter/setter
          }

          在這里:

          • ToUserName 表示開(kāi)發(fā)者的微信號(hào)
          • FromUserName 表示發(fā)送方賬號(hào)(用戶的 OpenID)
          • CreateTime 消息的創(chuàng)建時(shí)間
          • MsgType 表示消息的類型
          • MsgId 表示消息 id

          這是我們的基本消息類型,就是說(shuō),我們返回給用戶的消息,無(wú)論是什么類型的消息,都有這幾個(gè)基本屬性。然后在此基礎(chǔ)上,我們?cè)偃U(kuò)展出文本消息、圖片消息 等。

          我們來(lái)看下文本消息的定義:

          public class TextMessage extends BaseMessage {
              private String Content;
              //省略 getter/setter
          }

          文本消息在前面消息的基礎(chǔ)上多了一個(gè) Content 屬性,因此文本消息繼承自 BaseMessage ,再額外添加一個(gè) Content 屬性即可。

          其他的消息類型也是類似的定義,我就不一一列舉了,至于其他消息的格式,大家可以參考微信開(kāi)放文檔(http://1t.click/aPXK)。

          7.返回消息生成

          消息類型的 Bean 定義完成之后,接下來(lái)就是將實(shí)體類生成 XML。

          首先我們定義一個(gè)消息工具類,將常見(jiàn)的消息類型枚舉出來(lái):

          /**
           * 返回消息類型:文本
           */

          public static final String RESP_MESSAGE_TYPE_TEXT = "text";
          /**
           * 返回消息類型:音樂(lè)
           */

          public static final String RESP_MESSAGE_TYPE_MUSIC = "music";
          /**
           * 返回消息類型:圖文
           */

          public static final String RESP_MESSAGE_TYPE_NEWS = "news";
          /**
           * 返回消息類型:圖片
           */

          public static final String RESP_MESSAGE_TYPE_Image = "image";
          /**
           * 返回消息類型:語(yǔ)音
           */

          public static final String RESP_MESSAGE_TYPE_Voice = "voice";
          /**
           * 返回消息類型:視頻
           */

          public static final String RESP_MESSAGE_TYPE_Video = "video";
          /**
           * 請(qǐng)求消息類型:文本
           */

          public static final String REQ_MESSAGE_TYPE_TEXT = "text";
          /**
           * 請(qǐng)求消息類型:圖片
           */

          public static final String REQ_MESSAGE_TYPE_IMAGE = "image";
          /**
           * 請(qǐng)求消息類型:鏈接
           */

          public static final String REQ_MESSAGE_TYPE_LINK = "link";
          /**
           * 請(qǐng)求消息類型:地理位置
           */

          public static final String REQ_MESSAGE_TYPE_LOCATION = "location";
          /**
           * 請(qǐng)求消息類型:音頻
           */

          public static final String REQ_MESSAGE_TYPE_VOICE = "voice";
          /**
           * 請(qǐng)求消息類型:視頻
           */

          public static final String REQ_MESSAGE_TYPE_VIDEO = "video";
          /**
           * 請(qǐng)求消息類型:推送
           */

          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(自定義菜單點(diǎn)擊事件)
           */

          public static final String EVENT_TYPE_CLICK = "CLICK";
          /**
           * 事件類型:VIEW(自定義菜單 URl 視圖)
           */

          public static final String EVENT_TYPE_VIEW = "VIEW";
          /**
           * 事件類型:LOCATION(上報(bào)地理位置事件)
           */

          public static final String EVENT_TYPE_LOCATION = "LOCATION";
          /**
           * 事件類型:LOCATION(上報(bào)地理位置事件)
           */

          public static final String EVENT_TYPE_SCAN = "SCAN";

          大家注意這里消息類型的定義,以 RESP 開(kāi)頭的表示返回的消息類型,以 REQ 表示微信服務(wù)器發(fā)來(lái)的消息類型。然后在這個(gè)工具類中再定義兩個(gè)方法,用來(lái)將返回的對(duì)象轉(zhuǎn)換成 XML:

          public static String textMessageToXml(TextMessage textMessage) {
              xstream.alias("xml", textMessage.getClass());
              return xstream.toXML(textMessage);
          }
          private static XStream xstream = new XStream(new XppDriver() {
              public HierarchicalStreamWriter createWriter(Writer out) {
                  return new PrettyPrintWriter(out) {
                      boolean cdata = true;
                      @SuppressWarnings("rawtypes")
                      public void startNode(String name, Class clazz) {
                          super.startNode(name, clazz);
                      }
                      protected void writeText(QuickWriter writer, String text) {
                          if (cdata) {
                              writer.write(");
          ????????????????????writer.write(text);
          ????????????????????writer.write("]]>");
                          } else {
                              writer.write(text);
                          }
                      }
                  };
              }
          });

          textMessageToXML 方法用來(lái)將 TextMessage 對(duì)象轉(zhuǎn)成 XML 返回給微信服務(wù)器,類似的方法我們還需要定義 imageMessageToXml、voiceMessageToXml 等,不過(guò)定義的方式都基本類似,我就不一一列出來(lái)了。

          8.返回消息分發(fā)

          由于用戶發(fā)來(lái)的消息可能存在多種情況,我們需要分類進(jìn)行處理,這個(gè)就涉及到返回消息的分發(fā)問(wèn)題。因此我在這里再定義一個(gè)返回消息分發(fā)的工具類,如下:

          public class MessageDispatcher {
              public static String processMessage(Map  map)  {
                  String openid = map.get("FromUserName"); //用戶 openid
                  String mpid = map.get("ToUserName");   //公眾號(hào)原始 ID
                  if (map.get("MsgType").equals(MessageUtil.REQ_MESSAGE_TYPE_TEXT)) { 
                      //普通文本消息
                      TextMessage txtmsg = new TextMessage();
                      txtmsg.setToUserName(openid);
                      txtmsg.setFromUserName(mpid);
                      txtmsg.setCreateTime(new Date().getTime());
                      txtmsg.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT);
                      txtmsg.setContent("這是返回消息");
                      return MessageUtil.textMessageToXml(txtmsg);
                  }
                  return null;
              }
              public String processEvent(Map  map)  {
                  //在這里處理事件
              }
          }

          這里我們還可以多加幾個(gè) elseif 去判斷不同的消息類型,我這里因?yàn)橹挥衅胀ㄎ谋鞠ⅲ砸粋€(gè) if 就夠用了。

          在這里返回值我寫(xiě)死了,實(shí)際上這里需要根據(jù)微信服務(wù)端傳來(lái)的 Content 去數(shù)據(jù)中查詢,將查詢結(jié)果返回,數(shù)據(jù)庫(kù)查詢這一套相信大家都能搞定,我這里就不重復(fù)介紹了。

          最后在消息接收 Controller 中調(diào)用該方法,如下:

          @PostMapping(value = "/verify_wx_token",produces = "application/xml;charset=utf-8")
          public String handler(HttpServletRequest request, HttpServletResponse response) throws Exception {
              request.setCharacterEncoding("UTF-8");
              Map  map = MessageUtil.parseXml(request);
              String msgType = map.get( "MsgType");
               if (MessageUtil.REQ_MESSAGE_TYPE_EVENT.equals(msgType)) {
                   return messageDispatcher.processEvent(map);
              } else{
                   return messageDispatcher.processMessage(map);
              }
          }

          在 Controller 中,我們首先判斷消息是否是事件,如果是事件,進(jìn)入到事件處理通道,如果不是事件,則進(jìn)入到消息處理通道。

          「注意,這里需要配置一下返回消息的編碼,否則可能會(huì)出現(xiàn)中文亂碼。」

          如此之后,我們的服務(wù)器就可以給公眾號(hào)返回消息了。

          好了,本文我們就先說(shuō)到這里,感興趣的小伙伴不妨試試。

                  
          — 【 THE END 】—
          本公眾號(hào)全部博文已整理成一個(gè)目錄,請(qǐng)?jiān)诠娞?hào)里回復(fù)「m」獲取!


          3T技術(shù)資源大放送!包括但不限于:Java、C/C++,Linux,Python,大數(shù)據(jù),人工智能等等。在公眾號(hào)內(nèi)回復(fù)「1024」,即可免費(fèi)獲取!!




          瀏覽 77
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日本无码中文字幕 | 我要看黄色A片 | 日韩特级毛片 | 国产高清无码视频在线 | 成人国产日韩AV网站 |