热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

ThinkPHP实现微信小程序微信授权登录

网上找了很多教程都不好使,最后自己又研究了一番,终于成功了


网上找了很多教程都不好使,最后自己又研究了一番,终于成功了

一、总体设计

小程序登录流程:客户端通过调用wx.login(),获取登录凭证(code),将此code发往服务器,服务器通过调用 auth.code2Session 接口,使用 code 换取 openid、unionid、session_key 等信息。

参考官方给出的时序图:
ThinkPHP实现微信小程序微信授权登录 - 文章图片

本实例中将openid作为用户唯一标识,存入数据库中。服务器后端通过ThinkPHP框架实现两个接口:

http://localhost/srclib/index.php/Home/User/wxlogin // 登录接口
http://localhost/srclib/index.php/Home/User/wxsign // 首次登录(微信授权登录)接口

1. 登录状态的维持

小程序在初始化时,在小程序onLaunch生命周期函数中调用wx.login(),向登录接口发送请求,将code发送给服务器。服务器接收到code后,调用https://api.weixin.qq.com/sns/jscode2session接口,将code,appid和appSecret发送给微信官方服务器,服务器会收到用户唯一标识openid和会话密钥 session_key等信息。

此时服务器后台在数据库User表中查询openid:

  • 如果在User表中没有查到该记录,则说明此用户是新用户,还未授权微信登录,服务器返回客户端的状态码err_code值为1,表示不存在该用户,需要用户手动授权微信登录;

  • 如果在User表中查到了该记录,则表示用户已授权微信登录,此时服务器返回客户端的状态码err_code值为0,表示登录成功,服务器将用户信息和状态码发送至前端。

客户端在接收到状态码后判断:

  • 若状态码为0,则表示登录成功。客户端接收到用户信息后,通过调用wx.setStorageSync()将用户信息加入本地缓存,并将登录状态设置为true,此时实现了登录状态的维持;

  • 若状态码为1,表示用户还未授权登录,此时跳转至auth页等待用户的授权操作。

首次授权后,小程序每次启动时,自动调用登录接口,并将在本地缓存中更新用户信息和登录状态,小程序其他页面在初始化时调用_initial()函数,从本地缓存中取出用户信息和登录状态,赋值给全局变量userInfo和loginState,此后小程序页面渲染时便可从全局变量获取用户信息,设置给页面对象的data属性值。

2. 微信授权登录的实现

微信官方提供了wx.getUserProfile接口,使用获取用户信息。在页面中加入一个button,监听用户点击,点击后获取用户信息,本实例中主要获取了用户昵称nickName和用户头像地址avatarUrl。之后向(微信授权登录)首次登录接口发送请求,输入数据为code、nickName和avatarUrl。

服务端接收到这些参数后,首先调用https://api.weixin.qq.com/sns/jscode2session接口,将code,appid和appSecret发送给微信官方服务器,服务器会收到用户唯一标识openid等参数,然后在数据库User表中查询该openid:

  • 如果在User表中没有查到该记录,则将openid、nickName和avatarUrl构建为一条用户记录数据,插入User表中,服务器返回客户端的状态码err_code值为0,表示新用户授权登录成功,服务器将用户信息和状态码发送至前端;

  • 如果在User表中查到了该记录,则表示该用户重复授权,抛出异常,此时服务器返回客户端的状态码err_code值为1。


二、步骤说明


1. 用户首次进入,显示授权登录按钮

ThinkPHP实现微信小程序微信授权登录 - 文章图片

2. 点击授权登录按钮,出现提示弹窗

ThinkPHP实现微信小程序微信授权登录 - 文章图片

3. 点击允许后,登录成功,跳转至个人中心页

ThinkPHP实现微信小程序微信授权登录 - 文章图片

4. 此后用户再次进入小程序,已自动登录,实现了登录状态的维持

ThinkPHP实现微信小程序微信授权登录 - 文章图片

三、完整代码:


1. 小程序前端:


app.js

// app.js
App({
globalData: {
loginState: false, // 用户登录状态
userInfo: null // 用户信息
},
onLaunch() {
this.login() // 登录
},
_initial() { // Page初始化函数,用于读取本地缓存,并更新全局变量
this.globalData.userInfo = wx.getStorageSync('userInfo')
this.globalData.loginState = wx.getStorageSync('loginState')
console.log("登录状态:"+this.globalData.loginState)
console.log("用户信息:")
console.log(this.globalData.userInfo)
},
login() {
let _this = this
// 登录请求
wx.login({
success: res => {
wx.request({
url: 'http://localhost/srclib/index.php/Home/User/wxlogin',
method: 'post',
data: {
code: res.code
},
header: {
"Content-Type": "application/x-www-form-urlencoded"
},
success: function (res) {
if(res.data.error_code == 1) { // 用户未授权登录
console.log("error")
wx.setStorageSync('loginState', false)
wx.navigateTo({ // 跳转至授权页,等待用户授权url: '../auth/auth',
})
} else if(res.data.error_code == 0) { // 登录成功
wx.setStorageSync('userInfo', res.data.data) // 更新本地缓存
wx.setStorageSync('loginState', true)
}
}
})
}
})
}
})

