欧美精品一区二区三区在线播放_国产精品久久久久永久免费观看_www一区二区_奇米影_超碰最新在线_国产精品一区二区三区不卡视频

石家莊網(wǎng)絡(luò)公司電話

徹底搞懂微信小程序登錄流程-附小程序和服務(wù)端代碼

2020-01-15 08:59實(shí)搜網(wǎng)絡(luò)


微信小程序登錄是很適用的功能,相信大家對這個(gè)功能的實(shí)現(xiàn)很感興趣,那么,今天就為大家介紹下微信小程序登錄的開發(fā)實(shí)現(xiàn),有什么問題請聯(lián)系實(shí)搜客服,實(shí)搜客為您解答小程序的各種難題。

微信小程序登錄流程實(shí)現(xiàn)

用戶登錄是大部分完整App必備的流程,一個(gè)簡單的用戶系統(tǒng)需要關(guān)注至少這些層面

安全性(加密)

持久化登錄態(tài)(類似cookie)

登錄過期處理

確保用戶唯一性,避免出現(xiàn)多賬號

授權(quán)

綁定用戶昵稱頭像等信息

綁定手機(jī)號(實(shí)名和密保方式)

很多的業(yè)務(wù)需求都可以抽象成Restful接口配合CRUD操作

但登錄流程卻是錯(cuò)綜復(fù)雜,各個(gè)平臺有各自的流程,反倒成了項(xiàng)目中費(fèi)時(shí)間的部分,比如小程序的登錄流程


對于一個(gè)從零開始的項(xiàng)目來說,搞定登錄流程,就是一個(gè)好的開始,一個(gè)好的開始,就是成功的一半

本文就以微信小程序這個(gè)平臺,講述一個(gè)完整的自定義用戶登錄流程,一起來啃這塊難啃的骨頭

名詞解釋

先給登錄流程時(shí)序圖中出現(xiàn)的名詞簡單做一個(gè)解釋

code臨時(shí)登錄憑證,有效期五分鐘,通過wx.login()獲取

session_key會(huì)話密鑰,服務(wù)端通過code2Session獲取

openId用戶在該小程序下的用戶唯一標(biāo)識,永遠(yuǎn)不變,服務(wù)端通過code獲取

unionId用戶在同一個(gè)微信開放平臺帳號(公眾號,小程序,網(wǎng)站,移動(dòng)應(yīng)用)下的唯一標(biāo)識,永遠(yuǎn)不變

appId小程序唯一標(biāo)識

appSecret小程序的appsecret,可以和code,appId一起換取session_key

其他名詞

rawData不包括敏感信息的原始數(shù)據(jù)字符串,用于計(jì)算簽名

encryptedData包含敏感信息的用戶信息,是加密的

signature用于校驗(yàn)用戶信息是否無篡改

iv加密算法的初始向量

哪些信息是敏感信息呢?手機(jī)號,openId,unionId,可以看出這些值都可以唯一定位一個(gè)用戶,而昵稱,頭像這些不能定位用戶的都不是敏感信息

小程序登錄相關(guān)函數(shù)

wx.login

wx.getUserInfo

wx.checkSession

小程序的promise

我們發(fā)現(xiàn)小程序的異步接口都是success和fail的回調(diào),很容易寫出回調(diào)地獄

因此可以先簡單實(shí)現(xiàn)一個(gè)wx異步函數(shù)轉(zhuǎn)成promise的工具函數(shù)

constpromisify=original=>{

returnfunction(opt){

returnnewPromise((resolve,reject)=>{

opt=Object.assign({

success:resolve,

fail:reject

},opt)

original(opt)

})

}

}

這樣我們就可以這樣調(diào)用函數(shù)了

promisify(wx.getStorage)({key:'key'}).then(value=>{

//success

}).catch(reason=>{

//fail

})

服務(wù)端實(shí)現(xiàn)

本demo的服務(wù)端實(shí)現(xiàn)基于express.js

注意,為了demo的簡潔性,服務(wù)端使用js變量來保存用戶數(shù)據(jù),也就是說如果重啟服務(wù)端,用戶數(shù)據(jù)就清空了

如需持久化存儲(chǔ)用戶數(shù)據(jù),可以自行實(shí)現(xiàn)數(shù)據(jù)庫相關(guān)邏輯

//存儲(chǔ)所有用戶信息

constusers={

//openId作為索引

openId:{

//數(shù)據(jù)結(jié)構(gòu)如下

openId:'',//理論上不應(yīng)該返回給前端

sessionKey:'',

nickName:'',

avatarUrl:'',

unionId:'',

phoneNumber:''

}

}

