全栈修仙之路(一):微信小程序登录流程理解实现;

963 阅读4分钟

一、官方登录流程图详解;

上图是微信小程序官方对于用户登录的流程说明,下面我们对各个步骤去做一些说明:

创建一个微信小程序的项目以后,我们可以看到app.js文件里面提供了一个登录的功能参考案例,基于这个我们做一些改进说明;

  • 1、首先这里我把wx.login修改为用promise的方式进行处理,通过wx.login 这个api获取用的code 值,然后我们的后端服务器去做一个接口去接收到用户的code值(这里的api后端服务器,我是用golang的技术去实现的,附上接口代码)
 // 登录promise
   let self = this;
   const userLogin = new Promise(function (resolve, reject) {
     wx.login({
       success: res => {
         // 发送 res.code 到后台换取 openId, sessionKey, unionId
         webHttp.get(api.userCode.opendId, {code: res.code})
         .then((res) => {
           resolve();
         }).catch( err => {
           reject(err);
         })
       }
     })
   })
  • 2、上一步我们把前端获取到的usercode及小程序的appid、secret信息获取到发送到服务器,服务器端通过这些数据去微信接口获取到用户的openid、session_key、unionid信息(下面为golang后端实现方式)
package v1

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"miniprogram_api/models"
	"miniprogram_api/pkg/e"
	"strings"

	"github.com/gin-gonic/gin"
)

// 缓存请求回来的 session_key openid unionid 等信息,直接导入数据库
var WxUserInfo map[string]interface{}


// 微信用户登录,获取用户的code、openid、session_key

func GetOpenid(c *gin.Context) {
	// 获取微信用户登录零时code、小程序码、密钥
	userCode := c.Query("code")
	appid := "********"
	secret := "**************"
	code := e.SUCCESS
	if userCode != "" {
		// 获取用户的openid session_key
		result, _ := SendCodeToWx(userCode, appid, secret)
		WxUserInfo = result
		c.JSON(http.StatusOK, gin.H{
			"code": code,
			"msg":  e.GetMsg(code),
		})
		return
	}
	code = e.ERROR_GET_CODE

	c.JSON(http.StatusOK, gin.H{
		"code": code,
		"msg":  e.GetMsg(code),
		"data": make(map[string]interface{}),
	})
}

func SendCodeToWx(code, appid, AppSecret string) (map[string]interface{}, error) {
	// 请求地址拼接
	url := "https://api.weixin.qq.com/sns/jscode2session?js_code=" + code + "&appid=" + appid + "&secret=" + AppSecret + "&grant_type=authorization_code"
	client := &http.Client{}
	result := make(map[string]interface{})

	// get请求模拟,获取openid
	req, err := http.NewRequest("GET", url, strings.NewReader("name=cjb"))
	if err != nil {
		return result, err
	}
	resp, err := client.Do(req)
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return result, err
	}
	json.Unmarshal(body, &result)
	return result, err
}

其中 api.weixin.qq.com/sns/jscode2… 这个是微信提供的openid获取接口,下图是微信官方提供的接口信息详解

  • 3、申请完毕我们后端可以用jwt做用户登录token验证,这块我暂时没有加入token的方式对用api访问的权限控制(服务器端jwt还没有写完,后续再增加)

4、第四步通过wx.getUserInfo获取登录账号的昵称、avater、地区等等一些信息,然后获取到信息我们可以把它存储到自己的用户表里面,下面是我做的微信端的用户数据请求及服务器端做的接口去存储信息

完整的app.js实现方式

// 这是微信请求封装
import webHttp from './utils/request';
// api位置同意管理
import api from './utils/apiconfig';

//app.js
App({
  onLaunch: function () {
    // 展示本地存储能力
    // var logs = wx.getStorageSync('logs') || []
    // logs.unshift(Date.now())
    // wx.setStorageSync('logs', logs)

    // 登录promise
    let self = this;
    const userLogin = new Promise(function (resolve, reject) {
      wx.login({
        success: res => {
          // 发送 res.code 到后台换取 openId, sessionKey, unionId
          webHttp.get(api.userCode.opendId, {code: res.code})
          .then((res) => {
            resolve();
          }).catch( err => {
            reject(err);
          })
        }
      })
    })
    
    // 获取用户信息
    
    const getSetting = new Promise(function (resolve, reject) {
      wx.getSetting({
        success: res => {
          if (res.authSetting['scope.userInfo']) {
            // 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框
            wx.getUserInfo({
              success: res => {
                // 可以将 res 发送给后台解码出 unionId
                self.globalData.userInfo = res.userInfo
                // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
                // 所以此处加入 callback 以防止这种情况
                if (self.userInfoReadyCallback) {
                  self.userInfoReadyCallback(res)
                }
                resolve();
              }
            })
          }
        }
      });
    })
    Promise.all([userLogin, getSetting]).then((res) => { 
      userInfoSet()
    }).catch((err) => {
      console.log(err)
    })
    // 用户资料存储数据库
    function userInfoSet() {
      webHttp.get(api.user.info, {
        ...self.globalData.userInfo
      }).then((res) => {
        console.log(res);
      })
    }
  },
  globalData: {
    userInfo: null,
  }
})

下面是golang数据库表结构设计及存储

package models

import (
	"time"

	"github.com/jinzhu/gorm"
)

type User struct {
	Model

	CreatedBy  string `json:"created_by"`
	ModifiedBy string `json:"modified_by"`
	Opendid    string `json:"opendid"`
	NickName     string `json:"nick_name"`
	AvaterUrl	string `json:"avater_url"`
	Gender		string	`json:"gender"`
}

// 判断用户是否已经被注册,同时获取用户资料
func Login(openid string) (bool, User) {
	var user User
	db.Where("openid=?", openid).First(&user)
	if user.ID > 0 {
		return true, user
	}
	return false, user
}

// 修改用户资料
func EditInfo(opendid string, data interface{}) bool {
	db.Model(&User{}).Where("opendi = ?", opendid).Updates(data)

	return true
}

// 小程序登录用户表,增加用户信息
func RegisterUserInfo(data map[string]interface{}) bool {
	db.Create(&User{
		Opendid: data["openid"].(string),
		NickName: data["nick_name"].(string),
		AvaterUrl:data["avater_url"].(string),
		Gender: data["gender"].(string),
	})

	return true
}


// 修改调整时间戳
func (article *User) BeforeCreate(scope *gorm.Scope) error {
	scope.SetColumn("CreatedOn", time.Now().Unix())
	return nil
}

func (article *User) BeforeUpdate(scope *gorm.Scope) error {
	scope.SetColumn("ModifiedOn", time.Now().Unix())
	return nil
}

大概流程理解是这样的一个过程,这是全栈修仙过程中的开篇,也是自己尝试着把自己自学的一些东西慢慢的融合起来做一个小程序的demo项目,同时小程序也算是刚刚上手,题主本来是个单纯的前端,迫于爱好与生计开始学习一些东西,聊以慰籍,有什么理解错误的地方,希望各位大佬指点迷津,进步永无止境,与君共勉~