微信小程序開發-登陸態維護


微信小程序開發-登陸態維護

在小程序開發過程中,如遇到需要用戶登陸的場景,我們可以調用小程序的API獲取數據,從而生成自己需要的登陸狀態。

登陸流程

  • 登陸過程

1. 調用 wx.login() 獲取臨時登錄憑證code ,並回傳到開發者服務器。

2. 調用 auth.code2Session 接口,換取用戶唯一標識 OpenID 和 會話密鑰 session_key。

3. 開發者服務器可以根據用戶標識來生成自定義登錄態,用於後續業務邏輯中前後端交互時識別用戶身份。

  • 注意

1. 會話密鑰 session_key 是對用戶數據進行 加密簽名 的密鑰。為了應用自身的數據安全,開發者服務器不應該把會話密鑰下發到小程序,也不應該對外提供這個密鑰。 2. 臨時登錄憑證 code 只能使用一次

  • 時序圖
微信小程序開發-登陸態維護


示例代碼

  • 小程序端代碼
<code>//login方法獲取code,需要授權
wx.login({
success: function (res) {
//獲取用戶信息
wx.getUserInfo({
success: function (uinfo) {
var params = {
js_code: res.code, //調用小程序api所需code
rawData: uinfo.rawData, //不包括敏感信息的原始數據字符串,用於計算簽名
signature: uinfo.signature, //使用 sha1( rawData + sessionkey ) 得到字符串,用於校驗用戶信息
encryptedData: uinfo.encryptedData, //包括敏感數據在內的完整用戶信息的加密數據
iv: uinfo.iv, //加密算法的初始向量
}
//封裝的調用後臺api方法
app.HttpService.getLogin(params)
.then(res => {
if (res.data.errcode == 0){
//獲取到session放入本地存儲
wx.setStorage({
key: "session",
data: res.data.session,
success (res) {
}
})
}else{
//登陸失敗
wx.showToast({

title: "登陸失敗",
icon: 'none',
duration: 2000
});
}
})
}
})
},
fail: function (res) {
wx.showToast({
title: "登陸失敗",
icon: 'none',
duration: 2000
});
}
})/<code>
  • 後臺代碼(ruby)
<code>def get_login
js_code = params[:js_code]
#調用api獲取登陸信息
client = RestClient::Request.execute(method: :get, url: "https://api.weixin.qq.com/sns/jscode2session?appid=#{WEI_APPID}&secret=#{WEI_SECRET}&js_code=#{js_code}&grant_type=authorization_code", timeout: 10)
result = JSON.parse client.body
if result["errcode"].to_i == 0
#該用戶openid,唯一
openid = result["openid"]
#臨時登陸session_key
session_key = result["session_key"]
if !openid.blank? && !session_key.blank?
#不包括敏感信息的原始數據字符串,用於計算簽名
raw_data = params[:rawData]
#使用 sha1( rawData + sessionkey ) 得到字符串,用於校驗用戶信息
signature = params[:signature]
#包括敏感數據在內的完整用戶信息的加密數據
de_encrypted_data = Base64.decode64(params[:encryptedData])
#加密算法的初始向量
de_iv = Base64.decode64(params[:iv])

#驗證簽名
if signature == Digest::SHA1.hexdigest(raw_data+session_key)
aes_key = Base64.decode64(session_key)
#開始解密
decipher = OpenSSL::Cipher::AES.new(128, :CBC)
decipher.decrypt
decipher.key = aes_key
decipher.iv = de_iv
decipher.padding = 1
rand_msg = decipher.update(de_encrypted_data) + decipher.final
#解密後數據
data = JSON.parse(rand_msg).deep_symbolize_keys
Rails.logger.info data
hash = {
wx_openid: openid,
name: data[:nickName],
head: data[:avatarUrl],
gender: data[:gender],
}
#創建或查找用戶
user = User.find_or_create_weixin(hash)
#生成session,此為維護用戶登錄態最終session,此處使用的uuid
session = UUIDTools::UUID.random_create.to_s
#可以在redis中存儲用戶登陸信息,並根據自己需要設置過期時間
RedisOperation.set_wx_session(session, openid, session_key)
end
end
end
if !user.blank?
#給小程序端的信息
userinfo = {}
render json: {:errcode => 0, :session => session, :userinfo => userinfo}
else
render json: {:errcode => 500, :errmessage => "登陸失敗"}
end
end/<code>

踩坑

小程序官方文檔可以用open-type='getUserInfo'直接發起授權並獲得code和userinfo,於是嘗試了一下

<code><button>/<code>

點擊後回調bindgetuserinfo方法

<code>bindgetuserinfo: function (e) {
var that = this
//注意這裡是先獲取到了getUserInfo,再wx.login
uinfo = e.detail.userInfo
if (uinfo) {
wx.login({
success: function (res) {
var params = {
js_code: res.code,
rawData: uinfo.rawData,
signature: uinfo.signature,
encryptedData: uinfo.encryptedData,
iv: uinfo.iv,
}
app.HttpService.getLogin(params)
.then(res => { if (res.data.errcode == 0){
wx.setStorage({
key: "session",
data: res.data.session,
success (res) {
cb()
}
})
}else{
that.globalData.userInfo = null
wx.showToast({
title: "登陸失敗",
icon: 'none',
duration: 2000
});
}
})
},
fail: function (res) {
wx.showToast({
title: "登陸失敗",
icon: 'none',
duration: 2000
});
}
})
}
}/<code>

但是當後臺解密數據時發現,有時候解密不成功,隱藏猜測可能是在點擊按鈕獲取userInfo時,小程序官方會自動先生成一個code和session_key,而當我們再調用wx.login以及jscode2session時獲取到的很有可能是刷新之後的session_key,那麼再用這個session_key去解密肯定是不成功的。

所以產生了我上面的寫法,點擊按鈕後,e.detail.userInfo棄用,直接走正常流程調用wx.login然後再wx.getUserInfo,這樣就每次都能成功解密數據。


分享到:


相關文章: