這是前端切圖仔寫的第一篇文章,因爲才疏學淺,內容可能過於膚淺與沒什麼學習價值,就當紀錄事情好了。前端
前幾天接到貴公司前端組長的一個需求,爲了實現多個域名的登錄,將目前的登錄模塊拆分出來,作成單點登錄。react
1.跳轉到微信的受權頁面,若用戶贊成受權,則跳轉至指定的redirect_uri並帶上code參數webpack
2.將code傳給後端換取網頁受權的access_tokenios
3.後端獲取用戶信息(分爲兩種,一種是靜默受權獲取用戶的openid,一種是網頁受權獲取用戶的基本信息)web
4.根據用戶的token是否失效來判斷用戶是否須要再次登錄axios
以上流程只針對該項目,詳細請參考官方文檔微信網頁受權小程序
因爲這個單點登錄代碼很少,實現的功能點也少,因此簡單地搭了個webpack。後端
項目代碼中包含小程序跳轉與其餘一些業務需求,但對這篇文章可有可無,故刪減api
目錄結構以下 跨域
下面就登錄受權流程來講明各個目錄的代碼
可能有同窗注意到上面有兩個獲取code的操做,這是爲何呢?由於貴公司的項目爲知識付費,用戶可在多個公衆號上面獲取知識,那麼怎麼判斷在不一樣公衆號聽課的用戶爲同一個呢?
是這樣的。用戶在不一樣公衆號登錄會有不一樣的openid。那若是用戶只在一個公衆號登錄不就能夠惟一肯定用戶了嗎?嘻嘻,可真是個小機靈鬼。因此get_code_1就是獲取用戶在用於惟一標識用戶的公衆號登錄的code,而get_code_2纔是獲取用戶在目前公衆號登錄的code。
get_code_1:跳轉到受權頁面,獲取用戶在用於惟一標識用戶的公衆號登錄的code1。因爲只須要獲取openid,故爲靜默受權。
const wurl = require('wurl') // 解析連接參數的庫
const tx_appid = 用於標識用戶的公衆號的appid
const href = location.href
const code = wurl('?code', href)
let no_auth_host, appid
function handleParams () {
no_auth_host = wurl('?no_auth_host', href)
appid = wurl('?appid', href)
window.localStorage.setItem('no_auth_host', no_auth_host)
// no_auth_host字段爲登錄成功後跳轉的域名
window.localStorage.setItem('appid', appid)
// 目前公衆號的appid,用於獲取code2
}
if (window.localStorage.getItem('callback-accessed')) {
window.localStorage.removeItem('callback-accessed')
}
// callback-accessed字段爲了不用戶回退到登錄頁面,具體用法在callback部分講
// 已經拿到code1
if (code) {
handleParams()
location.replace(`${location.origin}/tlogin1/_get_code2/?code1=${code}`)
} else {
location.replace(`https://open.weixin.qq.com/connect/oauth2/authorize?appid=${tx_appid}&redirect_uri=${encodeURIComponent(href)}&response_type=code&scope=snsapi_base#wechat_redirect`)
}
複製代碼
get_code_2:跳轉到受權頁面,獲取用戶目前公衆號登錄的code2。因爲須要獲取用戶信息,故爲網頁受權。
const wurl = require('wurl')
const href = location.href
const code1 = wurl('?code1', href)
const appid = window.localStorage.getItem('appid')
function getCode2Redirect () {
return encodeURIComponent(`${location.origin}/tlogin1/_callback/?code1=${code1}`)
}
location.replace(`https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appid}&redirect_uri=${getCode2Redirect()}&response_type=code&scope=snsapi_userinfo&component_appid=wxa56b4f5c89803c1f#wechat_redirect`)
複製代碼
callback:將code一、code2傳給後端換取登錄憑證access_code
import { wxConfig } from '../service'
const wurl = require('wurl')
const axios = require('axios')
const href = location.href
const code1 = wurl('?code1',href)
const code2 = wurl('?code', href)
const no_auth_host = window.localStorage.getItem('no_auth_host')
window.addEventListener('pageshow', (e) => {
if (e.persisted) {
// ios的微信回退
// 回退會致使微信sdk的config出問題,因此reload
location.reload()
} else {
// 微信sdk配置
wxConfig()
main()
}
})
function fetchAccessCode () {
axios.get('/api/account/auth/unique_weixin_mp_code', {
params: {
code1: code1,
code2: code2,
}
})
.then(res => {
const data = res.data.data
if (res.data.code === 0) {
window.localStorage.setItem('callback-accessed', 1)
// 獲取access_code成功,本地存儲callback-accessed
location.assign(`${no_auth_host}/auth/exchange_token?access_code=${data.access_code}`)
// 爲了安全,跳轉到原先域名的/auth/exchange_token,用access_code換取token,
}
})
.catch(err => {
alert('系統繁忙,請稍後再試' + err.message)
window.wx.ready(() => {
window.wx.closeWindow()
})
window.wx.closeWindow()
})
}
function main () {
const callback_accessed = window.localStorage.getItem('callback-accessed')
if (callback_accessed) {
// 用戶回退到登錄頁面,此時無需再走什麼流程,直接關閉頁面便可
console.log('will close window...')
window.wx.ready(() => {
window.wx.closeWindow()
})
window.wx.closeWindow()
return
}
fetchAccessCode()
}
複製代碼
service:service裏面是全局配置函數。這裏只說明微信sdk的配置
const APP_ID = window.localStorage.getItem('appid')
export function wxConfig() {
return axios.get('/api/weixin/jssdk_config', {
params: {
url: location.href,
appid: APP_ID,
},
})
.then(res => {
const _config = res.data.data
window.wx.config({
debug: false, // debug設爲true可彈出一些配置信息
appId: APP_ID,
timestamp: _config.timestamp,
nonceStr: _config.nonceStr,
signature: _config.signature,
jsApiList: ['closeWindow'],
})
})
}
複製代碼
更多關於微信sdk配置,請參考官方文檔微信JS-SDK說明文檔
微信JSAPI:'permission denied'錯誤
當用戶登錄成功跳轉到別的域名後,點擊微信的回退按鈕,這時回退到登錄頁面而且無需進行任何操做,調用微信的closeWindow便可。但在調用時出現了'permission denied'錯誤。
官方對於該錯誤的說明是:該公衆號沒有權限使用這個JSAPI,或者是調用的JSAPI沒有傳入config的jsApiList參數中(部分接口須要認證以後才能使用)。
由於目前正在運行的項目用公衆號A調用該接口沒有問題,故排除第一個可能。 又因代碼確實將'closeWindow'傳入jsApiList。故排除第二個可能。
那麼??問題出在哪?
個人猜測:
用青花瓷代理時,https接口沒法調用
實際上是這樣的,像青花瓷這種抓包工具,默承認以抓http的請求,若是須要抓取https的請求,須要安裝證書。安裝教程網上有不少哦~
後記:爲了不網頁跨域名再次登錄產生的用戶體驗不友好,在對某些跳域名頁面,採用連接上帶上token參數,又爲了不token泄露,使用base64對其加密。具體實現邏輯爲:
加密
const conbine = `${window.btoa(token)}|${window.btoa(now + expires)}`
複製代碼
window.btoa(conbine).replace(/=/g, this.salt)
複製代碼
解密,即加密過程的逆序