梳理下微信小程序中登录部分。
需求分析
在小程序中,可以拥有常规的网站手机或账户密码登录外,还有微信登录。类似摩拜单车的微信小程序就是两者共存。
但是无论如何,使用微信登录我认为是必须的,而常规的登录可有可无了。
我的需求:
用户点击微信登录,返回用户的登录令牌accessToken。
如果在数据库中不存在过这个微信用户,就构造用户名密码,入库。再返回登录令牌。
整体流程
1. 用户点击微信登录,用户授权后,或者基本信息以及带上服务端需要的参数信息请求服务端。
2. 服务端校验参数,请求微信的接口获取用户的唯一标识。openid或者unionid。在微信开放平台下绑定你的小程序,然后小程序和其他移动应用,网站应用一样,同一个用户登录,虽然openid不同,但是unionid一致。
3. 根据unionid判断该微信用户是否存在。不存在,构造信息入库。
具体步骤
登录事件的绑定,比如在login页面,给一个微信登录的按钮绑定一个事件。
方法代码:
其实这些微信文档里面都有的。我主要想说下一个坑。
在服务端需要对传过来的encryptedData参数进行解密。
如果传参之前做个url编码,在服务端记得urldecode解密。
微信官方提供了一个解密SDK。其中php部分居然带了BOM头。如果你的json格式返回一直被解析成字符串,而不是对象,而且你的服务器是linux。那么应该就是这个Bom头的锅了。
接口可以看文档: 小程序-登录-签名加密
服务端处理流程
AppID(小程序ID) 和 AppSecret(小程序密钥) 是已有的,从微信后台拿。
接受下参数:
$code = $_POST['code'];//用户授权码
$encryptedData = urldecode($_POST['encryptedData']);
$iv = $_POST['iv'];
用这个code去获取sessionKey
//获取sessionKey
$sessionKey_url = "https://api.weixin.qq.com/sns/jscode2session?appid=$AppId&secret=$AppSecret&js_code=$code&grant_type=authorization_code";
$res = json_decode(file_get_contents($sessionKey_url));
然后判断下返回,在接收一下sessionKey和openid(用户在当前小程序的唯一id)
$sessionKey = $res->session_key;
$openid = $res->openid;
接着引入上面的微信解码SDK,wxBizDataCrypt.php
$pc = new WXBizDataCrypt($AppId, $sessionKey);
$errCode = $pc->decryptData($encryptedData, $iv, $data);
if ($errCode == 0) {
$data = json_decode($data);
//对比水印中的appid
$watermark_appid = $data->watermark->appid;
if($AppId == $watermark_appid){
$unionid = $data->unionId;
$nickname = $data->nickName;//微信用户名
$headimgurl = $data->avatarUrl;//微信用户头像
/*unionid是微信开放平台下多应用间的用户统一标识
后面就是根据unionid判断用户是否已存在了,
没存在把数据入库,否则就更新用户信息。
最后都是返回accessToken,作为用户的登录令牌。
不了解unionid的自己搜哦*/
}
//失败
}else{
//失败
}
获取用户手机号码
微信小程序文档: getPhoneNumber(OBJECT)
大体上和上面那个都差不多,就是一个获取code,然后授权后拿到数据,再对数据进行解密的过程。
我最开始做的时候,是用户授权后,我拿到了 encryptedData 和 iv 之后,再调用wx.login 得到code。
然后发起请求,通过code拿到sessionKey,同微信登录解密用户信息一样,同样的解密代码。
然后出现了偶尔解密失败的情况,问题的定位是 sessionKey 出的问题,过期或是不匹配。
注意: 你需要确保你提交的参数和你服务端接收到的参数要一致,这是前提。因为可能存在全局过滤数据,导致数据变化等情况。
这里给两种解决方案:
1. 用户登录的时候,缓存其sessionKey 。然后根据 wx.checkSession 来判断用户的登录态,过期的话,则需要重新调用,wx.login拿最新的code。
如果服务端接收到了code,则根据code重新去获取sessionKey,并更新缓存,否则直接使用缓存的sessionKey
2. 在getPhoneNumber API 调用前,先调用 wx.login 获取code
我使用了这种方式,在个人中心页面onShow的时候,先调用wx.login,得到code并存起来,后面发起拿手机号授权的时候带上code就是了。(这些步骤可以做的更精致一点)