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


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 mapreduce工作原理简析
Nov 25 Javascript
javascript间隔定时器(延时定时器)学习 间隔调用和延时调用
Jan 13 Javascript
node.js中的path.delimiter方法使用说明
Dec 09 Javascript
浅谈Javascript数据属性与访问器属性
Jul 26 Javascript
AngularJS 与Bootstrap实现表格分页实例代码
Oct 14 Javascript
JS中setTimeout和setInterval的最大延时值详解
Feb 13 Javascript
Egg.js 中 AJax 上传文件获取参数的方法
Oct 10 Javascript
小程序实现订单倒计时功能
Apr 23 Javascript
详解vue项目中调用百度地图API使用方法
Apr 25 Javascript
微信小程序下拉菜单效果的实例代码
May 14 Javascript
Vue自定义组件双向绑定实现原理及方法详解
Sep 03 Javascript
代码解析React中setState同步和异步问题
Jun 03 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
香妃
2021/03/03 冲泡冲煮
PHP读取数据库并按照中文名称进行排序实现代码
2013/01/29 PHP
PHP单态模式简单用法示例
2016/11/16 PHP
laravel 5.4中实现无限级分类的方法示例
2017/07/27 PHP
Laravel访问出错提示:`Warning: require(/vendor/autoload.php): failed to open stream: No such file or di解决方法
2019/04/02 PHP
JQuery防止退格键网页后退的实现代码
2012/03/23 Javascript
jquery动态添加删除div 具体实现
2013/07/20 Javascript
javascript 构造函数方式定义对象
2015/01/02 Javascript
自己编写的支持Ajax验证的JS表单验证插件
2015/05/15 Javascript
基于JQuery打造无缝滚动新闻步骤详解
2016/03/31 Javascript
Google Maps基础及实例解析
2016/08/06 Javascript
让浏览器崩溃的12行JS代码(DoS攻击分析及防御)
2016/10/10 Javascript
JavaScript中省略元素对数组长度的影响
2016/10/26 Javascript
ionic2 tabs 图标自定义实例
2017/03/08 Javascript
Node.JS循环删除非空文件夹及子目录下的所有文件
2018/03/12 Javascript
KOA+egg.js集成kafka消息队列的示例
2018/11/09 Javascript
vue.js实现备忘录demo
2019/06/26 Javascript
JS中的算法与数据结构之列表(List)实例详解
2019/08/16 Javascript
Js参数RSA加密传输之jsencrypt.js的使用
2020/02/07 Javascript
[51:52]Liquid vs Secret 2019国际邀请赛淘汰赛 败者组 BO3 第二场 8.24
2019/09/10 DOTA
python将人民币转换大写的脚本代码
2013/02/10 Python
python打开网页和暂停实例
2014/09/30 Python
python实现随机森林random forest的原理及方法
2017/12/21 Python
jupyter notebook引用from pyecharts.charts import Bar运行报错
2020/04/23 Python
Pyspider中给爬虫伪造随机请求头的实例
2018/05/07 Python
Python实现FM算法解析
2019/06/18 Python
pytorch使用指定GPU训练的实例
2019/08/19 Python
python实现上传文件到linux指定目录的方法
2020/01/03 Python
欧缇丽英国官方网站:Caudalie英国
2016/08/17 全球购物
理肤泉英国官网:La Roche-Posay英国
2019/01/14 全球购物
庆七一活动方案
2014/01/25 职场文书
设计专业毕业生求职信
2014/06/25 职场文书
合理化建议书
2015/02/04 职场文书
逃课检讨书范文
2015/05/06 职场文书
python实现对doc、txt、xls等文档的读写操作
2022/04/02 Python
mysql 索引的数据结构为什么要采用B+树
2022/04/26 MySQL