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


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 相关文章推荐
Jquery + Ajax调用webService实例代码(asp.net)
Aug 27 Javascript
javascript getElementsByClassName实现代码
Oct 11 Javascript
JavaScript中valueOf函数与toString方法深入理解
Dec 02 Javascript
javaScript矢量图表库-gRaphael几行代码实现精美的条形图/饼图/点图/曲线图
Jan 09 Javascript
JS实现下拉框的动态添加(附效果)
Apr 03 Javascript
JS Date函数整理方便使用
Oct 23 Javascript
jquery中EasyUI使用技巧小结
Feb 10 Javascript
jQuery中的基本选择器用法学习教程
Apr 14 Javascript
全面了解javascript三元运算符
Jun 27 Javascript
input file样式修改以及图片预览删除功能详细概括(推荐)
Aug 17 Javascript
vue.js使用v-model实现父子组件间的双向通信示例
Feb 05 Javascript
js实现轮播图特效
May 28 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
基于mysql的论坛(1)
2006/10/09 PHP
php5.3 废弃函数小结
2010/05/16 PHP
php的mkdir()函数创建文件夹比较安全的权限设置方法
2014/07/28 PHP
php魔法函数与魔法常量使用介绍
2017/07/23 PHP
清空上传控件input file的值
2010/07/03 Javascript
禁止页面刷新让F5快捷键及右键都无效
2014/01/22 Javascript
jquery实现弹出div,始终显示在屏幕正中间的简单实例
2014/03/08 Javascript
使用JS或jQuery模拟鼠标点击a标签事件代码
2014/03/10 Javascript
初识Node.js
2014/09/03 Javascript
Jquery判断form表单数据是否变化
2016/03/30 Javascript
Javascript操作表单实例讲解(下)
2016/06/20 Javascript
基于Bootstrap实现下拉菜单项和表单导航条(两个菜单项,一个下拉菜单和登录表单导航条)
2016/07/22 Javascript
移动端滑动插件Swipe教程
2016/10/16 Javascript
详解js前端代码异常监控
2017/01/11 Javascript
jQuery扩展_动力节点Java学院整理
2017/07/05 jQuery
vue-router+vuex addRoutes实现路由动态加载及菜单动态加载
2017/09/28 Javascript
iframe高度自适应及隐藏滚动条的实例详解
2017/09/29 Javascript
vue2.0中set添加属性后视图不能更新的解决办法
2019/02/22 Javascript
VUE 实现复制内容到剪贴板的两种方法
2019/04/24 Javascript
Python httplib模块使用实例
2015/04/11 Python
详解【python】str与json类型转换
2019/04/29 Python
使用pyshp包进行shapefile文件修改的例子
2019/12/06 Python
python如何把字符串类型list转换成list
2020/02/18 Python
numpy库ndarray多维数组的维度变换方法(reshape、resize、swapaxes、flatten)
2020/04/28 Python
python 如何将office文件转换为PDF
2020/09/22 Python
python中的yield from语法快速学习
2020/11/06 Python
使用Python下载抖音各大V视频的思路详解
2021/02/06 Python
python绘图模块之利用turtle画图
2021/02/12 Python
Html5页面获取微信公众号的openid的方法
2020/05/12 HTML / CSS
IFCHIC台湾:欧美国际设计师品牌
2019/05/18 全球购物
面料业务员岗位职责
2013/12/26 职场文书
初一科学教学反思
2014/01/27 职场文书
就业自我评价
2014/02/04 职场文书
营销团队口号
2014/06/06 职场文书
2014年销售助理工作总结
2014/12/01 职场文书
2015年基层党建工作总结
2015/05/14 职场文书