微信公众号开发+Oauth 登陆

1,187 阅读3分钟

1.微信公众号 消息对话实现

  1. mp.weixin.qq.com 扫描登录
  2. 首页点击 web开发者工具
  3. 绑定开发者自己的手机号
  4. 安全中心-设置自己服务器的白名单ip
  5. 首页-基本配置-获取AppID和 AppSecret
  6. 开发者工具- 公众号平台测试账号 设置 url和token image.png
  7. 关注公众号:微信公众平台测试号,进行消息发送测试

验证微信接口配置的逻辑 (加密算法的逻辑)

image.png

其实整个过程可以直接返回微信给的echostr,不需要加密校验。验证加密只是安全考虑(防止非微信的第三方攻击服务器,或者爬虫)
整个过程是:服务器验证微信是否安全。

1.验证代码实现

实现代码1(记得使用80端口)

//conf.js
module.exports = {
    appid:'xxxxx',
    appsecret:'xxxxx',
    token:'kaikeba',
}
//index.js
const Koa = require('koa')
const Router = require('koa-router')
const static = require('koa-static')
const xml2js = require('xml2js')
const app = new Koa()
const url = require('url')
const conf = require('./conf')

const crypto = require('crypto')
const xmlParser = require('koa-xml-body')
app.use(xmlParser())//每次接口返回都会,自动把xml格式转化为json

const router = new Router()
app.use(static(__dirname + '/'))

// 验证
router.get('/wechat', ctx => {
    console.log('微信认证...', ctx.url)
    const {
        query
    } = url.parse(ctx.url, true)
    const {
        signature, // 微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
        timestamp, // 时间戳
        nonce, // 随机数 
        echostr // 随机字符串
    } = query
    console.log('wechat', query)

    // 将 token timestamp nonce 三个参数进行字典序排序并用sha1加密

    let str = [conf.token, timestamp, nonce].sort().join('');
    console.log('str', str)
    let strSha1 = crypto.createHash('sha1').update(str).digest('hex');

    console.log(`自己加密后的字符串为:${strSha1}`);
    console.log(`微信传入的加密字符串为:${signature}`);
    console.log(`两者比较结果为:${signature == strSha1}`);

    // 签名对比,相同则按照微信要求返回echostr参数值
    if (signature == strSha1) {
        ctx.body = echostr
    } else {
        ctx.body = "你不是微信"
    }
})

app.use(router.routes());
app.use(router.allowedMethods());
app.listen(80);

2.实现后台代码二 使用co-wechat工具

const Koa = require('koa')
const Router = require('koa-router')
const static = require('koa-static')
const bodyParser = require('koa-bodyparser');
const app = new Koa()
const conf = require('./conf')
app.use(bodyParser())
const router = new Router()
app.use(static(__dirname + '/'))
const axios = require('axios')

const crypto = require('crypto')
const wechat = require('co-wechat')
//这里同时做了:验证 +  接受消息返回内容处理,通过中间件实现
router.all('/wechat', wechat(conf).middleware(
    async message => {
        console.log('wechat:', message)
        return 'Hello World ' + message.Content
    }
))

测试方式:

  1. 开发者工具- 公众号平台测试账号 设置 url和token 点击提交测试

  2. 输入地址:www.soxstudio.top/wechat

     注意首页-接口权限,对应的接口权限是否有打开
    

image.png

高级api使用

  1. 获取token
  2. 调用微信的接口

1.自己管理token

const tokenCache = {
    access_token: '',
    updateTime: Date.now(),
    expires_in: 7200,
};
//获取token
router.get('/getTokens', async ctx => {
    const wxDomain = `https://api.weixin.qq.com`;
    const path = `/cgi-bin/token`;
    const params = `?grant_type=client_credential&appid=${conf.appid}&secret=${conf.appsecret}`;
    const url = `${wxDomain}${path}${params}`;
    const res = await axios.get(url);
    Object.assign(tokenCache, res.data, {
        updateTime: Date.now()
    });
    ctx.body = res.data
})
//获取对应的ip地址列表 注意如果没有权限的接口不用乱掉
router.get('/getcallbackip',async ctx => {
    const url = `https://api.weixin.qq.com/cgi-bin/getcallbackip?access_token=${tokenCache.access_token}`
    const res = await axios.get(url)
    console.log('getcallbackip:',res)
    ctx.body = res.data
})

