文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

php如何实现小程序登录功能

2023-06-21 22:49

关注

本篇内容介绍了“php如何实现小程序登录功能”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

RequestTask说明

方法说明
RequestTask.abort()中断请求任务。
RequestTask.onHeadersReceived(function callback)监听 HTTP Response Header 事件。会比请求完成事件更早。
RequestTask.offHeadersReceived(function callback)取消监听 HTTP Response Header 事件。
RequestTask.onChunkReceived(function callback)监听 Transfer-Encoding Chunk Received 事件。当接收到新的chunk时触发。
RequestTask.offChunkReceived(function callback)取消监听 Transfer-Encoding Chunk Received 事件。

wx.request(Object object)属性

此处只列比较常用的属性,全部属性请查看链接。

属性类型默认值必填说明
urlstring
开发者服务器接口地址
datastring/object/ArrayBuffer
请求的参数
headerObject
设置请求的 header,header 中不能设置 Referer。 content-type 默认为 application/json
timeoutnumber
超时时间,单位为毫秒
methodstringGETHTTP 请求方法
successfunction
接口调用成功的回调函数
failfunction
接口调用失败的回调函数
completefunction
接口调用结束的回调函数(调用成功、失败都会执行)哪怕是abort掉的请求!

总结一下:所有的小程序接口基本上都有两个特征:

接口执行的各种情况下的errMsg对象介绍。

回调属性errMsg对象
success{errMsg:"request:ok"...}
fail{errMsg:"request:fail "...} 有的系统这个fail后面有个空格,所以要使用这个判断,最好是使用正则表达式。也可以使用indexOf函数,大于-1进行判断。
abort{errMsg:"request:fail abort"...}

