Wechat web page sharing (with wechat public platform)

Posted by sn202 on Fri, 18 Feb 2022 19:34:35 +0100

Source code download address:
https://download.csdn.net/download/bynn_csdn/81350948

I preparation

  • Prepare a domain name (the legal links shared by wechat are all attached under the domain name, but the ip name of the server is not available). It is also OK to use the intranet penetration method (I used the intranet penetration method in the test stage). The intranet penetration method can be found in the link referenced at the end of the chapter.

  • Prepare an account on wechat public platform

    • Set the js security domain name of the official account (put the domain name without prefix http).

Before saving, download the file and put it in the root directory of the project. If it is a springboot project of the development environment, you can refer to this tutorial

`https://www.cnblogs.com/pxblog/p/13445128.htm`
  • Set official account development information (get AppID and AppSecret)

At the same time`IP`Whitelist settings`ip`The address can only be obtained successfully`access_token`
Note: add the location of this machine to the white list in the development stage ip Address where the ECS is added in the production phase ip address

  • If the wechat public platform has no relevant account, it can be developed with wechat test account
    https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login&token=840340790&lang=zh_CN
    For the test details of the test account, please refer to the boss's process: https://zhuanlan.zhihu.com/p/134461089
    Note: use the test account interface to obtain access_ This step of token can be operated normally, but an error will be reported in the later stage of sharing friends and circle of friends. You still have to use the public platform account.

II The front and back end versions of SpringBoot are not separated

  1. Property file application Configure AppID and AppSecret in YML

  2. Please refer to the official document for initialization of JSSDK configuration information https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#3 The most important step in using JSSDK is to generate signature

    The process of obtaining signature is as follows:

    • The front end requests the url to be shared to the back end

    • Get access_token, and then according to access_token obtains jsapi from wechat official Api_ ticket

    • Sort noncestr (random string), a valid jsapi_ Splice 4 parameters: ticket, timestamp, URL (URL of the current web page, excluding # and its subsequent parts), for example: noncestr = XX & jsapi_ticket=XX&timestamp=XX&url=XX

    • Then, the four parameters of sha1 encryption splicing are used to obtain the signature

    Note:

    • As shown above, JSSDK configuration information needs to be: noncestr (random string), valid signature (signature), timestamp (time stamp), appid (official account appid), the configuration information needed for the backend generation and back to the front end, the front end get the configuration information and define the sharing function processing.

    • Some people may say that I can directly open the link to share in wechat and click to send friends or circle of friends. Can I share it directly? Yes, yes, but the style will be ugly. As shown below, there is only title + link, pale and powerless, and no style;

      After permission configuration, you can realize the following customized sharing links, that is, you can customize the effect of title + introduction + picture.

  3. Get access_token
    As you can see in the process of obtaining signature in step 2, you must first obtain access_token:

    String tokenUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential";
    tokenUrl = tokenUrl + "&appid=" + appid + "&secret=" + secret;
    JSONObject tokenJson = new JSONObject();
    tokenJson = getUrlResponse(tokenUrl);
    log.info(tokenJson.toString());
    log.info("tokenJson:"+tokenJson.toString());
    String token="";
    try {
        /**
        * TODO:access_token It should be stored in the cache and set the validity period to 7200s
        */
        token = tokenJson.getString("access_token");
    } catch (JSONException e) {
        e.printStackTrace();
        log.error("Wrong report");
        return null;
    }
    
    
  4. Get jsapi_ticket

    String jsapiTicketUrl="https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi";
    JSONObject jsapiTickeJson = new JSONObject();
    log.info("getJsapiTicket: obtain token: "+token);
    if(StringUtils.isNotBlank(token)){
        jsapiTicketUrl = jsapiTicketUrl.replace("ACCESS_TOKEN",token);
        jsapiTickeJson=getUrlResponse(jsapiTicketUrl);
        log.info("tokenJson:"+jsapiTickeJson.toString());
        try {
            return (String) jsapiTickeJson.get("ticket");
        } catch (JSONException e) {
            e.printStackTrace();
            return null;
        }
    }else{
        return null;
    }
    
  5. Get sort noncestr (random string)

    public class RandomStr {
        private static char ch[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
                'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b',
                'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
                'x', 'y', 'z', '0', '1' };//Finally, repeat the two zeros and ones, because you need to make up for the array length of 64
    
        private static Random random = new Random();
        //Generates a random string of the specified length
        public static String createRandomString(int length) {
            if (length > 0) {
                int index = 0;
                char[] temp = new char[length];
                int num = random.nextInt();
                for (int i = 0; i < length % 5; i++) {
                    temp[index++] = ch[num & 63];//Take the last six digits and remember that the corresponding binary exists in the form of complement.
                    num >>= 6;//63 binary: 111111
                    // Why shift six bits to the right? Because there are 64 valid characters in the array. Why divide by five? Because an int type needs to be represented by 4 bytes, that is, 32 bits.
                }
                for (int i = 0; i < length / 5; i++) {
                    num = random.nextInt();
                    for (int j = 0; j < 5; j++) {
                        temp[index++] = ch[num & 63];
                        num >>= 6;
                    }
                }
                return new String(temp, 0, length);
            }
            else if (length == 0) {
                return "";
            }
            else {
                throw new IllegalArgumentException();
            }
        }
    
        public static void main(String[] args) {
            System.out.println(createRandomString(16));
        }
    }
    
  6. Obtain signature through Sha1 encryption

    long timestamp = System.currentTimeMillis() / 1000;
    String noncestr = RandomStr.createRandomString(16);
    String str = "jsapi_ticket=" + ticket + "&noncestr=" + noncestr + "&timestamp=" + timestamp + "&url=" + url;
    String signature = Sha1.encode(str);
    
    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);
            // Convert ciphertext into hexadecimal string form
            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);
            }
        }
    }
    

    After generating the above configuration information, the back-end will package and return the JSSDK configuration information (signature carrying the shared page information) and other customized information to the front-end

  7. Front end JS interface configuration

    Because the front and back ends are not separated, the page has to introduce the Js file of Jquery

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Policy details page</title>
    </head>
    <body>
        <p class="MsoNormal" style="mso-para-margin-top: .5gd; mso-para-margin-right: 0cm; mso-para-margin-bottom: .5gd; mso-para-margin-left: 0cm; text-align: center; line-height: 150%; margin: 7.8pt 0cm 7.8pt 0cm;" align="center"><strong style="mso-bidi-font-weight: normal;"><span style="font-size: 12.0pt; line-height: 150%; font-family: 'Microsoft YaHei ','sans-serif';">Notice of the Beijing Municipal Committee of the Communist Party of China and the Beijing Municipal People's Government on printing and distributing the implementation plan on promoting the inheritance, innovation and development of traditional Chinese Medicine</span></strong></p>
    </body>
    <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
    <script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.4.0.js"></script>
    <script type="text/javascript">
        $(function () {
            //url address of the current page
            var currUrl = decodeURIComponent(location.href.split('#')[0]);
            var shareLink = window.location.href.toString();//Share links
            $.ajax({
                url: "/get_wx_config",
                dataType : "json",
                data: {
                    'url': currUrl
                },
                error: function (res) {
                    console.log(res);
                    alert("An error occurred");
                },
                success: function (res) {
                    console.log(res);
                    var appId = res.appId;
                    var nonceStr = res.nonceStr;
                    var timestamp = res.timestamp;
                    var signature = res.signature;
                    //Custom return content
                    var shareImgUrl = res.backImgUrl;
                    var backTitle = res.backTitle;
                    var backDesc = res.backDesc;
                    wx.config({
                        debug: false, //When the debugging mode is enabled, it can be changed to true in the development stage, and the return values of all APIs called will be alert ed on the client. To view the incoming parameters, it can be opened on the pc side, and the parameter information will be printed through log, which will only be printed on the pc side.
                        appId: appId, //Required, the only sign of official account.
                        timestamp: timestamp, // Required, time stamp to generate signature
                        nonceStr: nonceStr, //Required, generate random string of signature
                        signature: signature, // Required, signature, see Appendix 1
                        jsApiList: [            //Required. List of JS interfaces to be used. See Appendix 2 for the list of all JS interfaces
                            'updateAppMessageShareData',
                            'updateTimelineShareData'
                        ]
                    });
    
    
                    wx.ready(function () {      //It needs to be called before the user may click the share button
                        //"Share to friends" and "share to QQ"
                        wx.updateAppMessageShareData({
                            title: 'Friend, I'm the title', // Share title
                            desc: 'Friend, I'm describing', // Share description
                            link: shareLink, // Share the link, the domain name or path must be consistent with the official account JS security domain corresponding to the current page.
                            imgUrl: shareImgUrl, // Share Icon
                            success: function (res) {
                                // Set successfully
                            }
                        })
    
                        //"Share to the circle of friends" and "share to QQ space"
                        wx.updateTimelineShareData({
                            title: 'Friend, I'm the title', // Share title
                            desc: 'Friend, I'm describing', // Share description
                            link: shareLink, // Share the link, the domain name or path must be consistent with the official account JS security domain corresponding to the current page.
                            imgUrl: shareImgUrl, // Share Icon
                            success: function (res) {
                                // Set successfully
                            }
                        })
    
    
                    });
    
                    wx.error(function (res) {
    
                    });
                }
            });
    
        });
    
    </script>
    </html>  
    
  8. test
    Open the domain name of wechat tool and enter the address in the http: / / / field_ Detail, click share, and the following interface will appear, indicating that sharing can be realized!

The above mainly refers to the official documents of wechat and the practices of the following two leaders:

  • https://www.cnblogs.com/a876459952/p/13294124.html
  • https://www.cnblogs.com/pxblog/p/12881454.html

III SpringBoot+Vue front and rear end separated versions

  1. The backend changes little, mainly share Html is transformed into Vue page, and a QR code scanning and sharing function is added at the same time

    Click the wechat icon as shown in the figure to pop up the QR code. After scanning and clicking wechat, you can share:

  2. Transform Vue front end page
    First create a wxshare js

    // To use wechat API
    function getJSSDK(url, dataForWeixin) {
        // Call the background interface for parameters
        axios.get('http://Domain name / get_wx_config', {
            params: {
                url,
            },
        }).then((res) => {
            console.log(res)
            wx.config({
                debug: false, // When the debugging mode is enabled, the return values of all APIs called will be alert ed on the client. To view the incoming parameters, you can open them on the pc side, and the parameter information will be printed through log, which will only be printed on the pc side.
                appId: res.appId, // Required, the only sign of official account.
                timestamp: res.timestamp, // Required, time stamp to generate signature
                nonceStr: res.nonceStr, // Required, generate random string of signature
                signature: res.signature, // Required, signature
                jsApiList: ['updateAppMessageShareData', 'updateTimelineShareData'] // Required, list of JS interfaces to be used
            });
            wx.ready(function () {
                //Share with friends
                wx.updateAppMessageShareData({
                    title: dataForWeixin.title,
                    desc: dataForWeixin.desc,
                    link: dataForWeixin.linkurl,
                    imgUrl: dataForWeixin.img,
                    success: function () {
                        // Callback function executed after user confirms sharing
                        // alert('share success');
                    },
                    cancel: function () {
                        // Callback function executed after user cancels sharing
                    },
                    fail: function (res) {
                        alert(JSON.stringify(res));
                    }
                });
                //Share with friends
                wx.updateTimelineShareData({
                    title: dataForWeixin.title,
                    desc: dataForWeixin.desc,
                    link: dataForWeixin.linkurl,
                    imgUrl: dataForWeixin.img,
                    success: function (res) {
                        // Set successfully
                        // alert("share success!")
                        // console.log("sharing succeeded!")
                    },
                    cancel: function () {
                        // alert("cancelled")
                    },
                    fail: function (res) {
                        alert(JSON.stringify(res));
                    }
                })
                wx.error(function (res) {
                    // If the verification of config information fails, the error function will be executed. For example, the verification fails due to the expiration of the signature. The specific error information can be viewed in the debug mode of config or in the returned res parameter. For SPA, the signature can be updated here.
                    // alert("errorMSG:" + res);
                });
            });
        });
    }
    
    export default {
        // Get JSSDK
        getJSSDK,
    }
    

    Secondly, create a sharing page wxshare Vue, where title, desc, linkurl and img are shared settings. Linkurl should dynamically obtain the current front-end page address

    <template>
      <div>
        <div v-html="title"></div>
        <div class="wx"style="text-align: center">
          <img src="../assets/wx.jpg" alt="Click Share" title="Click Share"/>
        </div>
        ......
      </div>
    </template>
    <script>
    import share from '@/assets/js/share'
    import vueQr from 'vue-qr'
    export default {
      name: "wxShare",
      components: {
        vueQr
      },
      mounted() {
        const url = location.href.split('#')[0];
        console.log(url)
        const dataForWeixin = {
          title: 'Notice of the Beijing Municipal Committee of the Communist Party of China and the Beijing Municipal People's Government on printing and distributing the implementation plan on promoting the inheritance, innovation and development of traditional Chinese Medicine',    // Share title
          desc: 'In order to implement the spirit of the opinions of the CPC Central Committee and the State Council on promoting the inheritance, innovation and development of traditional Chinese medicine, and in combination with the actual situation of this city, the following implementation plan is formulated',            // Content description
          linkurl: window.location.href.toString(), // Share the link, the domain name or path must be consistent with the official account JS security domain corresponding to the current page.
          img: 'http://wx.qlogo.cn/mmopen/ciaIftfPzwlo0coPuwwLS5Fw9UwGMlxY2ziaWpqXzevJI8dKeDvk4n3NxtZS4D8dNHSYUhbiaA6IIGnFsiagEbRlaExselicC3pEA/64 '/ / share the picture displayed by the content (the picture must be a square link)
        };
        share.getJSSDK(url,dataForWeixin)
      },
      methods: {
    
      },
      data() {
        return {
          dialogVisible: false,
          shareData: {
            // url: 'http://cxyabc.vaiwan.com/to_detail '/ / the URL that needs to be converted into QR code
            url: window.location.href.toString(),  //URL that needs to be converted into QR code
            icon: require('@/assets/img.png')  //The picture in the middle of the QR code can not be set
          },
          title: '<p class="MsoNormal" style="mso-para-margin-top: .5gd; mso-para-margin-right: 0cm; mso-para-margin-bottom: .5gd; mso-para-margin-left: 0cm; text-align: center; line-height: 150%; margin: 7.8pt 0cm 7.8pt 0cm;" align="center"><strong style="mso-bidi-font-weight: normal;"><span style="font-size: 12.0pt; line-height: 150%; font-family: \'Microsoft YaHei \',\'sans-serif\';">Notice of the Beijing Municipal Committee of the Communist Party of China on the implementation of the Beijing Municipal Government's plan for promoting the inheritance and development of traditional Chinese Medicine</span></strong></p>\n',
        }
      }
    }
    </script>
    
  3. The vue project file build is packaged as a static file, and nginx is used as an agent to separate the front and back end configuration
    Note: there may be {"errMsg":"translateVoice:fail, the permission value is offline verifying"} in the actual online or development process. In addition to the solutions that Baidu can find, I have encountered the following solutions in the actual process:

    • The format of the JSSDK configuration information returned from the back-end packaging to the front-end is incorrect. The following screenshot position can be returned directly without changing to String. Because there is a + @ ResponseBody annotation, the framework will automatically return the json object

    • The back-end receives the url of the current web page with domain name on the front-end. Here, my project will not report errors at the beginning, but always report the above errors later. If you cancel the operation of replacing '#', you will not report errors. The specific reasons are still being explored. At least this is how my project solves the reasons for errors.

Front and rear end separation is mainly combined https://zhuanlan.zhihu.com/p/135179184 This guy's article

Source code download address:
https://download.csdn.net/download/bynn_csdn/81350948

Topics: Java Spring Boot Vue.js wechat