小程序速成系列:手把手教你设计用户登录体系
跳一跳的出现再次激活了小程序,吃饭在跳,睡觉在跳,洗澡在跳。有句话说得好,谁掌握流量,谁掌握过去;谁掌握入口,谁掌握现在;谁掌握趋势,谁掌握未来,即使我们不能掌握趋势,也要顺应趋势。
小程序内的用户体系
通常要构建一个用户体系需要解决两个关键问题:
1、用户id在系统中保证唯一
2、维护用户在系统中的登录状态
只要这两个问题得到解决,其他事情就是无限的拓展和丰富。比如你要给用户增加昵称、性别、年龄,或者给用户增加角色权限等等,这些都是用户体系下的延伸。盖高楼最重要的是坚固稳定的基石,其他楼层的搭建就是时间问题。
小程序里面也有一套自己的用户体系,所以我们不必从0去开发,可以通过其登录接口进行开发。
以下是小程序官方的登录流程图(先认真看两遍)
小程序:指的是小程序前端框架,也就是在微信开发者工具中写代码的部分
第三方服务器:指的是我们自己的后端服务器,此处对微信而言是第三方服务器,后面我们统一称为业务服务器
微信服务器:指的是微信的后端服务器,也就是提供的API接口,目前域名地址为 : https://api.weixin.qq.com
微信官方已经给出大致思路:
第一步:通过wx.login的API获取登录凭证code,发送请求到第三方服务器,并带上code,然后调用微信后端API获得openid和session_key.
第二步:生成自己的session的键名,这里把该key称为3rd_session,以3rd_session为key,session_key
+openid
为value,写入session(session可以通过Redis或Memcached的KV存储),这里的过期时间可以根据业务场景自行设置
第三步:生成3rd_session后,在客户端写入storage,保存在本地,这里最好再机上过期时间expire,每次登录之前判断3rd_session是否过期,如果过期,则重新请求登录,生成3rd_session
第四步:wx.request每次带上3rd_session,向后端发起请求,根据3rd_session在session存储中查找合法的session_key
和openid
静默获取openid
上面第一步提到了微信的wx.login接口,该接口会返回一个临时凭证code
,为什么是临时凭证,因为用户每次进入小程序,code
都是不同的。
//登录
wx.login({
success: res => {
console.log(res)
}
})
接下来用code
换取openid
,
前端代码:
// 登录
wx.login({
success: res => {
if (res.code) {
wx.request({
url: 'https://test.com/test/login.php', //此处仅仅为演示接口
data: {
code: res.code
},
success: function (res) {
console.log(res)
}
})
} else {
console.log('获取code失败' + res.errMsg)
}
}
})
获取openid的开放接口:
https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code
请求参数
参数 | 必填 | 说明 |
---|---|---|
appid | 是 | 小程序唯一标识 |
secret | 是 | 小程序的 app secret |
js_code | 是 | 登录时获取的 code |
grant_type | 是 | 填写为 authorization_code |
返回参数
在不满足UnionID下发条件的情况下,返回参数
参数 | 说明 |
---|---|
openid | 用户唯一标识 |
session_key | 会话密钥 |
在满足UnionID下发条件的情况下,返回参数
参数 | 说明 |
---|---|
openid | 用户唯一标识 |
session_key | 会话密钥 |
unionid | 用户在开放平台的唯一标识符 |
https://test.com/test/login.php
对应的后端代码:
public function login()
{
$code = isset($_POST['code']) ? $_POST['code'] : "";
$appid = "你的小程序id";
$secret = "你的小程序密钥"; //密钥和appid都在微信公众后台生成
//这里为了演示直接把获取openid的api写在当前方法中,实际项目建议封装所有的微信接口,通过某个库抽象处理
$openid_api = "https://api.weixin.qq.com/sns/jscode2session?appid={$appid}&secret={$secret}&js_code={$code}&grant_type=authorization_code";
return file_get_contents($openid_api);
}
前端如图所示,已经获取到openid(在这个小程序中是唯一的),可以为所欲为了。整个过程是静默获取,用户没有感知。
业务方维护登录态
根据上面的流程图,可以看到微信建议我们自己维护登录态,生成3rd_session(表示第三方会话)。
关于3rd_session的生成,上面的流程图已经说得非常详细。可以通过Linux操作系统提供的随机数机制,如命令head -n 80 /dev/urandom | tr -dc A-Za-z0-9 | head -c 64
,生成出来的就是3rd_session,我们使用它作为key,然后以openid
+ session_key
作为value。
我们可以使用KV存储(Redis或Memcached)这个会话,这里使用Redis。
public function create3rdSession($openid = "", $session_key = "")
{
if (!$openid || !$session_key) {
return false;
}
//通过操作系统随机数机制生成key
exec('head -n 80 /dev/urandom | tr -dc A-Za-z0-9 | head -c 64', $exec_ret);
if (! isset($exec_ret[0]) || strlen($exec_ret[0]) != 64) {
return false;
}
$app_session_key = $exec_ret[0]; //随机数作为会话的key
$app_session_value = ['openid'=>$openid, 'session_key'=>$session_key]; //openid和微信的session_key作为会话的value
cache($app_session_key, $app_session_value, 3600);//会话缓存1小时
return $app_session_key;
}
上面的create3rdSession产生了会话,但是其中两个参数是需要接口获得,那么我们在login方法里面丰富一下:
public function login()
{
$code = isset($_POST['code']) ? $_POST['code'] : "";
$ret = array();
$appid = "你的小程序id";
$secret = "你的小程序密钥";
$openid_api = "https://api.weixin.qq.com/sns/jscode2session?appid={$appid}&secret={$secret}&js_code={$code}&grant_type=authorization_code";
$result = file_get_contents($openid_api);
$ret = json_decode($result, 1);
//这个地方创建会话,生成一个会话token
$token = $this->create3rdSession($ret['openid'], $ret['session_key']);
$ret['token'] = $token;
return json_encode($ret);
}
我们再次访问小程序,app.js入口就会调用login的api,返回如下:
再查看下Redis中存储的状态:
可以看到我们的token是一串随机数,这里我用right_
做了前缀,查看该key,可以看见序列化的openid和session_key。ok。至此数据存储成功。
维护登录态已经完成一半,接下来是用户从小程序进入,需要判断是否已经登录。
我们需要把前面生成的token存储到cookie中,然而小程序中并没有cookie机制,需要使用storage来替代。不熟悉的可以看文档:https://developers.weixin.qq.com/miniprogram/dev/api/data.html#wxsetstorageobject
我们通过wx.setStorageSync
把token存储在小程序本地,再通过wx.getStorageSync
把token取出来,请求接口在header带上这个cookie即可。
前端代码:
// 登录
wx.login({
success: res => {
if (res.code) {
wx.request({
url: 'https://test.com/test/login.php',
data: {
code: res.code
},
method:"post",
header: {
"Content-Type": "application/x-www-form-urlencoded",
'cookie': wx.getStorageSync("sessionid") //读取cookie
},
success: function (res) {
let data = res.data
if (data.status == 1) {
wx.showModal({
title: '提示',
content: '已经登录',
showCancel:false
})
} else if (data.status == 0) {
wx.showModal({
title: '提示',
content: '本地存储成功',
showCancel: false
})
wx.setStorageSync("sessionid", data.token) //设置cookie
}
}
})
} else {
console.log('获取code失败' + res.errMsg)
}
}
})
login接口我们判断如果cookie中带有sessionid就,通过sessionid作为key,去Redis中查找信息。如果有则会话未过期,如果没有则证明已过期,重新读取openid接口。代码如下:
public function login()
{
$ret = array();
$request = new Request();
$session_id = $request->header('Cookie');
$info = cache($session_id);
if ($info) {
$ret['status'] = 1;
$ret['data'] = $info;
return json_encode($ret);
}
$code = isset($_POST['code']) ? $_POST['code'] : "";
$appid = "你的小程序id";
$secret = "你的小程序密钥";
$openid_api = "https://api.weixin.qq.com/sns/jscode2session?appid={$appid}&secret={$secret}&js_code={$code}&grant_type=authorization_code";
$result = file_get_contents($openid_api);
$ret = json_decode($result, 1);
//创建会话
$token = $this->create3rdSession($ret['openid'], $ret['session_key']);
$ret['status'] = 0;
$ret['token'] = $token;
return json_encode($ret);
}
第一次进入小程序,效果如下:
控制台成功将token写入到本地:
当我们再次进入小程序,效果如下:
header里面也塞上了Cookie,如下:
到此,一个简单但完整的登录态已经完成。
谈谈session_key和wx.checkSession()
session_key是通过获取openid的那个接口一起得到的,即接口https://api.weixin.qq.com/sns/jscode2session
但是上面我们的登录态并没有使用,session_key保证了当前用户进行会话操作的有效性,这个session_key是微信服务端给我们派发的。
由于业务自身维护了登态,这里就不没必要使用session_key了,那么是否它一无是处呢?并不是,在获取某些敏感接口需要用到session_key。有两个地方需要:
- 校验用户信息(wx.getUserInfo(OBJECT)返回的signature);
- 解密(wx.getUserInfo(OBJECT)返回的encryptedData);
session_key还有两个注意点:
- session_key和微信派发的code是一一对应的,同一code只能换取一次session_key。每次调用wx.login(),都会下发一个新的code和对应的session_key,为了保证用户体验和登录态的有效性,开发者需要清楚用户需要重新登录时才去调用wx.login()
- session_key是有时效性的,即便是不调用wx.login,session_key也会过期,过期时间跟用户使用小程序的频率成正相关,但具体的时间长短开发者和用户都是获取不到的
从上述而看,本人不建议使用session_key去维护业务的登录态。
看得仔细,微信小程序文档上还有一个wx.checkSession(),这个API是根据session_key的过期与否来检测当前的会话是否过期。那么我们如果不实用session_key,这个wx.checkSession()也可以不用。实际上前面我们的token已经可以知道会话什么时候过期,这个就显得没什么用处,再者,这个过期时长是微信维护的,业务方也无法控制,在做一些特性逻辑的时候,也不好开发。
划重点,个人建议在维护小程序登录态的时候可以不使用session_key
和wx.checkSession()
这两个东西,感觉很鸡肋,还容易把开发者搞糊涂。
会话过期与注销
会话过期
实际上前面已经有加上过期时间,会话为一个小时,这就是KV的特性的,十分便于做一些过期的事情。
cache($app_session_key, $app_session_value, 3600);
查看生命周期,只要3600秒过去,会话自动删除,headers中的Cookie就无法读取到Redis中的信息,那就过期了。下图的会话还剩900多秒
会话注销
这个太好做了,提供一个logout的接口,直接把key删除即可。一行代码搞定,不展开了。
cache($app_session_key, null);
关于小程序登录的思考
实际上在小程序中没有注册的概念,因为用户在使用小程序前必须登录微信,此刻实际上已经有存在登录态了,微信让开发者通过小程序的appid和secert去获得openid,这里更确切的叫绑定,而不是注册。每个小程序应该有自己的用户信息,比如需要获得用户的手机号、用户的邮箱等等,这些在初次登录的时候和openid绑定即可,再建立自己的用户表。这样,以后每次进入小程序可以获得用户的其他的相关信息。当然,有些开发者如果完全不使用微信登录这一套,必须有自己的用户名和密码也完全可以自行设计,至于绑不绑openid,那就具体问题,具体分析了。
最后更新于 2018-05-23 12:50:29 并被添加「小程序 登录设计 PHP」标签,已有 894 位童鞋阅读过。
本站使用「署名 4.0 国际」创作共享协议,可自由转载、引用,但需署名作者且注明文章出处
uk cialis online
cialis coupons 2018
rabaris 20mg cialis
cialis 20mg
@MarkAneta
非常不错啊,感谢无私的分享!