微信小程序开发-登陆态维护


微信小程序开发-登陆态维护

在小程序开发过程中,如遇到需要用户登陆的场景,我们可以调用小程序的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,这样就每次都能成功解密数据。


分享到:


相關文章: