美文网首页
在前端艰难爬行之H5网页支持微信分享

在前端艰难爬行之H5网页支持微信分享

作者: Realank | 来源:发表于2018-06-27 16:57 被阅读1579次

    微信真坑,鼓捣了一天,眼睛都瞎了,最后发现个人微信公众账号没有权限,尼玛。。。

    1. 注册微信公众平台

    为了能够使用微信的接口,需要注册开发者账号身份, 这个自行百度

    2.搭建一个服务器

    我们需要一个服务器,来获取访问微信API的签名,这里使用NodeJS服务器:

    const request = require('request')
    var express = require('express');
    var app = express();
    const sha1 = require('sha1')
    const waterfall = require('async/waterfall')
    const NodeCache = require('node-cache')
    const cache = new NodeCache({stdTTL: 3600, checkperiod: 3600})
    
    // app.get('/wxJssdk', function (req, res) {
    //     let wx = req.query
    
    //     // 1)将token、timestamp、nonce三个参数进行字典序排序
    //     let token = 'jegfjaeghfuyawegfgjdbh'
    //     let timestamp = wx.timestamp
    //     let nonce = wx.nonce
    
    //     // 2)将三个参数字符串拼接成一个字符串进行sha1加密
    //     let list = [token, timestamp, nonce]
    //     let result = sha1(list.sort().join(''))
    
    //     // 3)开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
    //     if (result === wx.signature) {
    //         res.send(wx.echostr)
    //     } else {
    //         res.send(false)
    //     }
    // })
    
    app.get('/wxJssdk/getJssdk', function (req, res) {
        let grant_type = 'client_credential'
        let appid = 'xxxx'
        let secret = 'xxxx' // appscret
    
        let steps = []
    
        // 第一步,获取access_token
        steps.push((cb) => {
    
            let steps1 = []
    
            // 第1.1步,从缓存中读取access_token
            steps1.push((cb1) => {
                let access_token = cache.get('access_token', (err, access_token) => {
                    cb1(err, access_token)
                })
            })
    
            // 第1.2步,缓存中有access_token则直接返回,如果没有,则从服务器中读取access_token
            steps1.push((access_token, cb1) => {
                if (access_token) {
                    cb1(null, access_token, 'from_cache')
                } else {
                    request('https://api.weixin.qq.com/cgi-bin/token?grant_type=' + grant_type + '&appid=' + appid + '&secret=' + secret, (err, response, body) => {
                        cb1(err, JSON.parse(body).access_token, 'from_server')
                    })
                }
            })
    
            // 第1.3步,如果是新从服务器取的access_token,则缓存起来,否则直接返回
            steps1.push((access_token, from_where, cb1) => {
                if (from_where === 'from_cache') {
                    console.log(' === 成功从缓存中读取access_token: ' + access_token + ' ===')
                    cb1(null, access_token)
                } else if (from_where === 'from_server') {
                    cache.set('access_token', access_token, (err, success) => {
                        if (!err && success) {
                            console.log(' === 缓存已过期,从服务器中读取access_token: ' + access_token + ' ===')
                            cb1(null, access_token)
                        } else {
                            cb1(err || 'cache设置access_token时,出现未知错误')
                        }
                    })
                } else {
                    cb1('1.3获取from_where时,from_where值为空')
                }
            })
    
    
            waterfall(steps1, (err, access_token) => {
                cb(err, access_token)
            })
        })
    
    
        // 第二步,获取ticket
        steps.push((access_token, cb) => {
            let steps1 = []
    
            // 第2.1步,从缓存中读取ticket
            steps1.push((cb1) => {
                let ticket = cache.get('ticket', (err, ticket) => {
                    cb1(err, ticket)
                })
            })
    
            // 第2.2步,缓存中有ticket则直接返回,如果没有,则从服务器中读取ticket
            steps1.push((ticket, cb1) => {
                if (ticket) {
                    cb1(null, ticket, 'from_cache')
                } else {
                    request('https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=' + access_token + '&type=jsapi', (err, response, body) => {
                        cb1(err, JSON.parse(body).ticket, 'from_server')
                    })
                }
            })
    
            // 第2.3步,如果新从服务器取的ticket,则缓存起来,否则直接返回
            steps1.push((ticket, from_where, cb1) => {
                if (from_where === 'from_cache') {
                    console.log(' === 成功从缓存中读取ticket: ' + ticket + ' ===')
                    cb1(null, ticket)
                } else if (from_where === 'from_server') {
                    cache.set('ticket', ticket, (err, success) => {
                        if (!err && success) {
                            console.log(' === 缓存已过期,从服务器中读取ticket: ' + ticket + ' ===');
                            cb1(null, ticket)
                        } else {
                            cb1(err || 'cache设置ticket时,出现未知错误')
                        }
                    })
                } else {
                    cb1('2.3获取from_where时,from_where值为空')
                }
            })
    
            waterfall(steps1, (err, ticket) => {
                cb(err, ticket)
            })
        })
    
    
        // 第三步,生成签名
        steps.push((ticket, cb) => {
            let jsapi_ticket = ticket
            let nonce_str = '123456'
            let timestamp = Math.floor((new Date().getTime())/1000)
            let url = req.query.url
    
            let str = 'jsapi_ticket=' + jsapi_ticket + '&noncestr=' + nonce_str + '&timestamp=' + timestamp + '&url=' + url
            console.log('signature ' + str)
            let signature = sha1(str)
            console.log('==> ' + signature)
            cb(null, {
                appId: appid,
                timestamp: timestamp,
                nonceStr: nonce_str,
                signature: signature,
                ticket: ticket
            })
        })
    
        waterfall(steps, (err, data) => {
            res.setHeader("Content-Type", "text/json");
            res.setHeader("Access-Control-Allow-Origin", "*");
            if (err) {
                res.send({status: 'error', data: err})
            } else {
                res.send({status: 'success', data: data})
            }
        })
    })
    
    app.use('/wxJssdk/public', express.static('public'))
    
    var server = app.listen(3000, function () {
        var host = server.address().address;
        var port = server.address().port;
    
        console.log('Example app listening at http://%s:%s', host, port);
    })
    

    出处是:https://www.jb51.net/article/118729.htm

    一定要注意,生成签名的部分打log,然后和微信公众平台上的签名验证工具对比一下,比如时间戳位数,url对不对等

    3. 在H5中导入

    <script src="./js/jquery-1.11.1.js"></script>
    <script src="http://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>
    <script>
    
        var currentUrl =  encodeURIComponent(location.href.split('#')[0])
        $.get('http://xxxx.com/wxJssdk/getJssdk?url=' + currentUrl, function (data, status) {
    
            wx.config({
                debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
                appId: data.data.appId, // 必填,公众号的唯一标识
                timestamp: data.data.timestamp, // 必填,生成签名的时间戳
                nonceStr: data.data.nonceStr, // 必填,生成签名的随机串
                signature: data.data.signature,// 必填,签名
                jsApiList: ['onMenuShareTimeline','onMenuShareAppMessage'] // 必填,需要使用的JS接口列表
            });
    
            console.log('wx finish');
            wx.ready(function(){
                console.log('wx ready');
                // alert('wx ready')
    
                wx.error(function(res){
                    // config 信息验证失败会执行 error 函数,如签名过期导致验证失败,具体错误信息可以打开 config 的 debug 模式查看,也可以在返回的 res 参数中查看,对于 SPA 可以在这里更新签名。
                    console.log("errorMSG:"+res);
                });
    
                wx.checkJsApi({
                    jsApiList: ['onMenuShareTimeline','onMenuShareAppMessage'], // 需要检测的JS接口列表,所有JS接口列表见附录2,
                    success: function(res) {
                        // 以键值对的形式返回,可用的api值true,不可用为false
                        // 如:{"checkResult":{"chooseImage":true},"errMsg":"checkJsApi:ok"}
                        console.log('res'+ res);
                    }
                });
    
                var shareParam = {
                    title: '刘彦博李茜婷的结婚请柬', // 分享标题
                    // desc: '邀请您的参加', // 分享描述
                    link: 'http://www.realank.com/wedding/', // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
                    imgUrl: 'http://www.realank.com/wedding/headimgwx.jpg', // 分享图标
                    // type: 'link', // 分享类型,music、video或link,不填默认为link
                    // dataUrl: '', // 如果type是music或video,则要提供数据链接,默认为空
                    trigger: function (res) {
                        console.log('用户点击发送给朋友');
                    },
                    success: function (res) {
                        console.log('已分享');
                    },
                    cancel: function (res) {
                        console.log('已取消');
                    },
                    fail: function (res) {
                        console.log(JSON.stringify(res));
                    }
                };
    
                wx.onMenuShareTimeline(shareParam);
                wx.onMenuShareAppMessage(shareParam);
    
            });
    
        })
    </script>
    
    

    这里使用的是ajax请求的,请求签名的时候,需要加上一个url参数,取当前url一定要使用encodeURIComponent(location.href.split('#')[0])方法啊,忘记从哪个网站上看到的了,试了好几个小时,因为这个大哥茅塞顿开,好人啊

    然后在微信web开发者工具中测试就能通过,但是真机没法通过,因为个人微信公众号没有权限!!!

    相关文章

      网友评论

          本文标题:在前端艰难爬行之H5网页支持微信分享

          本文链接:https://www.haomeiwen.com/subject/xuqiyftx.html