登录与授权登录与授权登录与授权官方文档官方文档一.登录登录流程时序一.登录登录流程时序一.登录登录流程时序说明:调用调用

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":"缺少参数"})