微信小程序后端实现授权登录


Posted in Javascript onFebruary 24, 2020

登录与授权

官方文档

一.登录登录流程时序

微信小程序后端实现授权登录

说明:

调用

  1. wx.login()获取临时登录凭证code,并回传到开发者服务器。
  2. 调用code2Session接口,换取用户唯一标识 OpenID和会话密钥 session_key。

之后开发者服务器可以根据用户标识来生成自定义登录态,用于后续业务逻辑中前后端交互时识别用户身份。

注意:

会话密钥session_key是对用户数据进行加密签名的密钥。为了应用自身的数据安全,开发者服务器不应该把会话密钥下发到小程序,也不应该对外提供这个密钥。

临时登录凭证 code 只能使用一次

总结:
小程序端执行wx.login后在回调函数中就能拿到上图的code,然后把这个code传给我们后端程序,后端拿到这个这个code后,可以请求code2Session接口拿到用的openid和session_key,openid是用户在微信中唯一标识,我们就可以把这个两个值(val)存起来,然后返回一个键(key)给小程序端,下次小程序请求我们后端的时候,带上这个key,我们就能找到这个val,就可以,这样就把登入做好了。

wx.login

调用接口获取登录凭证(code)。通过凭证进而换取用户登录态信息,包括用户的唯一标识(openid)及本次登录的会话
密钥(session_key)等。用户数据的加解密通讯需要依赖会话密钥完成。[/code]

参数

属性 类型 默认值 必填 说明 最低版本
timeout number   超时时间,单位ms 1.9.90
success function   接口调用成功的回调函数  
fail function   接口调用失败的回调函数  
complete function   接口调用结束的回调函数(调用成功、失败都会执行)  

object.success 回调函数

参数

属性 类型 说明
code string 用户登录凭证(有效期五分钟)。开发者需要在开发者服务器后台调用 code2Session,使用 code 换取 openid 和 session_key 等信息

code2Session

本接口应在服务器端调用,详细说明参见服务端API。

登录凭证校验。通过wx.login()接口获得临时登录凭证 code 后传到开发者服务器调用此接口完成登录流程。更多使用方法详见小程序登录。

请求地址

GET https://api.weixin.qq.com/sns/jscode2sessionappid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code

请求参数

属性 类型 默认值 必填 说明
appid string   小程序 appId
secret string   小程序 appSecret
js_code string   登录时获取的 code
grant_type string   授权类型,此处只需填写 authorization_code

返回值

Object

返回的 JSON 数据包

属性 类型 说明
openid string 用户唯一标识
session_key string 会话密钥
unionid string 用户在开放平台的唯一标识符,在满足 UnionID 下发条件的情况下会返回,详见 UnionID 机制说明。
errcode number 错误码
errmsg string 错误信息

errcode 的合法值

说明
-1 系统繁忙,此时请开发者稍候再试
0 请求成功
40029 code 无效
45011 频率限制,每个用户每分钟100次

二.信息授权wx.getUserInfo

获取用户信息。

参数

属性 类型 默认值 必填 说明
withCredentials boolean   是否带上登录态信息。当 withCredentials 为 true 时,要求此前有调用过 wx.login 且登录态尚未过期,此时返回的数据会包含 encryptedData, iv 等敏感信息;当 withCredentials 为 false 时,不要求有登录态,返回的数据不包含 encryptedData, iv 等敏感信息。
lang string en 显示用户信息的语言
success function   接口调用成功的回调函数
fail function   接口调用失败的回调函数
complete function   接口调用结束的回调函数(调用成功、失败都会执行)

object.lang 的合法值

说明
en 英文
zh_CN 简体中文
zh_TW 繁体中文

object.success 回调函数

参数

