官方文檔html
說明:mysql
以後開發者服務器能夠根據用戶標識來生成自定義登陸態,用於後續業務邏輯中先後端交互時識別用戶身份。redis
注意:算法
session_key
是對用戶數據進行 加密簽名 的密鑰。爲了應用自身的數據安全,開發者服務器不該該把會話密鑰下發到小程序,也不該該對外提供這個密鑰。總結:
小程序端執行wx.login後在回調函數中就能拿到上圖的code,而後把這個code傳給咱們後端程序,後端拿到這個這個code後,
能夠請求code2Session接口拿到用的openid和session_key,openid是用戶在微信中惟一標識,咱們就能夠把這個
兩個值(val)存起來,而後返回一個鍵(key)給小程序端,下次小程序請求咱們後端的時候,帶上這個key,
咱們就能找到這個val,就能夠,這樣就把登入作好了。
調用接口獲取登陸憑證(code)。經過憑證進而換取用戶登陸態信息,包括用戶的惟一標識(openid)及本次登陸的會話
密鑰(session_key)等。用戶數據的加解密通信須要依賴會話密鑰完成。
參數sql
屬性 | 類型 | 默認值 | 必填 | 說明 | 最低版本 |
---|---|---|---|---|---|
timeout | number | 否 | 超時時間,單位ms | 1.9.90 | |
success | function | 否 | 接口調用成功的回調函數 | ||
fail | function | 否 | 接口調用失敗的回調函數 | ||
complete | function | 否 | 接口調用結束的回調函數(調用成功、失敗都會執行) |
object.success 回調函數數據庫
參數django
屬性 | 類型 | 說明 |
---|---|---|
code | string | 用戶登陸憑證(有效期五分鐘)。開發者須要在開發者服務器後臺調用 code2Session,使用 code 換取 openid 和 session_key 等信息 |
本接口應在服務器端調用,詳細說明參見服務端API。json
登陸憑證校驗。經過 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次 |
獲取用戶信息。
參數
屬性 | 類型 | 默認值 | 必填 | 說明 |
---|---|---|---|---|
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
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 } })
wx ├── settings.py # 小程序id,code2Session等配置 ├── wx_login.py # 用於調用code2Session拿到openid等 └── WXBizDataCrypt.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": "", }, }, }
# 小程序開發者id AppId="..." # 小程序的AppSecret AppSecret="..." code2Session="https://api.weixin.qq.com/sns/jscode2session?appid={}&secret={}&js_code={}&grant_type=authorization_code" pay_mchid ='...' pay_apikey = '...'
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 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": "缺乏參數"})
<!--用戶信息受權--> <button open-type="getUserInfo" bindgetuserinfo="info">受權登陸</button>
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) } }) } }) })
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)
from rest_framework.serializers import ModelSerializer from app01 import models class User_ser(ModelSerializer): class Meta: model=models.Wxuser fields="__all__"
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":"缺乏參數"})