示例代码

  let reqTask = wx.request({      url: getApp().globalData.api,      success(res) {        if (res.errMsg === "request:ok") console.log("res", res);      },      fail(err) {        // if(err.errMsg.indexOf('request:fail')>-1) console.log('err', err);        if (/^request:fail/i.test(err.errMsg)) console.log("err", err);      },      complete(res) {        console.log("resOrErr", res);      },    });   const reqTaskOnHeadersReceived = (headers) => {      reqTask.offHeadersReceived(reqTaskOnHeadersReceived);      console.log("headers", headers);      // 由于请求还未完全结束,所以我们没办法获得请求的状态码,但是我们可以通过返回的requestBody的长度来进行判断。      // 两点说明:1. 两个~~可以把字符串数字快速转化为数字。      // 2. 为什么取小于19,是由于后台返回没有权限的requestBody的时候Content-length为“18”,正常情况下是大于19的。所以具体多少得看一下具体情况。      if (~~headers.header["Content-length"] < 19) reqTask.abort();    };    reqTask.onHeadersReceived(reqTaskOnHeadersReceived);

小程序登录接口

后端登录接口代码实现

后端使用NodeJS,web框架KOA版本^2.13.4,路由框架@koa/router版本^10.1.1,框架request,版本 ^2.88.2,jsonwebtoken用来加密解密token信息,版本^8.5.1

// app.jsconst Koa = require("koa");const Router = require("@koa/router");const WeixinAuth = require("./lib/koa2-weixin-auth");const jsonwebtoken = require("jsonwebtoken");const app = new Koa();// 小程序机票信息const miniProgramAppId = "*********";const miniProgramAppSecret = "***********";const weixinAuth = new WeixinAuth(miniProgramAppId, miniProgramAppSecret);const JWT_SECRET = "JWTSECRET";// 路由中间件需要安装@koa/router// 开启一个带群组的路由const router = new Router({  prefix: "/user",});// 这是正规的登陆方法// 添加一个参数,sessionKeyIsValid,代表sessionKey是否还有效router.post("/weixin-login", async (ctx) => {  let { code, userInfo, encryptedData, iv, sessionKeyIsValid } =    ctx.request.body;   // 解析openid  const token = await weixinAuth.getAccessToken(code);  userInfo.openid = token.data.openid;  // 这里可以自己进行处理,比方说记录到数据库,处理token等  let authorizationToken = jsonwebtoken.sign(    { name: userInfo.nickName },    JWT_SECRET,    { expiresIn: "1d" }  );  Object.assign(userInfo, { authorizationToken });  ctx.status = 200;  ctx.body = {    code: 200,    msg: "ok",    data: userInfo,  };});
// lib/koa2-weixin-auth.jsconst querystring = require("querystring");const request = require("request");const AccessToken = function (data) {  if (!(this instanceof AccessToken)) {    return new AccessToken(data);  }  this.data = data;};AccessToken.prototype.isValid = function () {  return (    !!this.data.session_key &&    new Date().getTime() < this.data.create_at + this.data.expires_in * 1000  );};const Auth = function (appid, appsecret) {  this.appid = appid;  this.appsecret = appsecret;  this.store = {};  this.getToken = function (openid) {    return this.store[openid];  };  this.saveToken = function (openid, token) {    this.store[openid] = token;  };};Auth.prototype.getAuthorizeURL = function (redirect_uri, scope, state) {  return new Promise((resolve, reject) => {    const url = "https://open.weixin.qq.com/connect/oauth3/authorize";    let info = {      appid: this.appid,      redirect_uri: redirect_uri,      scope: scope || "snsapi_base",      state: state || "",      response_type: "code",    };    resolve(url + "?" + querystring.stringify(info) + "#wechat_redirect");  });};Auth.prototype.processToken = function (data) {  data.create_at = new Date().getTime();  // 存储token  this.saveToken(data.openid, data);  return AccessToken(data);};Auth.prototype.getAccessToken = function (code) {  return new Promise((resolve, reject) => {    const url = "https://api.weixin.qq.com/sns/jscode2session";    //由于此框架版本很久没有更新了,此处地址发生了变化,需要修改为以上地址,不然会出现    //41008错误。这也是没有直接使用框架,引用本地使用的原因。    // const url = "https://api.weixin.qq.com/sns/oauth3/access_token";    const info = {      appid: this.appid,      secret: this.appsecret,      js_code: code,      grant_type: "authorization_code",    };    request.post(url, { form: info }, (err, res, body) => {      if (err) {        reject(err);      } else {        const data = JSON.parse(body);        resolve(this.processToken(data));      }    });  });};Auth.prototype.refreshAccessToken = function (refreshToken) {  return new Promise((resolve, reject) => {    const url = "https://api.weixin.qq.com/sns/oauth3/refresh_token";    var info = {      appid: this.appid,      grant_type: "refresh_token",      refresh_token: refreshToken,    };    request.post(url, { form: info }, (err, res, body) => {      if (err) {        reject(err);      } else {        const data = JSON.parse(body);        resolve(this.processToken(data));      }    });  });};Auth.prototype.getUser = async function (openid) {  const data = this.getToken(openid);  console.log("getUser", data);  if (!data) {    var error = new Error(      "No token for " + options.openid + ", please authorize first."    );    error.name = "NoOAuthTokenError";    throw error;  }  const token = AccessToken(data);  var accessToken;  if (token.isValid()) {    accessToken = token.data.session_key;  } else {    var newToken = await this.refreshAccessToken(token.data.refresh_token);    accessToken = newToken.data.session_key;  }  console.log("accessToken", accessToken);  return await this._getUser(openid, accessToken);};Auth.prototype._getUser = function (openid, accessToken, lang) {  return new Promise((resolve, reject) => {    const url = "https://api.weixin.qq.com/sns/userinfo";    const info = {      access_token: accessToken,      openid: openid,      lang: lang || "zh_CN",    };    request.post(url, { form: info }, (err, res, body) => {      if (err) {        reject(err);      } else {        resolve(JSON.parse(body));      }    });  });};Auth.prototype.getUserByCode = async function (code) {  const token = await this.getAccessToken(code);  return await this.getUser(token.data.openid);};module.exports = Auth;

小程序端登录代码实现

<!--pages/index.wxml--><view class="page-section">    <text class="page-section__title">微信登录</text>    <view class="btn-area">        <button  bindtap="getUserProfile" type="primary">登录</button>    </view></view>
// pages/index.jsPage({    data: {},  // 正确的登录方式  getUserProfile() {    // 推荐使用wx.getUserProfile获取用户信息,开发者每次通过该接口获取用户个人信息均需用户确认    // 开发者妥善保管用户快速填写的头像昵称,避免重复弹窗    wx.getUserProfile({      desc: "用于完善会员资料", // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写      success: (res) => {        let { userInfo, encryptedData, iv } = res;        const requestLoginApi = (code) => {          // 发起网络请求          wx.request({            url: "http://localhost:3000/user/weixin-login",            method: "POST",            header: {              "content-type": "application/json",            },            data: {              code,              userInfo,              encryptedData,              iv,            },            success(res) {              console.log("请求成功", res.data);              let token = res.data.data.authorizationToken;              wx.setStorageSync("token", token);              onUserLogin(token);              console.log("authorization", token);            },            fail(err) {              console.log("请求异常", err);            },          });        };        const onUserLogin = (token) => {          getApp().globalData.token = token;          wx.showToast({            title: "登录成功了",          });        };        //必须进行session是否过期检查,不然会出现第一次点击登录,服务器报Illegal Buffer        //的错误,但是第二次点击登录正常。        wx.checkSession({          success: (res) => {            // session_key 未过期,并且在本生命周期一直有效            console.log("在登陆中");            let token = wx.getStorageSync("token");            if (token) onUserLogin(token);          },          fail: (res) => {            // session_key已经失效,需要重新执行登录流程            wx.login({              success(res0) {                if (res0.code) {                  requestLoginApi(res0.code);                } else {                  console.log("登录失败!" + res0.errMsg);                }              },            });          },        });      },    });  },});

针对登录代码可以做哪些优化?

对于一个软件,就代码层面而言,需要追求最基本的几个方面(远不止这些,但是先姑且先做个好这些吧):

那么接下来就来优化一下代码吧:

模块化

可以把登录的代码模块化,代码如下:

// lib/login.jsfunction loginWithCallback(cb) {  // 推荐使用wx.getUserProfile获取用户信息,开发者每次通过该接口获取用户个人信息均需用户确认  // 开发者妥善保管用户快速填写的头像昵称,避免重复弹窗  wx.getUserProfile({    desc: "用于完善会员资料", // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写    success: (res) => {      let { userInfo, encryptedData, iv } = res;      const requestLoginApi = (code) => {        // 发起网络请求        wx.request({          url: "http://localhost:3000/user/weixin-login",          method: "POST",          header: {            "content-type": "application/json",          },          data: {            code,            userInfo,            encryptedData,            iv,          },          success(res) {            console.log("请求成功", res.data);            let token = res.data.data.authorizationToken;            wx.setStorageSync("token", token);            onUserLogin(token);            console.log("authorization", token);          },          fail(err) {            console.log("请求异常", err);          },        });      };      const onUserLogin = (token) => {        getApp().globalData.token = token;        wx.showToast({          title: "登录成功了",        });        if (cb && typeof cb == "function") cb(token);      };      wx.checkSession({        success: (res) => {          // session_key 未过期,并且在本生命周期一直有效          console.log("在登陆中");          let token = wx.getStorageSync("token");          if (token) onUserLogin(token);        },        fail: (res) => {          // session_key已经失效,需要重新执行登录流程          wx.login({            success(res0) {              if (res0.code) {                requestLoginApi(res0.code);              } else {                console.log("登录失败!" + res0.errMsg);              }            },          });        },      });    },  });}export default loginWithCallback;

Promise化

回调地狱问题,不利于代码的阅读,所以接下来我们基于Promise进行代码优化。有了 Promise 对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise 对象提供统一的接口,使得控制异步操作更加容易。

Promise的几个方法简介

方法名说明
Promise.prototype.then方法返回的是一个新的 Promise 对象,因此可以采用链式写法。这种设计使得嵌套的异步操作,可以被很容易得改写,从回调函数的"横向发展"改为"向下发展"。
Promise.prototype.catch是 Promise.prototype.then(null, rejection) 的别名,用于指定发生错误时的回调函数。Promise 对象的错误具有"冒泡"性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个 catch 语句捕获。
Promise.prototype.finally方法返回一个Promise。在promise结束时,无论结果是fulfilled或者是rejected,都会执行指定的回调函数。这为在Promise是否成功完成后都需要执行的代码提供了一种方式。
Promise.all这避免了同样的语句需要在then()catch()中各写一次的情况。Promise.all 方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。Promise.all 方法接受一个数组作为参数,var p = Promise.all([p1,p2,p3]);p1、p2、p3 都是 Promise 对象的实例。(Promise.all 方法的参数不一定是数组,但是必须具有 iterator 接口,且返回的每个成员都是 Promise 实例。)p 的状态由 p1、p2、p3 决定,分成两种情况。  (1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。 (2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
Promise.racePromise.race 方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。var p = Promise.race([p1,p2,p3]);上面代码中,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的Promise实例的返回值,就传递给p的返回值。
Promise.any接收一个Promise可迭代对象,只要其中的一个 promise 成功,就返回那个已经成功的 promise 。所有子实例都处于rejected状态,总的promise才处于rejected状态。
Promise.allSettled返回一个在所有给定的promise都已经fulfilledrejected后的promise,并带有一个对象数组,每个对象表示对应的promise结果。相比之下,Promise.all() 更适合彼此相互依赖或者在其中任何一个reject时立即结束。

小程序API接口Promise化并且把需要登录的调用接口模块化

安装插件。请先查看npm支持文档。

npm install --save miniprogram-api-promise

在微信开发者工具右方详情中勾选使用npm模块,并在菜单栏工具中点击构建npm。

初始化代码。

// app.jsimport {promisifyAll} from 'miniprogram-api-promise'import login from "../lib/login";const wxp ={}promisifyAll(wx,wxp)// 需要token的请求统一处理登录和设置header,并且处理错误信息wxp.requestNeedLogin = async function (args) {  let token = wx.getStorageSync("token");  if (!token) {    token = await loginWithPromise();  }  if (!args.header) args.header = {};  args.header["Authorization"] = `Bearer ${token}`;  return wxp.request(args).catch(console.error);};// app.jsApp({  wxp:wxp,});

改写login.js代码

// lib/login.jsfunction login() {  return new Promise((resolve, reject) => {    // 推荐使用wx.getUserProfile获取用户信息,开发者每次通过该接口获取用户个人信息均需用户确认    // 开发者妥善保管用户快速填写的头像昵称,避免重复弹窗    wx.getUserProfile({      desc: "用于完善会员资料", // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写       success:async (res0) => {        let {          userInfo,          encryptedData,          iv        } = res0;        const app = getApp();        try {          app.wxp.checkSession();        } catch (err) {          reject(err);        }        let token = wx.getStorageSync("token");        if (!token) {          let res1 = await app.wxp.login().catch(err => reject(err));          let code = res1.code;          let res = await app.wxp.request({            url: "http://localhost:3000/user/weixin-login",            method: "POST",            header: {              "content-type": "application/json",            },            data: {              code,              userInfo,              encryptedData,              iv,            }          }).catch(err => reject(err));          token = res.data.data.authorizationToken;          wx.setStorageSync("token", token);          app.globalData.token = token;          wx.showToast({            title: "登录成功了",          });          resolve(token);        }      },    });  })}export default login;

调用代码

<view class="container page-head">  <text class="page-section__title">需要登录的请求调用</text>  <view class="btn-area">    <button bindtap="request1" type="primary">请求1</button>    <button bindtap="request2" type="primary">请求2</button>  </view></view>
// pages/index.jsPage({    data: {},  request1() {    getApp().wxp.requestNeedLogin({        url: "http://localhost:3000/user/home?name=andying",      }).then(console.log)  },  request2() {    getApp().wxp.requestNeedLogin({        url: "http://localhost:3000/user/home?name=eva",      }).then(console.log)  },});

“php如何实现小程序登录功能”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