属性 类型 说明
userInfo UserInfo 用户信息对象,不包含 openid 等敏感信息
rawData string 不包括敏感信息的原始数据字符串,用于计算签名
signature string 使用 sha1( rawData + sessionkey ) 得到字符串,用于校验用户信息,详见 用户数据的签名验证和加解密
encryptedData string 包括敏感数据在内的完整用户信息的加密数据,详见 用户数据的签名验证和加解密
iv string 加密算法的初始向量,详见 用户数据的签名验证和加解密

注意:

1.小程序端获取授权信息要用button按钮触发

2.小程序端需要将 encryptedData, iv, login_key 传到后端用于解密

案例:

登录:

当小程序第一次执行的时候就调用wx.login

小程序端:apps.js

App({
 onLaunch: function () {
 var _this=this
 // 登录
 wx.login({
  success: res => {
  // 发送 res.code 到后台换取 openId, sessionKey, unionId
  wx.request({
   url: _this.globalData.Url+'/login/', // 后端路径
   data:{"code":res.code}, // code
   header:{"content-type":"application/json"},
   method:"POST",
   success:function(res){
   console.log(res)
   // 小程序端存储login_key
   wx.setStorageSync("login_key",res.data.data.login_key)
   }
  })
  }
 })
 },
 globalData: {
 Url:"http://127.0.0.1:8000",
 userInfo: null
 }
})

后端 django

wx
 ├── settings.py  # 小程序id,code2Session等配置
 ├── wx_login.py  # 用于调用code2Session拿到openid等
 └── WXBizDataCrypt.py # 获取用户授权信息的解密算法,官方下载

微信官方解密算法代码

项目/settings.py

# 配置数据库
DATABASES = {
 'default': {
  'ENGINE': 'django.db.backends.mysql',
  'NAME': 'wx',
  'USER':'root',
  'PASSWORD':'root',
  'HOST':'127.0.0.1',
  'PORT': 3306,
  'OPTIONS': {'charset': 'utf8mb4'}, # 微信用户名可能有标签,所以用utf8mb4
 }
}

# 配置 django-redis
CACHES = {
 'default': {
  'BACKEND': 'django_redis.cache.RedisCache',
  'LOCATION': 'redis://127.0.0.1:6379',
  "OPTIONS": {
   "CLIENT_CLASS": "django_redis.client.DefaultClient",
    "PASSWORD": "",
  },
 },
}

wx/settings.py

# 小程序开发者id
AppId="..."
# 小程序的AppSecret
AppSecret="..."

code2Session="https://api.weixin.qq.com/sns/jscode2session?appid={}&secret={}&js_code={}&grant_type=authorization_code"
pay_mchid ='...'
pay_apikey = '...'

wx/wx_login.py

from app01.wx import settings
import requests

# 调用微信code2Session接口,换取用户唯一标识 OpenID 和 会话密钥 session_key
def login(code):
 response = requests.get(settings.code2Session.format(settings.AppId,settings.AppSecret,code))
 data = response.json()
 if data.get("openid"):
  return data
 else:
  return False

项目/views.py

from rest_framework.views import APIView
from rest_framework.response import Response
from app01.wx import wx_login
from django.core.cache import cache
from app01 import models
import time, hashlib

class Login(APIView):
 def post(self, request):
  param = request.data
  # 拿到小程序端提交的code
  if param.get('code'):
   # 调用微信code2Session接口,换取用户唯一标识 OpenID 和 会话密钥 session_key
   data = wx_login.login(param.get('code'))
   if data:
    # 将openid 和 session_key拼接
    val = data['openid'] + "&" + data["session_key"]
    key = data["openid"] + str(int(time.time()))
    # 将 openid 加密
    md5 = hashlib.md5()
    md5.update(key.encode("utf-8"))
    key = md5.hexdigest()
    # 保存到redis内存库,因为小程序端后续需要认证的操作会需要频繁校验
    cache.set(key, val)
    has_user = models.Wxuser.objects.filter(openid=data['openid']).first()
    # 用户不存在则创建用户
    if not has_user:
     models.Wxuser.objects.create(openid=data['openid'])
    return Response({
     "code": 200,
     "msg": "ok",
     "data": {"login_key": key} # 返回给小程序端
    })
   else:
    return Response({"code": 401, "msg": "code无效"})
  else:
   return Response({"code": 401, "msg": "缺少参数"})

