久久狠狠一本精品综合网,国产精品观看在线亚洲人成网,国产精品欧美亚洲韩国日本不卡,欧美一区二区三区在线观看不卡,日本高清中文字幕一区二区三区,阿v天堂网

石家莊網絡公司電話

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

2020-01-15 08:59實搜網絡


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

微信小程序登錄流程實現

用戶登錄是大部分完整App必備的流程,一個簡單的用戶系統需要關注至少這些層面

安全性(加密)

持久化登錄態(類似cookie)

登錄過期處理

確保用戶唯一性,避免出現多賬號

授權

綁定用戶昵稱頭像等信息

綁定手機號(實名和密保方式)

很多的業務需求都可以抽象成Restful接口配合CRUD操作

但登錄流程卻是錯綜復雜,各個平臺有各自的流程,反倒成了項目中費時間的部分,比如小程序的登錄流程


對于一個從零開始的項目來說,搞定登錄流程,就是一個好的開始,一個好的開始,就是成功的一半

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

名詞解釋

先給登錄流程時序圖中出現的名詞簡單做一個解釋

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

session_key會話密鑰,服務端通過code2Session獲取

openId用戶在該小程序下的用戶唯一標識,永遠不變,服務端通過code獲取

unionId用戶在同一個微信開放平臺帳號(公眾號,小程序,網站,移動應用)下的唯一標識,永遠不變

appId小程序唯一標識

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

其他名詞

rawData不包括敏感信息的原始數據字符串,用于計算簽名

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

signature用于校驗用戶信息是否無篡改

iv加密算法的初始向量

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

小程序登錄相關函數

wx.login

wx.getUserInfo

wx.checkSession

小程序的promise

我們發現小程序的異步接口都是success和fail的回調,很容易寫出回調地獄

因此可以先簡單實現一個wx異步函數轉成promise的工具函數

constpromisify=original=>{

returnfunction(opt){

returnnewPromise((resolve,reject)=>{

opt=Object.assign({

success:resolve,

fail:reject

},opt)

original(opt)

})

}

}

這樣我們就可以這樣調用函數了

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

//success

}).catch(reason=>{

//fail

})

服務端實現

本demo的服務端實現基于express.js

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

如需持久化存儲用戶數據,可以自行實現數據庫相關邏輯

//存儲所有用戶信息

constusers={

//openId作為索引

openId:{

//數據結構如下

openId:'',//理論上不應該返回給前端

sessionKey:'',

nickName:'',

avatarUrl:'',

unionId:'',

phoneNumber:''

}

}

app

.use(bodyParser.json())

.use(session({

secret:'alittlegirl',

resave:false,

saveUninitialized:true

}))

小程序登錄

我們先實現一個基本的oauth授權登錄

oauth授權登錄主要是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'

})

})

}

服務端實現oauth授權

服務端實現上述/oauth/login這個接口

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('未知的授權類型')


}


})

獲取用戶信息

登錄系統中都會有一個重要的功能:獲取用戶信息,我們稱之為getUserInfo

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

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

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

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

app


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


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


next()


})


然后實現/user/info接口,用來返回用戶信息


app


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


if(req.user){


returnres.send({


code:0,


data:req.user


})


}


thrownewError('用戶未登錄')


})


小程序調用用戶信息接口


getUserInfo(){


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


letdata=response.data


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


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


this.globalData.userInfo=data


returndata


}


returnPromise.reject(response)


})


}


專為小程序發請求設計的庫

小程序代碼通過http.get,http.post這樣的api來發請求,背后使用了一個請求庫,@chunpu/http1是一個專門為小程序設計的http請求庫,可以在小程序上像axios一樣發請求,支持攔截器等強大功能,甚至比axios更順手

初始化方法如下

importhttpfrom'@chunpu/http'


http.init({


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


wx//標記是微信小程序用


})


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

自定義登錄態持久化

瀏覽器有cookie,然而小程序沒有cookie,那怎么模仿出像網頁這樣的登錄態呢?

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

為了方便各端共用接口,或者直接復用web接口,我們自行實現一個簡單的讀cookie和種cookie的邏輯

先是要根依據返回的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


})

當然我們還需要在發請求的時候帶上所有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


})


})


登錄態的有效期

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

小程序已經幫我們實現好了session有效期的判斷wx.checkSession

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

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

也就是說小程序還會幫我們自動renew咱們的登錄態,簡直是人工智能cookie,點個贊?

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

onLaunch:function(){


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


console.log('session生效')


returnthis.getUserInfo()


}).then(userInfo=>{


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


}).catch(err=>{


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


returnthis.login()


}).catch(err=>{


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


})


}


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


注釋里寫著

由于getUserInfo是網絡請求,可能會在Page.onLoad之后才返回,所以此處加入callback以防止這種情況。

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

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

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

就這個思路了!我們把小程序的App當成網頁的document

我們的目標是可以這樣在Page中不會出錯的獲取userInfo

Page({


data:{


userInfo:null


},


onLoad:function(){


app.ready(()=>{


this.setData({


userInfo:app.globalData.userInfo


})


})


}


})


此處我們使用min-ready2來實現此功能,代碼實現依然寫在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成功的時機就是appready的時機


ready.open()


returndata


}


returnPromise.reject(response)


})


},


ready(func){


//把函數放入隊列中


ready.queue(func)


}


})


綁定用戶信息和手機號

僅僅獲取用戶的openId是遠遠不夠的,openId只能標記用戶,連用戶的昵稱和頭像都拿不到。如何獲取這些用戶信息然后存到后端數據庫中呢?

我們在服務端實現這兩個接口,綁定用戶信息,綁定用戶手機號

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('用戶未登錄')


})


小程序個人中心wxml實現如下




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'>綁定手機號


{{userInfo.phoneNumber}}



小程序中的bindUserInfo和bindPhoneNumber函數,根據微信最新的策略,這倆操作都需要用戶點擊按鈕統一授權才能觸發


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


})


})


})


}


}

(免責聲明:本網站內容主要網絡,不保證有關資料的準確性及可靠性,讀者在使用前請進一步核實,并對任何自主決定的行為負責。本網站概不負任何法律責任)


標簽:


34
分享到:
主站蜘蛛池模板: 娼年免费在线观看 | 4444在线网站 | 久久精品国产91久久综合麻豆自制 | 国内自拍中文字幕 | 视频久久精品 | 一级毛片全部免费播放 | 日韩美女视频一区 | 在线小视频国产 | 色婷婷六月桃花综合影院 | 97视频在线观看视频在线精品 | 国内国语一级毛片在线视频 | 日韩精品久久不卡中文字幕 | 国产成人免费在线 | 狠狠干精品 | 欧美性生活视频播放 | 免费二级毛片免费完整视频 | 久久网站视频 | 久久精品爱国产免费久久 | 欧美性区| 亚洲春色在线 | 国产成人亚洲精品大帝 | a级毛片免费在线观看 | 毛片观看网站 | 日韩经典第一页 | 爱爱一区| 久久精品中文字幕极品 | 欧洲性开放大片免费观看视频 | 97精品国产97久久久久久 | 毛片区 | 免费人成毛片动漫在线播放 | 少女免费观看完整版 | 四虎影视库永久在线地址 | 国产精品第九页 | aa小视频 | 最新久久免费视频 | 激情五月婷婷在线 | 久草免费资源视频 | 你懂的国产高清在线播放视频 | 综合网激情 | 久久成人国产 | 久久久久99精品成人片三人毛片 |