2.使用co-wechat-api库实现请求拦截 与token设置判断

const WechatAPI = require('co-wechat-api');//通过第三方工具 管理token
const { ServerToken} = require('./mongoose')
const api = new WechatAPI(conf.appid, conf.appsecret);
//获取对应的ip地址列表 
router.get('/getcallbackip', async ctx => {
    var res = await api.getIp();
    console.log('res', res)  
    ctx.body = res
})

测试:

  1. 运行node index.js
  2. http://127.0.0.1:3000/getTokens
  3. http://127.0.0.1:3000/getcallbackip

全局票据

原因

  1. token接口请求有次数限制
  2. 多服务器部署时,各个服务器各自生成token

解决

使用 持久化:mongodb 或 redis

//mongoose.js
const mongoose = require('mongoose')
const {
    Schema
} = mongoose
mongoose.connect('mongodb://username:pwd@81.71.143.xxx:27017/weixin?authSource=admin', {
    useNewUrlParser: true
}, () => {
    console.log('Mongodb connected..')
})
exports.ServerToken = mongoose.model('ServerToken', {
    accessToken: String
});

//index.js
const api = new WechatAPI(
    conf.appid,
    conf.appsecret,
    // 取Token
    async () => await ServerToken.findOne(),//根据数据库
    // 存Token
    async token => await ServerToken.updateOne({}, token, { upsert: true })
)

2. Oauth交互流程

image.png

  1. 基本配置-服务器配置 设置对应的服务器验证
  2. 打开微信开发者工具 进行调试
  3. 注意一般订阅号是没有权限,需要认证-企业资质

1.服务器验证地址

image.png

2.Oauth交互流程

image.png

后台代码

const OAuth = require('co-wechat-oauth')
const oauth = new OAuth(conf.appid, conf.appsecret
    ,
    async function (openid) {
        return await ClientToken.getToken(openid)
    },
    async function (openid, token) {
        return await ClientToken.setToken(openid, token)
    }
)
/**
 * 生成用户URL
 */
router.get('/wxAuthorize', async (ctx, next) => {
    const state = ctx.query.id
    console.log('ctx...' + ctx.href)
    let redirectUrl = ctx.href
    redirectUrl = redirectUrl.replace('wxAuthorize', 'wxCallback')
    const scope = 'snsapi_userinfo'
    const url = oauth.getAuthorizeURL(redirectUrl, state, scope)
    console.log('url' + url)
    ctx.redirect(url)
})
/**
 * 用户回调方法
 */
router.get('/wxCallback', async ctx => {
    const code = ctx.query.code
    console.log('wxCallback code', code)
    const token = await oauth.getAccessToken(code)
    const accessToken = token.data.access_token
    const openid = token.data.openid
    console.log('accessToken', accessToken)
    console.log('openid', openid)
    ctx.redirect('/?openid=' + openid)
})

3. 调用原生设备的api

相关代码

后台代码


const WechatAPI = require('co-wechat-api')
const api = new WechatAPI(
    conf.appid,
    conf.appsecret,
    // 取Token
    async () => await ServerToken.findOne(),
    // 存Token
    async token => await ServerToken.updateOne({}, token, { upsert: true })
)
router.get('/getJsConfig',async ctx => {
    console.log('getJSSDK...',ctx.query)
    const res = await api.getJsConfig(ctx.query)
    ctx.body = res
})

前端代码


    <script src="js/jweixin-1.4.0.js"></script>
    //对应方法
 async getJSConfig() {
                    console.log('wx', wx)
                    const res = await axios.get('/getJSConfig', {
                        params: {
                            url: window.location.href
                        }
                    })
                    console.log('res....', res.data)
                    res.data.jsApiList = ['onMenuShareTimeline', 'onMenuShareAppMessage']
                    wx.config(res.data);
                    wx.ready(function () {
                        console.log('wx.ready......')
                    })
                    wx.getNetworkType({
                        success: function (res) {
                            // 返回网络类型2g,3g,4g,wifi
                            var networkType = res.networkType;
                            console.log('getNetworkType...', networkType)
                        }
                    })
                }

dfcc3c7ec3814fb4962786cf0196b875_tplv-k3u1fbpfcp-watermark.image2.png