用户信息授权

小程序端test.wxml

<!--用户信息授权-->
<button open-type="getUserInfo" bindgetuserinfo="info">授权登录</button>

test.js

Page({
info: function (res) {
 // console.log(res)
 wx.checkSession({
  success() {
  //session_key 未过期,并且在本生命周期一直有效
  wx.getUserInfo({
   success: function (res) {
   // console.log(res)
   wx.request({
    url: app.globalData.Url + "/getinfo/",
    data: { "encryptedData": res.encryptedData, "iv": res.iv, "login_key": wx.getStorageSync("login_key") },
    method: "POST",
    header: { "content-type": "application/json" },
    success: function (res) {
    console.log(res)
    }
   })
   }
  })

})

后端 django

wx/WXBizDataCrypt.py

import base64
import json
from Crypto.Cipher import AES
from app01.wx import settings

class WXBizDataCrypt:
 def __init__(self, appId, sessionKey):
  self.appId = appId
  self.sessionKey = sessionKey

 def decrypt(self, encryptedData, iv):
  # base64 decode
  sessionKey = base64.b64decode(self.sessionKey)
  encryptedData = base64.b64decode(encryptedData)
  iv = base64.b64decode(iv)

  cipher = AES.new(sessionKey, AES.MODE_CBC, iv)

  decrypted = json.loads(self._unpad(cipher.decrypt(encryptedData)))

  if decrypted['watermark']['appid'] != self.appId:
   raise Exception('Invalid Buffer')

  return decrypted

 def _unpad(self, s):
  return s[:-ord(s[len(s)-1:])]

 @classmethod
 def getInfo(cls,encryptedData,iv,session_key):
  return cls(settings.AppId,session_key).decrypt(encryptedData, iv)

项目/serializer.py

from rest_framework.serializers import ModelSerializer

from app01 import models
class User_ser(ModelSerializer):
 class Meta:
  model=models.Wxuser
  fields="__all__"

项目/views.py

from app01.wx import WXBizDataCrypt
from app01 import serializer
from app01 import models

class GetInfo(APIView):
 def post(self,request):
  param=request.data
  # 需要小程序端将 encryptedData iv login_key 的值传到后端
  # encryptedData iv seesion_key 用于解密获取用户信息
  # login_key 用于校验用户登录状态
  if param['encryptedData'] and param['iv'] and param['login_key']:
   # 从redis中拿到login_key并切分拿到 openid 和 session_key
   openid,seesion_key=cache.get(param['login_key']).split("&")
   # 利用微信官方提供算法拿到用户的开放数据
   data=WXBizDataCrypt.WXBizDataCrypt.getInfo(param['encryptedData'] ,param['iv'] ,seesion_key)
   save_data={
    "name":data['nickName'],
    "avatar":data['avatarUrl'],
    "language":data['language'],
    "province":data['province'],
    "city":data['city'],
    "country":data['country'],
   }
   # 将拿到的用户信息更新到用户表中
   models.Wxuser.objects.filter(openid=openid).update(**save_data)
   # 反序列化用户对象,并返回到小程序端
   data=models.Wxuser.objects.filter(openid=openid).first()
   data=serializer.User_ser(instance=data,many=False).data
   return Response({"code":200,"msg":"缺少参数","data":data})
  else:
   return Response({"code":200,"msg":"缺少参数"})

到此这篇关于微信小程序后端实现授权登录的文章就介绍到这了,更多相关微信小程序 授权登录内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
Javascript 判断客户端浏览器类型代码
Mar 01 Javascript
javascript内存管理详细解析
Nov 11 Javascript
js获取IP地址的方法小结
Jul 01 Javascript
JS实现支持多选的遍历下拉列表代码
Aug 20 Javascript
Vue.js学习示例分享
Feb 05 Javascript
微信小程序本作用域下调用全局JS详解及实例
Feb 22 Javascript
基于VuePress 轻量级静态网站生成器的实现方法
Apr 17 Javascript
Bootstrap开发中Tab标签页切换图表显示问题的解决方法
Jul 13 Javascript
解决layui数据表格排序图标被超出的表头挤出去的问题
Sep 19 Javascript
JS控制GIF图片的停止与显示
Oct 24 Javascript
Javascript ParentNode和ChildNode接口原理解析
Mar 16 Javascript
微信小程序tab左右滑动切换功能的实现代码
Feb 08 Javascript
Node使用Nodemailer发送邮件的方法实现
Feb 24 #Javascript
原生javascript实现类似vue的数据绑定功能示例【观察者模式】
Feb 24 #Javascript
Vue 技巧之控制父类的 slot
Feb 24 #Javascript
原生javascript的ajax请求及后台PHP响应操作示例
Feb 24 #Javascript
在 Vue 中编写 SVG 图标组件的方法
Feb 24 #Javascript
原生javascript中this几种常见用法总结
Feb 24 #Javascript
js实现坦克大战游戏
Feb 24 #Javascript
You might like
php中explode函数用法分析
2014/11/15 PHP
php-app开发接口加密详解
2018/04/18 PHP
js 数组实现一个类似ruby的迭代器
2009/10/27 Javascript
Javascript中Event属性搜集整理
2013/09/17 Javascript
jQuery中replaceWith()方法用法实例
2014/12/25 Javascript
jquery实现textarea输入框限制字数的方法
2015/01/15 Javascript
jquery中ajax使用error调试错误的方法
2015/02/08 Javascript
JavaScript获取页面中表单(form)数量的方法
2015/04/03 Javascript
百度地图API之本地搜索与范围搜索
2015/07/30 Javascript
轻松掌握JavaScript中的Math object数学对象
2016/05/26 Javascript
常用原生js自定义函数总结
2016/11/20 Javascript
JS定时器实现数值从0到10来回变化
2016/12/09 Javascript
xmlplus组件设计系列之下拉刷新(PullRefresh)(6)
2017/05/03 Javascript
Node.js 异步异常的处理与domain模块解析
2017/05/10 Javascript
AngularJS获取json数据的方法详解
2017/05/27 Javascript
简述vue状态管理模式之vuex
2018/08/29 Javascript
JS实现的点击按钮图片上下滚动效果示例
2019/01/28 Javascript
使用 js 简单的实现 bind、call 、aplly代码实例
2019/09/07 Javascript
小程序实现列表倒计时功能
2021/01/29 Javascript
vue中h5端打开app(判断是安卓还是苹果)
2021/02/26 Vue.js
Python批量转换文件编码格式
2015/05/17 Python
python找出一个列表中相同元素的多个索引实例
2019/06/11 Python
Python IDE环境之 新版Pycharm安装详细教程
2020/03/05 Python
Python+appium框架原生代码实现App自动化测试详解
2020/03/06 Python
Django实现随机图形验证码的示例
2020/10/15 Python
pycharm2020.1.2永久破解激活教程,实测有效
2020/10/29 Python
如何使用Django Admin管理后台导入CSV
2020/11/06 Python
html5 canvas 画图教程案例分析
2012/11/23 HTML / CSS
瑞士网球商店:Tennis-Point
2020/03/12 全球购物
酒店实习个人鉴定
2013/12/07 职场文书
公职人员索取回扣检举信
2014/04/04 职场文书
工作疏忽、懈怠的检讨书
2014/09/11 职场文书
2015年度护士个人工作总结
2015/04/09 职场文书
2016教师给学生的毕业寄语
2015/12/04 职场文书
2016年劳模先进事迹材料
2016/02/25 职场文书
win11如何查看端口是否被占用? Win11查看端口是否占用的技巧
2022/04/05 数码科技