app

.use(bodyParser.json())

.use(session({

secret:'alittlegirl',

resave:false,

saveUninitialized:true

}))

小程序登錄

我們先實(shí)現(xiàn)一個(gè)基本的oauth授權(quán)登錄

oauth授權(quán)登錄主要是code換取openId和sessionKey的過程

前端小程序登錄

寫在app.js中

login(){

console.log('登錄')

returnutil.promisify(wx.login)().then(({code})=>{

console.log(`code:${code}`)

returnhttp.post('/oauth/login',{

code,

type:'wxapp'

})

})

}

服務(wù)端實(shí)現(xiàn)oauth授權(quán)

服務(wù)端實(shí)現(xiàn)上述/oauth/login這個(gè)接口

app


.post('/oauth/login',(req,res)=>{


varparams=req.body


var{code,type}=params


if(type==='wxapp'){


//code換取openId和sessionKey的主要邏輯


axios.get('https://api.weixin.qq.com/sns/jscode2session',{


params:{


appid:config.appId,


secret:config.appSecret,


js_code:code,


grant_type:'authorization_code'


}


}).then(({data})=>{


varopenId=data.openid


varuser=users[openId]


if(!user){


user={


openId,


sessionKey:data.session_key


}


users[openId]=user


console.log('新用戶',user)


}else{


console.log('老用戶',user)


}


req.session.openId=user.openId


req.user=user


}).then(()=>{


res.send({


code:0


})


})


}else{


thrownewError('未知的授權(quán)類型')


}


})

獲取用戶信息

登錄系統(tǒng)中都會(huì)有一個(gè)重要的功能:獲取用戶信息,我們稱之為getUserInfo

如果已登錄用戶調(diào)用getUserInfo則返回用戶信息,比如昵稱,頭像等,如果未登錄則返回'用戶未登錄'

也就是說此接口還有判斷用戶是否登錄的功效...

小程序的用戶信息一般存儲(chǔ)在app.globalData.userInfo中(模板如此)

我們在服務(wù)端加上前置中間件,通過session來獲取對應(yīng)的用戶信息,并放在req對象中

app


.use((req,res,next)=>{


req.user=users[req.session.openId]


next()


})


然后實(shí)現(xiàn)/user/info接口,用來返回用戶信息


app


.get('/user/info',(req,res)=>{


if(req.user){


returnres.send({


code:0,


data:req.user


})


}


thrownewError('用戶未登錄')


})


小程序調(diào)用用戶信息接口


getUserInfo(){


returnhttp.get('/user/info').then(response=>{


letdata=response.data


if(data&&typeofdata==='object'){


//獲取用戶信息成功則保存到全局


this.globalData.userInfo=data


returndata


}


returnPromise.reject(response)


})


}


專為小程序發(fā)請求設(shè)計(jì)的庫

小程序代碼通過http.get,http.post這樣的api來發(fā)請求,背后使用了一個(gè)請求庫,@chunpu/http1是一個(gè)專門為小程序設(shè)計(jì)的http請求庫,可以在小程序上像axios一樣發(fā)請求,支持?jǐn)r截器等強(qiáng)大功能,甚至比axios更順手

初始化方法如下

importhttpfrom'@chunpu/http'


http.init({


baseURL:'http://localhost:9999',//定義baseURL,用于本地測試


wx//標(biāo)記是微信小程序用


})


具體使用方法可參照文檔https://github.com/chunpu/http#readme

自定義登錄態(tài)持久化

瀏覽器有cookie,然而小程序沒有cookie,那怎么模仿出像網(wǎng)頁這樣的登錄態(tài)呢?

這里要用到小程序自己的持久化接口,也就是setStorage和getStorage

為了方便各端共用接口,或者直接復(fù)用web接口,我們自行實(shí)現(xiàn)一個(gè)簡單的讀cookie和種cookie的邏輯

先是要根依據(jù)返回的httpresponseheaders來種上cookie,此處我們用到了@chunpu/http中的response攔截器,和axios用法一樣

http.interceptors.response.use(response=>{


//種cookie


var{headers}=response


varcookies=headers['set-cookie']||''


cookies=cookies.split(/,*/).reduce((prev,item)=>{


item=item.split(/;*/)[0]


varobj=http.qs.parse(item)


returnObject.assign(prev,obj)


},{})


if(cookies){


returnutil.promisify(wx.getStorage)({


key:'cookie'


}).catch(()=>{}).then(res=>{


res=res||{}


varallCookies=res.data||{}


Object.assign(allCookies,cookies)


returnutil.promisify(wx.setStorage)({


key:'cookie',


data:allCookies


})


}).then(()=>{


returnresponse


})


}


returnresponse


})