profile.js

// pages/profile/profile.js
const app = getApp()
const globalData = getApp().globalData
Page({
data: {
userInfo: null
},
onl oad() {
app._initial() // 页面初始化
this.setData({
userInfo: globalData.userInfo
})
}
})

auth.js

// auth.js
const app = getApp()
const globalData = getApp().globalData
Page({
data: {
userInfo: null
},
onl oad() {
app._initial() // 页面初始化
},
getUserProfile(e) {
// 使用wx.getUserProfile获取用户信息
wx.getUserProfile({
desc: '展示用户信息', // 声明获取用户个人信息后的用途,后续会展示在弹窗中
success: (res) => {
globalData.userInfo = res.userInfo
this.setData({
userInfo: globalData.userInfo
})
this.sign()
}
})
},
sign() {
let _this = this
wx.showLoading({
title: '加载中',
})
// 用户首次登录请求
wx.login({
success: res => {
wx.request({
url: 'http://localhost/srclib/index.php/Home/User/wxsign',
method: 'post',
data: {
code: res.code,
name: globalData.userInfo.nickName,
avatarUrl: globalData.userInfo.avatarUrl
},
header: {
"Content-Type": "application/x-www-form-urlencoded"
},
success: function (res) {
if(res.data.error_code == 1) { // 用户登录失败
console.log("error")
} else if(res.data.error_code == 0) { // 用户登录成功
wx.setStorageSync('userInfo', res.data.data) // 更新本地缓存
wx.setStorageSync('loginState', true)
wx.hideLoading()
wx.showToast({title: '授权成功',icon: 'success',duration: 2000,complete: (res) => { wx.navigateTo({ // 跳转至个人中心页 url: '../profile/profile', })},
})
}
}
})
}
})
}
})

profile.wxml





{{userInfo.username}}


{{loginState}}



auth.wxml






2. 服务器后端

namespace Home\Controller;
use Think\Controller;
class UserController extends BaseController
{
/**
* 微信登录
* @return [type] [description]
*/
public function wxlogin()
{
// 校验参数是否存在
if (!$_POST['code']) {
$return_data = array();
$return_data['error_code'] = 2;
$return_data['msg'] = '参数不足: code';
$this->ajaxReturn($return_data);
} else {
$appid = "wxc6a376950e1c45c0"; // appid
$secret = "632hjdcba4c2b972c591738hdga91fvg"; // app密钥
$code = $_POST['code']; // 小程序传来的code值
$curl = curl_init();
$url = "https://api.weixin.qq.com/sns/jscode2session?appid=" . $appid . "&secret=" . $secret . "&js_code=" . $code . "&grant_type=authorization_code";
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_HEADER, 0);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$result = curl_exec($curl);
$json = json_decode($result); // 对json数据解码
$arr = get_object_vars($json);
$openid = $arr['openid'];
curl_close($curl);
// 检验是否已经注册
$User = M('User');
// 构造查询条件
$where = array();
$where['openid'] = $openid;
$user = $User->where($where)->find();
if ($user) {$return_data = array();$return_data['error_code'] = 0;$return_data['msg'] = '登录成功';$return_data['data']['userid'] = $user['userid'];$return_data['data']['username'] = $user['username'];$return_data['data']['face_url'] = $user['face_url'];$this->ajaxReturn($return_data);
} else {$return_data = array();$return_data['error_code'] = 1;$return_data['msg'] = '不存在该用户,请授权登录';$this->ajaxReturn($return_data);
}
}
}
/**
*首次授权登录
* @return [type] [description]
*/
public function wxsign()
{
// 校验参数是否存在
if (!$_POST['code']) {
$return_data = array();
$return_data['error_code'] = 2;
$return_data['msg'] = '参数不足: code';
$this->ajaxReturn($return_data);
} else if (!$_POST['name']) {
$return_data = array();
$return_data['error_code'] = 2;
$return_data['msg'] = '参数不足: name';
$this->ajaxReturn($return_data);
} else if (!$_POST['avatarUrl']) {
$return_data = array();
$return_data['error_code'] = 2;
$return_data['msg'] = '参数不足: avatarUrl';
$this->ajaxReturn($return_data);
} else {
$appid = "wxc6a376950e1c45c0"; // appid
$secret = "632hjdcba4c2b972c591738hdga91fvg"; // app密钥
$code = $_POST['code']; // 小程序传来的code值
$curl = curl_init();
$url = "https://api.weixin.qq.com/sns/jscode2session?appid=" . $appid . "&secret=" . $secret . "&js_code=" . $code . "&grant_type=authorization_code";
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_HEADER, 0);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$result = curl_exec($curl);
$json = json_decode($result); // 对json数据解码
$arr = get_object_vars($json);
$openid = $arr['openid'];
curl_close($curl);
// 检验是否已经授权过
$User = M('User');
// 构造查询条件
$where = array();
$where['openid'] = $openid;
$user = $User->where($where)->find();
if ($user) {$return_data = array();$return_data['error_code'] = 1;$return_data['msg'] = '登录失败';$this->ajaxReturn($return_data);
} else {// 如果用户尚未注册,则注册// 构建插入的数据 $data = array();$data['openid'] = $openid;$data['username'] = $_POST['name'];$data['face_url'] = $_POST['avatarUrl'];// 插入数据$result = $User->add($data);// add数据添加成功之后,返回的就是该条数据的idif ($result) { // 插入数据执行成功 $return_data = array(); $return_data['error_code'] = 0; $return_data['msg'] = '登录成功'; $return_data['data']['userid'] = $result; $return_data['data']['username'] = $_POST['name']; $return_data['data']['face_url'] = $_POST['avatarUrl']; $this->ajaxReturn($return_data);} else { // 插入数据执行失败 $return_data = array(); $return_data['error_code'] = 1; $return_data['msg'] = '登录失败'; $this->ajaxReturn($return_data);}
}
}
}
}


推荐阅读
  • Shiro 简单了解
    Shiro简单了解简单用过SpringSecurity安全框架后,再试试另一个安全框架——Shiro。1.Shiro简介ApacheShiro是一个强大且易用的Java安全框架:S ... [详细]
  • MyBatis模糊查询和多条件查询一、ISmbmsUserDao层根据姓名模糊查询publicListgetUser();多条件查询publicList ... [详细]
  • 通过CreateDirectory命令创建相应的Directory之后,可以将目录的访问权限授予其他用户,这样其他用户就能通过外部表访问很多主机上的文件,而不需要登录到数据库服务器 ... [详细]
  •   uni-app开发教程,uni-app实例教程  UNI-APP开发(仿饿)开发课程:进入学习  推荐(免费):uni-app开发教程  文章目录  简介,网 ... [详细]
  • 开发笔记:深度探索!Android之OkHttp网络架构源码解析
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了深度探索!Android之OkHttp网络架构源码解析相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 开发网站你需要知晓的部分专用术语
      越来越多的企业和个人都在拥有属于自己的网站门户,首当其冲的就是你得知晓几个网站方面的专业术语,先是中就有好多的客户不明白这些,造成误会是正常的,那不如我们对它有个大致的了解,这样就不容易感觉 ... [详细]
  • flash代码_正点原子【STM32F407探索者】第三十九章 FLASH 模拟 EEPROM 实验
    1)资料下载:点击资料即可下载2)对正点原子Linux感兴趣的同学可以加群讨论:9354467413)关注正点原子公众号,获取最新资料更新 ... [详细]
  • 这一篇主要总结一下jQuery这个js在引入的时候做的一些初始化工作第一句window.undefinedwindow.undefined;是为了兼容低版本的IE而写的因为在低版本 ... [详细]
  • 篇首语:本文由编程笔记#小编为大家整理,主要介绍了将JSON数组反序列化为强类型的.NET对象相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 外层|条件下_MySQL还能这样玩第五篇之视图应该这样玩
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了MySQL还能这样玩---第五篇之视图应该这样玩相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 接口测试的方式有很多,比如可以用工具(jmeter,postman)之类,也可以自己写代码进行接口测试,工具的使用相对来说都比较简单,重点是要搞清楚项目接口的协议是什么,然后有针对 ... [详细]
  • java内存模型浅析_浅析Java内存模型
    在并发编程中,需要处理两个关键问题:线程之间如何通信以及线程之间如何同步。通信是指线程之间以何种机制来交换信息。同步是指程序中用于控制不同线程间操作发生 ... [详细]
  • 目录结构如下:Nginx基础知识NginxHTTP服务器的特色及优点Nginx的主要企业功能Nginx作为web服务器的主要应用场景包括:Nginx的安装安装环境 ... [详细]
  • 超赞!GitHub上百万下载量Java面试手册!颠覆你的认知
    金三面试不顺心,马上银四面试在即,自己复盘总觉得Java知识点很凌乱?没有合适的方法学习!今天分享这份GitHub上百万下载量Ja ... [详细]
  • handler机制_Handler机制与原理
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了Handler机制与原理相关的知识,希望对你有一定的参考价值。 ... [详细]
author-avatar
兔子东大门一手批发
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有