首页 >> js开发 >> js微信小程序后端实现授权登录js大全
js微信小程序后端实现授权登录js大全
发布时间: 2021年1月13日 | 浏览:
| 分类:js开发
登录与授权登录与授权登录与授权官方文档官方文档一.登录登录流程时序一.登录登录流程时序一.登录登录流程时序说明:调用调用
wx.login()获取临时登录凭证code,并回传到开发者服务器。
调用code2Session接口,换取用户唯一标识 OpenID和会话密钥 session_key。
wx.login()获取临时登录凭证code,并回传到开发者服务器。wx.login()调用code2Session接口,换取用户唯一标识 OpenID和会话密钥 session_key。code2Session之后开发者服务器可以根据用户标识来生成自定义登录态,用于后续业务逻辑中前后端交互时识别用户身份。注意:注意:会话密钥session_key是对用户数据进行加密签名的密钥。为了应用自身的数据安全,开发者服务器不应该把会话密钥下发到小程序,也不应该对外提供这个密钥。session_key加密签名临时登录凭证 code 只能使用一次总结:
小程序端执行wx.login后在回调函数中就能拿到上图的code,然后把这个code传给我们后端程序,后端拿到这个这个code后,可以请求code2Session接口拿到用的openid和session_key,openid是用户在微信中唯一标识,我们就可以把这个两个值(val)存起来,然后返回一个键(key)给小程序端,下次小程序请求我们后端的时候,带上这个key,我们就能找到这个val,就可以,这样就把登入做好了。总结:wx.loginwx.loginwx.login调用接口获取登录凭证(code)。通过凭证进而换取用户登录态信息,包括用户的唯一标识(openid)及本次登录的会话
密钥(session_key)等。用户数据的加解密通讯需要依赖会话密钥完成。[/code]参数
属性
类型
默认值
必填
说明
最低版本
timeout
number
否
超时时间,单位ms
1.9.90
success
function
否
接口调用成功的回调函数
fail
function
否
接口调用失败的回调函数
complete
function
否
接口调用结束的回调函数(调用成功、失败都会执行)
属性
类型
默认值
必填
说明
最低版本
属性
类型
默认值
必填
说明
最低版本
属性类型默认值必填说明最低版本
timeout
number
否
超时时间,单位ms
1.9.90
success
function
否
接口调用成功的回调函数
fail
function
否
接口调用失败的回调函数
complete
function
否
接口调用结束的回调函数(调用成功、失败都会执行)
timeout
number
否
超时时间,单位ms
1.9.90
timeoutnumber 否超时时间,单位ms1.9.901.9.90
success
function
否
接口调用成功的回调函数
successfunction 否接口调用成功的回调函数
fail
function
否
接口调用失败的回调函数
failfunction 否接口调用失败的回调函数
complete
function
否
接口调用结束的回调函数(调用成功、失败都会执行)
completefunction 否接口调用结束的回调函数(调用成功、失败都会执行) object.success 回调函数参数
属性
类型
说明
code
string
用户登录凭证(有效期五分钟)。开发者需要在开发者服务器后台调用 code2Session,使用 code 换取 openid 和 session_key 等信息
属性
类型
说明
属性
类型
说明
属性类型说明
code
string
用户登录凭证(有效期五分钟)。开发者需要在开发者服务器后台调用 code2Session,使用 code 换取 openid 和 session_key 等信息
code
string
用户登录凭证(有效期五分钟)。开发者需要在开发者服务器后台调用 code2Session,使用 code 换取 openid 和 session_key 等信息
codestring用户登录凭证(有效期五分钟)。开发者需要在开发者服务器后台调用 code2Session,使用 code 换取 openid 和 session_key 等信息code2Sessioncode2Sessioncode2Sessioncode2Session本接口应在服务器端调用,详细说明参见服务端API。服务端API登录凭证校验。通过wx.login()接口获得临时登录凭证 code 后传到开发者服务器调用此接口完成登录流程。使用方法详见小程序登录。wx.login()小程序登录请求地址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
属性
类型
默认值
必填
说明
属性
类型
默认值
必填
说明
属性类型默认值必填说明
appid
string
是
小程序 appId
secret
string
是
小程序 appSecret
js_code
string
是
登录时获取的 code
grant_type
string
是
授权类型,此处只需填写 authorization_code
appid
string
是
小程序 appId
appidstring 是小程序 appId
secret
string
是
小程序 appSecret
secretstring 是小程序 appSecret
js_code
string
是
登录时获取的 code
js_codestring 是登录时获取的 code
grant_type
string
是
授权类型,此处只需填写 authorization_code
grant_typestring 是授权类型,此处只需填写 authorization_code返回值Object返回的 JSON 数据包
属性
类型
说明
openid
string
用户唯一标识
session_key
string
会话密钥
unionid
string
用户在开放平台的唯一标识符,在满足 UnionID 下发条件的情况下会返回,详见 UnionID 机制说明。
errcode
number
错误码
errmsg
string
错误信息
属性
类型
说明
属性
类型
说明
属性类型说明
openid
string
用户唯一标识
session_key
string
会话密钥
unionid
string
用户在开放平台的唯一标识符,在满足 UnionID 下发条件的情况下会返回,详见 UnionID 机制说明。
errcode
number
错误码
errmsg
string
错误信息
openid
string
用户唯一标识
openidstring用户唯一标识
session_key
string
会话密钥
session_keystring会话密钥
unionid
string
用户在开放平台的唯一标识符,在满足 UnionID 下发条件的情况下会返回,详见 UnionID 机制说明。
unionidstring用户在开放平台的唯一标识符,在满足 UnionID 下发条件的情况下会返回,详见 UnionID 机制说明。UnionID 机制说明
errcode
number
错误码
errcodenumber错误码
errmsg
string
错误信息
errmsgstring错误信息errcode 的合法值
值
说明
-1
系统繁忙,此时请开发者稍候再试
0
请求成功
40029
code 无效
45011
频率限制,每个用户每分钟100次
值
说明
值
说明
值说明
-1
系统繁忙,此时请开发者稍候再试
0
请求成功
40029
code 无效
45011
频率限制,每个用户每分钟100次
-1
系统繁忙,此时请开发者稍候再试
-1系统繁忙,此时请开发者稍候再试
0
请求成功
0请求成功
40029
code 无效
40029code 无效
45011
频率限制,每个用户每分钟100次
45011频率限制,每个用户每分钟100次
二.信息授权wx.getUserInfo二.信息授权wx.getUserInfo二.信息授权wx.getUserInfo获取用户信息。参数
属性
类型
默认值
必填
说明
withCredentials
boolean
否
是否带上登录态信息。当 withCredentials 为 true 时,要求此前有调用过 wx.login 且登录态尚未过期,此时返回的数据会包含 encryptedData, iv 等敏感信息;当 withCredentials 为 false 时,不要求有登录态,返回的数据不包含 encryptedData, iv 等敏感信息。
lang
string
en
否
显示用户信息的语言
success
function
否
接口调用成功的回调函数
fail
function
否
接口调用失败的回调函数
complete
function
否
接口调用结束的回调函数(调用成功、失败都会执行)
属性
类型
默认值
必填
说明
属性
类型
默认值
必填
说明
属性类型默认值必填说明
withCredentials
boolean
否
是否带上登录态信息。当 withCredentials 为 true 时,要求此前有调用过 wx.login 且登录态尚未过期,此时返回的数据会包含 encryptedData, iv 等敏感信息;当 withCredentials 为 false 时,不要求有登录态,返回的数据不包含 encryptedData, iv 等敏感信息。
lang
string
en
否
显示用户信息的语言
success
function
否
接口调用成功的回调函数
fail
function
否
接口调用失败的回调函数
complete
function
否
接口调用结束的回调函数(调用成功、失败都会执行)
withCredentials
boolean
否
是否带上登录态信息。当 withCredentials 为 true 时,要求此前有调用过 wx.login 且登录态尚未过期,此时返回的数据会包含 encryptedData, iv 等敏感信息;当 withCredentials 为 false 时,不要求有登录态,返回的数据不包含 encryptedData, iv 等敏感信息。
withCredentialsboolean 否是否带上登录态信息。当 withCredentials 为 true 时,要求此前有调用过 wx.login 且登录态尚未过期,此时返回的数据会包含 encryptedData, iv 等敏感信息;当 withCredentials 为 false 时,不要求有登录态,返回的数据不包含 encryptedData, iv 等敏感信息。
lang
string
en
否
显示用户信息的语言
langstringen否显示用户信息的语言
success
function
否
接口调用成功的回调函数
successfunction 否接口调用成功的回调函数
fail
function
否
接口调用失败的回调函数
failfunction 否接口调用失败的回调函数
complete
function
否
接口调用结束的回调函数(调用成功、失败都会执行)
completefunction 否接口调用结束的回调函数(调用成功、失败都会执行)object.lang 的合法值
值
说明
en
英文
zh_CN
简体中文
zh_TW
繁体中文
值
说明
值
说明
值说明
en
英文
zh_CN
简体中文
zh_TW
繁体中文
en
英文
en英文
zh_CN
简体中文
zh_CN简体中文
zh_TW
繁体中文
zh_TW繁体中文object.success 回调函数参数
属性
类型
说明
userInfo
UserInfo
用户信息对象,不包含 openid 等敏感信息
rawData
string
不包括敏感信息的原始数据字符串,用于计算签名
signature
string
使用 sha1( rawData + sessionkey ) 得到字符串,用于校验用户信息,详见 用户数据的签名验证和加解密
encryptedData
string
包括敏感数据在内的完整用户信息的加密数据,详见 用户数据的签名验证和加解密
iv
string
加密算法的初始向量,详见 用户数据的签名验证和加解密
属性
类型
说明
属性
类型
说明
属性类型说明
userInfo
UserInfo
用户信息对象,不包含 openid 等敏感信息
rawData
string
不包括敏感信息的原始数据字符串,用于计算签名
signature
string
使用 sha1( rawData + sessionkey ) 得到字符串,用于校验用户信息,详见 用户数据的签名验证和加解密
encryptedData
string
包括敏感数据在内的完整用户信息的加密数据,详见 用户数据的签名验证和加解密
iv
string
加密算法的初始向量,详见 用户数据的签名验证和加解密
userInfo
UserInfo
用户信息对象,不包含 openid 等敏感信息
userInfoUserInfoUserInfo用户信息对象,不包含 openid 等敏感信息
rawData
string
不包括敏感信息的原始数据字符串,用于计算签名
rawDatastring不包括敏感信息的原始数据字符串,用于计算签名
signature
string
使用 sha1( rawData + sessionkey ) 得到字符串,用于校验用户信息,详见 用户数据的签名验证和加解密
signaturestring使用 sha1( rawData + sessionkey ) 得到字符串,用于校验用户信息,详见 用户数据的签名验证和加解密用户数据的签名验证和加解密
encryptedData
string
包括敏感数据在内的完整用户信息的加密数据,详见 用户数据的签名验证和加解密
encryptedDatastring包括敏感数据在内的完整用户信息的加密数据,详见 用户数据的签名验证和加解密用户数据的签名验证和加解密
iv
string
加密算法的初始向量,详见 用户数据的签名验证和加解密
ivstring加密算法的初始向量,详见 用户数据的签名验证和加解密
用户数据的签名验证和加解密注意: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
}
})
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 # 获取用户授权信息的解密算法,官方下载
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": "",
},
},
}
# 配置数据库
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 = '...'
# 小程序开发者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
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": "缺少参数"})
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
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)
}
})
}
})
})
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)
}
})
}
})
})后端 djangowx/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)
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__"
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":"缺少参数"})
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":"缺少参数"})
wx.login()获取临时登录凭证code,并回传到开发者服务器。
调用code2Session接口,换取用户唯一标识 OpenID和会话密钥 session_key。
wx.login()获取临时登录凭证code,并回传到开发者服务器。wx.login()调用code2Session接口,换取用户唯一标识 OpenID和会话密钥 session_key。code2Session之后开发者服务器可以根据用户标识来生成自定义登录态,用于后续业务逻辑中前后端交互时识别用户身份。注意:注意:会话密钥session_key是对用户数据进行加密签名的密钥。为了应用自身的数据安全,开发者服务器不应该把会话密钥下发到小程序,也不应该对外提供这个密钥。session_key加密签名临时登录凭证 code 只能使用一次总结:
小程序端执行wx.login后在回调函数中就能拿到上图的code,然后把这个code传给我们后端程序,后端拿到这个这个code后,可以请求code2Session接口拿到用的openid和session_key,openid是用户在微信中唯一标识,我们就可以把这个两个值(val)存起来,然后返回一个键(key)给小程序端,下次小程序请求我们后端的时候,带上这个key,我们就能找到这个val,就可以,这样就把登入做好了。总结:wx.loginwx.loginwx.login调用接口获取登录凭证(code)。通过凭证进而换取用户登录态信息,包括用户的唯一标识(openid)及本次登录的会话
密钥(session_key)等。用户数据的加解密通讯需要依赖会话密钥完成。[/code]参数
属性
类型
默认值
必填
说明
最低版本
timeout
number
否
超时时间,单位ms
1.9.90
success
function
否
接口调用成功的回调函数
fail
function
否
接口调用失败的回调函数
complete
function
否
接口调用结束的回调函数(调用成功、失败都会执行)
属性
类型
默认值
必填
说明
最低版本
属性
类型
默认值
必填
说明
最低版本
属性类型默认值必填说明最低版本
timeout
number
否
超时时间,单位ms
1.9.90
success
function
否
接口调用成功的回调函数
fail
function
否
接口调用失败的回调函数
complete
function
否
接口调用结束的回调函数(调用成功、失败都会执行)
timeout
number
否
超时时间,单位ms
1.9.90
timeoutnumber 否超时时间,单位ms1.9.901.9.90
success
function
否
接口调用成功的回调函数
successfunction 否接口调用成功的回调函数
fail
function
否
接口调用失败的回调函数
failfunction 否接口调用失败的回调函数
complete
function
否
接口调用结束的回调函数(调用成功、失败都会执行)
completefunction 否接口调用结束的回调函数(调用成功、失败都会执行) object.success 回调函数参数
属性
类型
说明
code
string
用户登录凭证(有效期五分钟)。开发者需要在开发者服务器后台调用 code2Session,使用 code 换取 openid 和 session_key 等信息
属性
类型
说明
属性
类型
说明
属性类型说明
code
string
用户登录凭证(有效期五分钟)。开发者需要在开发者服务器后台调用 code2Session,使用 code 换取 openid 和 session_key 等信息
code
string
用户登录凭证(有效期五分钟)。开发者需要在开发者服务器后台调用 code2Session,使用 code 换取 openid 和 session_key 等信息
codestring用户登录凭证(有效期五分钟)。开发者需要在开发者服务器后台调用 code2Session,使用 code 换取 openid 和 session_key 等信息code2Sessioncode2Sessioncode2Sessioncode2Session本接口应在服务器端调用,详细说明参见服务端API。服务端API登录凭证校验。通过wx.login()接口获得临时登录凭证 code 后传到开发者服务器调用此接口完成登录流程。使用方法详见小程序登录。wx.login()小程序登录请求地址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
属性
类型
默认值
必填
说明
属性
类型
默认值
必填
说明
属性类型默认值必填说明
appid
string
是
小程序 appId
secret
string
是
小程序 appSecret
js_code
string
是
登录时获取的 code
grant_type
string
是
授权类型,此处只需填写 authorization_code
appid
string
是
小程序 appId
appidstring 是小程序 appId
secret
string
是
小程序 appSecret
secretstring 是小程序 appSecret
js_code
string
是
登录时获取的 code
js_codestring 是登录时获取的 code
grant_type
string
是
授权类型,此处只需填写 authorization_code
grant_typestring 是授权类型,此处只需填写 authorization_code返回值Object返回的 JSON 数据包
属性
类型
说明
openid
string
用户唯一标识
session_key
string
会话密钥
unionid
string
用户在开放平台的唯一标识符,在满足 UnionID 下发条件的情况下会返回,详见 UnionID 机制说明。
errcode
number
错误码
errmsg
string
错误信息
属性
类型
说明
属性
类型
说明
属性类型说明
openid
string
用户唯一标识
session_key
string
会话密钥
unionid
string
用户在开放平台的唯一标识符,在满足 UnionID 下发条件的情况下会返回,详见 UnionID 机制说明。
errcode
number
错误码
errmsg
string
错误信息
openid
string
用户唯一标识
openidstring用户唯一标识
session_key
string
会话密钥
session_keystring会话密钥
unionid
string
用户在开放平台的唯一标识符,在满足 UnionID 下发条件的情况下会返回,详见 UnionID 机制说明。
unionidstring用户在开放平台的唯一标识符,在满足 UnionID 下发条件的情况下会返回,详见 UnionID 机制说明。UnionID 机制说明
errcode
number
错误码
errcodenumber错误码
errmsg
string
错误信息
errmsgstring错误信息errcode 的合法值
值
说明
-1
系统繁忙,此时请开发者稍候再试
0
请求成功
40029
code 无效
45011
频率限制,每个用户每分钟100次
值
说明
值
说明
值说明
-1
系统繁忙,此时请开发者稍候再试
0
请求成功
40029
code 无效
45011
频率限制,每个用户每分钟100次
-1
系统繁忙,此时请开发者稍候再试
-1系统繁忙,此时请开发者稍候再试
0
请求成功
0请求成功
40029
code 无效
40029code 无效
45011
频率限制,每个用户每分钟100次
45011频率限制,每个用户每分钟100次
二.信息授权wx.getUserInfo二.信息授权wx.getUserInfo二.信息授权wx.getUserInfo获取用户信息。参数
属性
类型
默认值
必填
说明
withCredentials
boolean
否
是否带上登录态信息。当 withCredentials 为 true 时,要求此前有调用过 wx.login 且登录态尚未过期,此时返回的数据会包含 encryptedData, iv 等敏感信息;当 withCredentials 为 false 时,不要求有登录态,返回的数据不包含 encryptedData, iv 等敏感信息。
lang
string
en
否
显示用户信息的语言
success
function
否
接口调用成功的回调函数
fail
function
否
接口调用失败的回调函数
complete
function
否
接口调用结束的回调函数(调用成功、失败都会执行)
属性
类型
默认值
必填
说明
属性
类型
默认值
必填
说明
属性类型默认值必填说明
withCredentials
boolean
否
是否带上登录态信息。当 withCredentials 为 true 时,要求此前有调用过 wx.login 且登录态尚未过期,此时返回的数据会包含 encryptedData, iv 等敏感信息;当 withCredentials 为 false 时,不要求有登录态,返回的数据不包含 encryptedData, iv 等敏感信息。
lang
string
en
否
显示用户信息的语言
success
function
否
接口调用成功的回调函数
fail
function
否
接口调用失败的回调函数
complete
function
否
接口调用结束的回调函数(调用成功、失败都会执行)
withCredentials
boolean
否
是否带上登录态信息。当 withCredentials 为 true 时,要求此前有调用过 wx.login 且登录态尚未过期,此时返回的数据会包含 encryptedData, iv 等敏感信息;当 withCredentials 为 false 时,不要求有登录态,返回的数据不包含 encryptedData, iv 等敏感信息。
withCredentialsboolean 否是否带上登录态信息。当 withCredentials 为 true 时,要求此前有调用过 wx.login 且登录态尚未过期,此时返回的数据会包含 encryptedData, iv 等敏感信息;当 withCredentials 为 false 时,不要求有登录态,返回的数据不包含 encryptedData, iv 等敏感信息。
lang
string
en
否
显示用户信息的语言
langstringen否显示用户信息的语言
success
function
否
接口调用成功的回调函数
successfunction 否接口调用成功的回调函数
fail
function
否
接口调用失败的回调函数
failfunction 否接口调用失败的回调函数
complete
function
否
接口调用结束的回调函数(调用成功、失败都会执行)
completefunction 否接口调用结束的回调函数(调用成功、失败都会执行)object.lang 的合法值
值
说明
en
英文
zh_CN
简体中文
zh_TW
繁体中文
值
说明
值
说明
值说明
en
英文
zh_CN
简体中文
zh_TW
繁体中文
en
英文
en英文
zh_CN
简体中文
zh_CN简体中文
zh_TW
繁体中文
zh_TW繁体中文object.success 回调函数参数
属性
类型
说明
userInfo
UserInfo
用户信息对象,不包含 openid 等敏感信息
rawData
string
不包括敏感信息的原始数据字符串,用于计算签名
signature
string
使用 sha1( rawData + sessionkey ) 得到字符串,用于校验用户信息,详见 用户数据的签名验证和加解密
encryptedData
string
包括敏感数据在内的完整用户信息的加密数据,详见 用户数据的签名验证和加解密
iv
string
加密算法的初始向量,详见 用户数据的签名验证和加解密
属性
类型
说明
属性
类型
说明
属性类型说明
userInfo
UserInfo
用户信息对象,不包含 openid 等敏感信息
rawData
string
不包括敏感信息的原始数据字符串,用于计算签名
signature
string
使用 sha1( rawData + sessionkey ) 得到字符串,用于校验用户信息,详见 用户数据的签名验证和加解密
encryptedData
string
包括敏感数据在内的完整用户信息的加密数据,详见 用户数据的签名验证和加解密
iv
string
加密算法的初始向量,详见 用户数据的签名验证和加解密
userInfo
UserInfo
用户信息对象,不包含 openid 等敏感信息
userInfoUserInfoUserInfo用户信息对象,不包含 openid 等敏感信息
rawData
string
不包括敏感信息的原始数据字符串,用于计算签名
rawDatastring不包括敏感信息的原始数据字符串,用于计算签名
signature
string
使用 sha1( rawData + sessionkey ) 得到字符串,用于校验用户信息,详见 用户数据的签名验证和加解密
signaturestring使用 sha1( rawData + sessionkey ) 得到字符串,用于校验用户信息,详见 用户数据的签名验证和加解密用户数据的签名验证和加解密
encryptedData
string
包括敏感数据在内的完整用户信息的加密数据,详见 用户数据的签名验证和加解密
encryptedDatastring包括敏感数据在内的完整用户信息的加密数据,详见 用户数据的签名验证和加解密用户数据的签名验证和加解密
iv
string
加密算法的初始向量,详见 用户数据的签名验证和加解密
ivstring加密算法的初始向量,详见 用户数据的签名验证和加解密
用户数据的签名验证和加解密注意: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
}
})
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 # 获取用户授权信息的解密算法,官方下载
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": "",
},
},
}
# 配置数据库
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 = '...'
# 小程序开发者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
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": "缺少参数"})
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
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)
}
})
}
})
})
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)
}
})
}
})
})后端 djangowx/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)
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__"
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":"缺少参数"})
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":"缺少参数"})