當(dāng)然我們還需要在發(fā)請求的時(shí)候帶上所有cookie,此處用的是request攔截器

http.interceptors.request.use(config=>{

//給請求帶上cookie

returnutil.promisify(wx.getStorage)({


key:'cookie'


}).catch(()=>{}).then(res=>{


if(res&&res.data){


Object.assign(config.headers,{


Cookie:http.qs.stringify(res.data,';','=')


})


}


returnconfig


})


})


登錄態(tài)的有效期

我們知道,瀏覽器里面的登錄態(tài)cookie是有失效時(shí)間的,比如一天,七天,或者一個(gè)月,也許有朋友會(huì)提出疑問,直接用storage的話,小程序的登錄態(tài)有效期怎么辦?

小程序已經(jīng)幫我們實(shí)現(xiàn)好了session有效期的判斷wx.checkSession

它比cookie更智能,官方文檔描述如下

通過wx.login接口獲得的用戶登錄態(tài)擁有一定的時(shí)效性。用戶越久未使用小程序,用戶登錄態(tài)越有可能失效。反之如果用戶一直在使用小程序,則用戶登錄態(tài)一直保持有效

也就是說小程序還會(huì)幫我們自動(dòng)renew咱們的登錄態(tài),簡直是人工智能cookie,點(diǎn)個(gè)贊?

那具體在前端怎么操作呢?代碼寫在app.js中

onLaunch:function(){


util.promisify(wx.checkSession)().then(()=>{


console.log('session生效')


returnthis.getUserInfo()


}).then(userInfo=>{


console.log('登錄成功',userInfo)


}).catch(err=>{


console.log('自動(dòng)登錄失敗,重新登錄',err)


returnthis.login()


}).catch(err=>{


console.log('手動(dòng)登錄失敗',err)


})


}


要注意,這里的session不僅是前端的登錄態(tài),也是后端session_key的有效期,前端登錄態(tài)失效了,那后端也失效了需要更新session_key。理論上小程序也可以自定義登錄失效時(shí)間策略,但這樣的話我們需要考慮開發(fā)者自己的失效時(shí)間和小程序接口服務(wù)的失效時(shí)間,還不如保持統(tǒng)一來的簡單。確保每個(gè)Page都能獲取到userInfo,如果在新建小程序項(xiàng)目中選擇建立普通快速啟動(dòng)模板,我們會(huì)得到一個(gè)可以直接運(yùn)行的模板,點(diǎn)開代碼一看,大部分代碼都在處理userInfo....


注釋里寫著

由于getUserInfo是網(wǎng)絡(luò)請求,可能會(huì)在Page.onLoad之后才返回,所以此處加入callback以防止這種情況。

但這樣的模板并不科學(xué),這樣僅僅是考慮了首頁需要用戶信息的情況,如果掃碼進(jìn)入的頁面也需要用戶信息呢?還有直接進(jìn)入跳轉(zhuǎn)的未支付頁活動(dòng)頁等...

如果每個(gè)頁面都這樣判斷一遍是否加載完用戶信息,代碼顯得過于冗余

此時(shí)我們想到了jQuery的ready函數(shù)$(function),只要documentready了,就可以直接執(zhí)行函數(shù)里面的代碼,如果document還沒ready,就等到ready后執(zhí)行代碼

就這個(gè)思路了!我們把小程序的App當(dāng)成網(wǎng)頁的document

我們的目標(biāo)是可以這樣在Page中不會(huì)出錯(cuò)的獲取userInfo

Page({


data:{


userInfo:null


},


onLoad:function(){


app.ready(()=>{


this.setData({


userInfo:app.globalData.userInfo


})


})


}


})


此處我們使用min-ready2來實(shí)現(xiàn)此功能,代碼實(shí)現(xiàn)依然寫在app.js中

importReadyfrom'min-ready'


constready=Ready()


App({


getUserInfo(){


//獲取用戶信息作為全局方法


returnhttp.get('/user/info').then(response=>{


letdata=response.data


if(data&&typeofdata==='object'){


this.globalData.userInfo=data


//獲取userInfo成功的時(shí)機(jī)就是appready的時(shí)機(jī)


ready.open()


returndata


}


returnPromise.reject(response)


})


},


ready(func){


//把函數(shù)放入隊(duì)列中


ready.queue(func)


}


})


