最近在做微信网页版开发,整理出微信 H5 鉴权常见坑位,流程图分析如下,后附简易代码实现
逻辑实现:通过重定向授权从地址栏拿取 code
并缓存,不可重复调用,不然会报 code used
官方传送门 => 微信开放文档-网页授权
async getAccessToken(redirectUri) {
const code = $$.getQueryValue('code', redirectUri) || $$.getStore('code')
if (code) {
const $ticketToken = await $$.setStoreAsync('ticketToken', this.getTicketToken)
$$.setStore('code', code, true).setStoreAsync('token', this.getAuthToken.bind(this, code))
return Promise.resolve(this.getConfigInfo($ticketToken, redirectUri))
} else {
const url = ('https://open.weixin.qq.com/connect/oauth2/authorize?' + $$.serializeParams({
appid,
redirect_uri: encodeURIComponent(redirectUri),
response_type: 'code',
scope: 'snsapi_userinfo',
state: '200'
}) + '#wechat_redirect')
return redirect(url)
}
}
逻辑实现:调用https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
, 获取 access_token
并缓存,实现signature
签名
官方传送门 => 微信开放文档-获取access_token
async getTicketToken() {
const { access_token } = await get(prefix + '/cgi-bin/token', {
appid,
secret,
grant_type: 'client_credential'
})
return Promise.resolve(access_token)
},
async getTicket(ticketToken) {
const { ticket, errcode } = await get(prefix + '/cgi-bin/ticket/getticket', {
access_token: ticketToken,
type: 'jsapi'
})
return Promise.resolve(ticket)
}
逻辑实现:主要参数,第一步的回调 url
,第二步的 ticket
,并生成指定字段,返回配置信息给 wx.config
官方传送门 => JS-SDK使用权限签名算法
async getConfigInfo(ticketToken, redirectUri) {
const ticket = await $$.setStoreAsync('jsapiTicket', this.getTicket.bind(this, ticketToken))
const req = {
noncestr: $$.randomStr(),
'jsapi_ticket': ticket,
timestamp: Date.parse(new Date()),
url: redirectUri
}
const signature = [$$.objSortByASCII, $$.serializeParams, $$.cryptoBase().sha1].reduce(function(target, callBack) {
return callBack(target)
}, Object.assign({}, req))
return Promise.resolve({
signature,
nonceStr: req.noncestr,
timestamp: req.timestamp,
appId: appid
})
}
setStoreAsync
首次请求,调用接口,之后返回缓存信息,采用 store2 进行持久化存储方案
async setStoreAsync(key, callBack) {
let data = {}
if (!this.store.get(key)) {
data = await callBack()
this.store.set(key, data)
}
return Promise.resolve(this.store.get(key))
}
以下代码经过封装,仅供参考,如有意向了解更多,欢迎评论分享 : -)
import $$ from '~/utils/' // 常用工具类实现
const prefix = '/api-wx' // 固定请求头,本地代理实现跨域
const appid = ''
const secret = ''
export default (axios, post, get, store, redirect) => {
return {
async getAccessToken(redirectUri) {
const code = $$.getQueryValue('code', redirectUri) || $$.getStore('code')
if (code) {
const $ticketToken = await $$.setStoreAsync('ticketToken', this.getTicketToken)
$$.setStore('code', code, true).setStoreAsync('token', this.getAuthToken.bind(this, code))
return Promise.resolve(this.getConfigInfo($ticketToken, redirectUri))
} else {
const url = ('https://open.weixin.qq.com/connect/oauth2/authorize?' + $$.serializeParams({
appid,
redirect_uri: encodeURIComponent(redirectUri),
response_type: 'code',
scope: 'snsapi_userinfo',
state: '200'
}) + '#wechat_redirect')
return redirect(url)
}
},
async getAuthToken(code) {
const token = await get(prefix + '/sns/oauth2/access_token', {
appid,
secret,
code,
grant_type: 'authorization_code'
})
return Promise.resolve(token)
},
async refreshToken() {
const token = await get(prefix + '/sns/oauth2/refresh_token', {
appid,
grant_type: 'refresh_token',
refresh_token: $$.getStore('token')['refresh_token']
})
return Promise.resolve($$.setStore({ token }))
},
async getTicketToken() {
const { access_token } = await get(prefix + '/cgi-bin/token', {
appid,
secret,
grant_type: 'client_credential'
})
return Promise.resolve(access_token)
},
async getTicket(ticketToken) {
const { ticket, errcode } = await get(prefix + '/cgi-bin/ticket/getticket', {
access_token: ticketToken,
type: 'jsapi'
})
return Promise.resolve(ticket)
},
async getConfigInfo(ticketToken, redirectUri) {
const ticket = await $$.setStoreAsync('jsapiTicket', this.getTicket.bind(this, ticketToken))
const req = {
noncestr: $$.randomStr(),
'jsapi_ticket': ticket,
timestamp: Date.parse(new Date()),
url: redirectUri
}
const signature = [$$.objSortByASCII, $$.serializeParams, $$.cryptoBase().sha1].reduce(function(target, callBack) {
return callBack(target)
}, Object.assign({}, req))
return Promise.resolve({
signature,
nonceStr: req.noncestr,
timestamp: req.timestamp,
appId: appid
})
}
}
}