参考链接:
(1)微信小程序开发用户授权登录
https://www.cnblogs.com/dashucoding/p/9917371.html
(2)微信小程序如何通过用户授权获取手机号(getPhoneNumber)
https://www.jb51.net/article/179007.htm
获取手机号 | 微信开放文档
https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/getPhoneNumber.html
(3)微信小程序使用wx.login()获取用户的openid和session_key(示例后端使用Thinkphp)https://blog.csdn.net/qq15577969/article/details/102699215
在开发微信小程序过程中,需要绑定微信用户的信息到数据库里,那么就需要获得用户的唯一标识openid,而微信为了安全,禁止小程序直接访问https://api.weixin.qq.com 的接口,因此不能直接拿到用户的openid。
解决方案:
先通过wx.login获取一个临时凭证code
wx.login(OBJECT)
调用接口获取登录凭证(code)进而换取用户登录态信息,包括用户的唯一标识(openid) 及本次登录的 会话密钥(session_key)。用户数据的加解密通讯需要依赖会话密钥完成。
然后我们拿这个code去自己的服务器换取用户openid和session_key(code 换取openid和session_key)
这是一个 HTTPS 接口,开发者服务器使用登录凭证 code 获取 session_key 和 openid。其中 session_key 是对用户数据进行加密签名的密钥。为了自身应用安全,session_key 不应该在网络上传输。
微信小程序前端代码(app.js)
//app.js
App({
onLaunch: function() {
// 展示本地存储能力
var logs = wx.getStorageSync('logs') || []
logs.unshift(Date.now())
wx.setStorageSync('logs', logs)
/*
* 获取用户openid
* 示例说明:微信为了安全,禁止小程序访问https://api.weixin.qq.com 的接口,因此不能直接拿到用户的openid
* 解决方案:
* 1、先通过wx.login获取一个临时凭证code
* 2、然后我们拿这个code去自己的服务器换取用户openid
*/
var that=this;
wx.login({
success: function (res) {
var code = res.code;//发送给服务器的code
wx.getUserInfo({
success: function (res) {
var userNick = res.userInfo.nickName; //用户昵称
var avataUrl = res.userInfo.avatarUrl;//用户头像地址
var gender = res.userInfo.gender; //用户性别
if (code) {
wx.request({
url: 'https://后端网址/getOpenid.html',
data: {
code: code,
nick: userNick,
avaurl: avataUrl,
sex: gender,
},
header: {
'content-type': 'application/json'
},
success: function (res) {
console.log('获取到的用户openid为:' + res.data.openid);
//可以把openid保存到本地缓存,方便以后调用
wx.setStorageSync('openid', res.data.openid);
}
})
}
else {
console.log("获取用户登录态失败!");
}
}
})
},
fail: function (error) {
console.log('login failed ' + error);
}
})
},
/**
* 全局属性
*/
globalData: {
userInfo: null
}
})
后端Thinkphp代码
public function getOpenid(){
if(!isset($_GET['code'])||!isset($_GET['nick'])||!isset($_GET['avaurl'])||!isset($_GET['sex'])){
header("Content-type: text/html; charset=utf-8");
echo '参数错误.';
exit;
}
//微信小程序appid
$appid='小程序的APPID';
//微信小程序secret
$secret='小程序的SECRET';
$code = $_GET['code']; //小程序传来的code值
$nick = $_GET['nick']; //小程序传来的用户昵称
$imgUrl = $_GET['avaurl']; //小程序传来的用户头像地址
$sex = $_GET['sex']; //小程序传来的用户性别
$url = 'https://api.weixin.qq.com/sns/jscode2session?appid='.$appid.'&secret='.$secret.'&js_code=' . $code . '&grant_type=authorization_code';
$info = file_get_contents($url);//发送HTTPs请求并获取返回的数据,推荐使用curl
$json = json_decode($info);//对json数据解码
$arr = get_object_vars($json);
$openid = $arr['openid'];
$session_key = $arr['session_key'];
echo json_encode($arr);
}
wxml文件
<button open-type="getPhoneNumber" bindgetphonenumber="getPhoneNumber"></button>
JS内getPhoneNumbe组件函数(该事件中最重要的就是在wx.login登录后发起接口请求),这里需要配置参数来给接口:
这些是必不可少的参数,这些齐备才能算一个合法的请求。
appid: "你的小程序APPID",
secret: "你的小程序appsecret",
code: res.code,
encryptedData: telObj,
iv: ivObj
js文件
//通过绑定手机号登录
getPhoneNumber: function (e) {
var ivObj = e.detail.iv
var telObj = e.detail.encryptedData
var codeObj = "";
var that = this;
//------执行Login---------
wx.login({
success: res => {
console.log('code转换', res.code);
//用code传给服务器调换session_key
wx.request({
url: 'https://你的接口文件路径', //接口地址
data: {
appid: "你的小程序APPID",
secret: "你的小程序appsecret",
code: res.code,
encryptedData: telObj,
iv: ivObj
},
success: function (res) {
phoneObj = res.data.phoneNumber;
console.log("手机号=", phoneObj)
wx.setStorage({ //存储数据并准备发送给下一页使用
key: "phoneObj",
data: res.data.phoneNumber,
})
}
})
//-----------------是否授权,授权通过进入主页面,授权拒绝则停留在登陆界面
if (e.detail.errMsg == 'getPhoneNumber:user deny') { //用户点击拒绝
wx.navigateTo({
url: '../index/index',
})
} else { //允许授权执行跳转
wx.navigateTo({
url: '../test/test',
})
}
}
});
},
点击"拒绝",开发者能捕捉到该事件 ,此时getPhoneNumber 函数返回 e.detail.errMsg 为 getPhoneNumber:user deny
用wx.login获取登录凭证code
<!--pages/user/index.wxml-->
<view hidden='{{boolean}}'>
<view wx:if="{{isLogin == 1}}">
<!-- 个人信息 -->
<view class='infomation'>
<!-- 基本信息 -->
<view class="gameTitle">
<navigator hover-class="none" href=""><image src="{{dataList.head_photo}}"></image></navigator>
<view>
<view class="gameName"><navigator hover-class="none" href="">{{dataList.username}}</navigator></view>
<view class="gameSummary" wx:if="{{dataList.title == ''}}"><navigator hover-class="none" href="">这个玩家很懒,什么也没留下</navigator></view>
<view class="gameSummary" wx:if="{{dataList.title != ''}}"><navigator hover-class="none" href="">{{dataList.title}}</navigator></view>
</view>
</view>
</view>
<!-- “我的”列表 -->
<view class='myList'>
<view class='list'>
</view>
</view>
</view>
<view wx:if="{{isLogin == 2}}">
<view class='bgBox'>
</view>
<view class="unLogin">
</view>
</view>
</view>
wx.checkSession 校验登陆态
//原型:检测当前用户登录态是否有效
wx.checkSession({
success: function(){
//session 未过期,并且在本生命周期一直有效
},
fail: function(){
//登录态过期
wx.login() //重新登录
....
}
})
//用户登陆
function userLogin() {
wx.checkSession({
success: function () {
//存在登陆态
},
fail: function () {
//不存在登陆态
onLogin()
}
})
}
function onLogin() {
wx.login({
success: function (res) {
if (res.code) {
//发起网络请求
wx.request({
url: 'Our Server ApiUrl',
data: {
code: res.code
},
success: function (res) {
const self = this
if (逻辑成功) {
//获取到用户凭证 存儲 3rd_session
var json = JSON.parse(res.data.Data)
wx.setStorage({
key: "third_Session",
data: json.third_Session
})
getUserInfo()
}
else {
}
},
fail: function (res) {
}
})
}
},
fail: function (res) {
}
})
}
function getUserInfo() {
wx.getUserInfo({
success: function (res) {
var userInfo = res.userInfo
userInfoSetInSQL(userInfo)
},
fail: function () {
userAccess()
}
})
}
function userInfoSetInSQL(userInfo) {
wx.getStorage({
key: 'third_Session',
success: function (res) {
wx.request({
url: 'Our Server ApiUrl',
data: {
third_Session: res.data,
nickName: userInfo.nickName,
avatarUrl: userInfo.avatarUrl,
gender: userInfo.gender,
province: userInfo.province,
city: userInfo.city,
country: userInfo.country
},
success: function (res) {
if (逻辑成功) {
//SQL更新用户数据成功
}
else {
//SQL更新用户数据失败
}
}
})
}
})
}
第三方服务器和微信服务器:
注意:session_key是微信服务器生成的针对用户数据进行加密签名的密钥,不应该进行传输到客户端.
生成3rd_session
用于第三方服务器和小程序之间做登录态校验.为了保证安全性,3rd_session应该长度够长,一定有效时间, session_key + openid, key, 为 value, 写入到session存储.
3rd_session写入storage:
后续用户进入小程序,先从storage读取3rd_session
根据请求,在session存储中查找合法的session_key和openid
App({
onLaunch: function() {
wx.login({
success: function(res) {
var code = res.code;
if (code) {
console.log('获取用户登录凭证:' + code);
} else {
console.log('获取用户登录态失败:' + res.errMsg);
}
}
});
}
})
唯一标识(openid)和会话密钥(session_key)
服务端处理逻辑
wx.checkSession({
success:function(res){
//session_key未过期
},
fail:(res=>{
// session_key已过期
// 进行登录操作
wx.login({
success: function(res) {
const url = '接口地址' //本次项目中是从后台获取session_key的接口,可能流程不同,会有相应的变化
const data = {
//你要传的数据
}
wepy.request({
url: url,
method: 'POST',
data: data,
}).then(res=>{
if(res.data.code == 0){
//拿到的openid和session_key存到缓存中
//调用换取用户id接口[需求不同,可能接口会有相应的变化]
const url = '获取用户id的接口'
const data = {'要传的数据'}
wepy.request({
url: url,
method: 'POST',
data: data,
}).then((res)=>{
//该接口设计返回的参数包括换取的用户ID和返回的用户的微信中信息,也就是通过button获取的那个userInfo[我们为了后续的处理,所以后台这块返回用户信息,如果用户还未登录,用户信息,返回是空,反之则有值]
//存储用户ID
wx.setStorage({
key:'userId',
data:'获取到的用户ID'
})
//存储用户信息[userInfo]
wx.setStorage({
key:'userInfo',
data:'获取到的用户信息'
})
})
}
})
}
});
})
})
onShow(){
//从缓存中获取session_key
let skey = wx.getStorageSync('session_key');
if (!skey) {
//如果session_key不存在,再次执行登录
wx.login({
//该处登录同app.wpy[流程一样]
});
}else{
// session_key存在
}
}
//我们假设这个页面需要获取用户的信息,首先给一个button[open-type设置为getUserInfo],使用这个拿到用户的信息,
getUserInfo(e){
this.userInfo = e.detail.userInfo; //e.detail.userInfo携带的就是用户的个人信息[包括头像、昵称等]
if(e.detail.userInfo){
wx.showToast({
title: '授权成功',
icon: 'success',
duration: 1500
})
//这里做这样的处理,是因为需求需要点击购物车按钮跳转订单待支付页,在没有获取到用户信息之前,跳转购物车的按钮隐藏,获取用户信息按钮显示,反之则返
this.button = 'none'; //获取用户信息button
this.myMenu = 'block'; //跳转待支付订单页
//更新缓存中的用户信息
wx.setStorage({
key:'userInfo',
data:this.userInfo
})
}else if(e.detail.errMsg == 'getUserInfo:fail auth deny'){
wx.showModal({
title: '提示',
content: '若不授权微信登录,则无法使用小程序。点击"授权"按钮并允许使用"用户信息"方可正常使用。',
showCancel:false,
confirmText:'授权',
success:(res=>{
wx.openSetting({
success: (res) => {
}
})
})
})
}
}
数字签名校验
https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code
对称解密的目标密文为 Base64_Decode(encryptedData)。
对称解密算法初始向量 为Base64_Decode(iv),其中iv由数据接口返回。
signature = sha1( rawData + session_key )
//最终供外面调用的方法
function login(){
console.log('logining..........');
//调用登录接口
wx.login({
success: function (e) {
console.log('wxlogin successd........');
var code = e.code;
wx.getUserInfo({
success: function (res) {
console.log('wxgetUserInfo successd........');
var encryptedData = encodeURIComponent(res.encryptedData);
thirdLogin(code,encryptedData,res.iv);//调用服务器api
}
})
}
});
}
function thirdLogin(code,encryptedData,iv){
var url = "eeee/xxx/login/ttttt";
var params = new Object();
params.code = code;
params.encryptedData = encryptedData;
params.iv =iv;
buildRequest(new Object(),url,params,{
onPre: function(page){},
onSuccess:function (data){
console.log('my login successd........');
console.log(data);
getApp().globalData.session_id = data.session_id;
getApp().globalData.uid = data.uid;
getApp().globalData.isLogin = true;
},
onError : function(msgCanShow,code,hiddenMsg){
}
}).send();
}