綁定用戶信息和手機(jī)號

僅僅獲取用戶的openId是遠(yuǎn)遠(yuǎn)不夠的,openId只能標(biāo)記用戶,連用戶的昵稱和頭像都拿不到。如何獲取這些用戶信息然后存到后端數(shù)據(jù)庫中呢?

我們在服務(wù)端實(shí)現(xiàn)這兩個(gè)接口,綁定用戶信息,綁定用戶手機(jī)號

app


.post('/user/bindinfo',(req,res)=>{


varuser=req.user


if(user){


var{encryptedData,iv}=req.body


varpc=newWXBizDataCrypt(config.appId,user.sessionKey)


vardata=pc.decryptData(encryptedData,iv)


Object.assign(user,data)


returnres.send({


code:0


})


}


thrownewError('用戶未登錄')


})




.post('/user/bindphone',(req,res)=>{


varuser=req.user


if(user){


var{encryptedData,iv}=req.body


varpc=newWXBizDataCrypt(config.appId,user.sessionKey)


vardata=pc.decryptData(encryptedData,iv)


Object.assign(user,data)


returnres.send({


code:0


})


}


thrownewError('用戶未登錄')


})


小程序個(gè)人中心wxml實(shí)現(xiàn)如下




wx:if='{{!userInfo.nickName}}'


type='primary'


open-type='getUserInfo'


bindgetuserinfo='bindUserInfo'>獲取頭像昵稱




{{userInfo.nickName}}






wx:if='{{!userInfo.phoneNumber}}'


type='primary'


style='margin-top:20px;'


open-type='getPhoneNumber'


bindgetphonenumber='bindPhoneNumber'>綁定手機(jī)號


{{userInfo.phoneNumber}}



小程序中的bindUserInfo和bindPhoneNumber函數(shù),根據(jù)微信最新的策略,這倆操作都需要用戶點(diǎn)擊按鈕統(tǒng)一授權(quán)才能觸發(fā)


bindUserInfo(e){


vardetail=e.detail


if(detail.iv){


http.post('/user/bindinfo',{


encryptedData:detail.encryptedData,


iv:detail.iv,


signature:detail.signature


}).then(()=>{


returnapp.getUserInfo().then(userInfo=>{


this.setData({


userInfo:userInfo


})


})


})


}


},


bindPhoneNumber(e){


vardetail=e.detail


if(detail.iv){


http.post('/user/bindphone',{


encryptedData:detail.encryptedData,


iv:detail.iv


}).then(()=>{


returnapp.getUserInfo().then(userInfo=>{


this.setData({


userInfo:userInfo


})


})


})


}


}

(免責(zé)聲明:本網(wǎng)站內(nèi)容主要網(wǎng)絡(luò),不保證有關(guān)資料的準(zhǔn)確性及可靠性,讀者在使用前請進(jìn)一步核實(shí),并對任何自主決定的行為負(fù)責(zé)。本網(wǎng)站概不負(fù)任何法律責(zé)任)


標(biāo)簽:


34
分享到:
主站蜘蛛池模板: 国产精品二区三区 | www..com18午夜观看 | 成人精品鲁一区一区二区 | 在线伊人| 一本岛道一二三不卡区 | 亚洲成人一区二区三区 | 欧美综合在线观看 | 日韩at| 九七午夜剧场福利写真 | 日韩三区在线观看 | 亚洲导航深夜福利涩涩屋 | 日日骚网 | 91在线精品视频 | 亚洲综合无码一区二区 | 天天看天天操 | 亚洲人免费视频 | 国产黄色大片网站 | 青青草视频网站 | 成人国产精品久久久 | 91日韩 | 免费午夜视频在线观看 | 91夜色在线观看 | 精品96久久久久久中文字幕无 | 国产精品成人一区二区 | 亚洲人免费视频 | 国产精品毛片一区二区三区 | 午夜精品久久久久久久星辰影院 | 日韩午夜电影在线观看 | 精品亚洲一区二区三区四区五区 | 婷婷综合在线 | 人人干人人艹 | 久久久夜 | 成人依人 | 成人久久18免费网站麻豆 | 国产精品视频久久 | 宅男噜噜噜66一区二区 | 欧美一级二级三级视频 | 亚洲国产欧美一区二区三区久久 | 国产精品久久国产精品99 gif | 精品久久99 | 在线视频第一页 |