官方文檔html
流程稍微有點複雜,可是這個比較重要,經過受權得到用戶 openid 才能進行給用戶主動推送消息等功能。python
特別注意:這個必需要微信公衆號(我的的訂閱號不能用這個功能)進行了微信認證才能使用。還有回調域名須要是 https 的,如今有地方能夠申請到免費 https 證書,能夠參考個人這篇文章 百度智能雲免費 SSL 證書的申請與部署(nginx HTTPS 配置,http 請求重定向)nginx
由於咱們讓用戶受權的目的就是獲得 openid,得到 openid 後要存到數據庫中,並和用戶創建對應關係。web
我用的是 django 自帶的用戶系統,官方推薦創建一個自定義的 UserProfile 之類的 Model 保存用戶相關信息。數據庫
from django.db import models from django.contrib.auth.models import User class UserProfile(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE, null=True, default=None) openid = models.CharField("微信 openid", max_length=32)
openid 的長度貌似是 28 位,這裏定義的最大長度 32 應該夠用。django
參考json
這個頁面是微信的頁面,須要咱們加上參數,讓用戶跳轉過去,url 是api
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
appid 去公衆號平臺 - > 開發 -> 基本配置 裏找。服務器
redirect_uri 是成功之後的回調地址,是你本身的一個 url,要經過 urlEncode 進行編碼微信
response_type 寫 code
scope 可選填 snsapi_base 或 snsapi_userinfo
state 非必填,因此直接不加這個參數了
關於 python 的 urlEncode 可使用:
import urllib.parse s='https://xxx.com/api/weixin_bind_callback/' urllib.parse.quote(s)
結果是:'https%3A//xxx.com/api/weixin_bind_callback/'
它沒有處理斜槓,添加一個 safe 參數就能夠處理斜槓了
import urllib.parse s='https://xxx.com/api/wexin_bind_callback/' urllib.parse.quote(s, safe='')
結果是:'https%3A%2F%2Fxxx.com%2Fapi%2Fwexin_bind_callback%2F'
個人這個 url 的最終結果是:
https://open.weixin.qq.com/connect/oauth2/authorize?appid=XXXX&redirect_uri=https%3A%2F%2FXXXX.com%2Fweixin%2Fbind%2Fcallback%2F&response_type=code&scope=snsapi_userinfo#wechat_redirect
下面的邏輯要寫在上面給用戶訪問的 url 裏的回調的頁面裏。
請求 url 是:
https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
APPID 和 secret 去公衆號平臺 - > 開發 -> 基本配置 裏找。
code 是返回的 code
grant_type 就寫 authorization_code
這裏須要使用 http 客戶端庫,例如 python3 的 requests 或者 python2 的 urllib2,請求微信的接口
python3 的例子:
import requests APPID = '' secret = '' r = requests.get('https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code')
請求結果是:
{ "access_token":"ACCESS_TOKEN", "expires_in":7200, "refresh_token":"REFRESH_TOKEN", "openid":"OPENID", "scope":"SCOPE" }
這時候須要再發起一次請求才能獲得用戶的信息。
此次的請求 url:
https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
這個返回的結果是:
{ "openid":" OPENID", "nickname": NICKNAME, "sex":"1", "province":"PROVINCE", "city":"CITY", "country":"COUNTRY", "headimgurl": "http://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/46", "privilege":[ "PRIVILEGE1" "PRIVILEGE2" ], "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL" }
最終的 view 是:
def weixinbind_callback(request): code = request.GET.get('code') state = request.GET.get('state') # print(code, state) APPID = 'xxxxxx' secret = 'xxxxx' r = requests.get( 'https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code' % (APPID, secret, code) ) print(r.text) d = json.loads(r.text) # { # "access_token":"ACCESS_TOKEN", # "expires_in":7200, # "refresh_token":"REFRESH_TOKEN", # "openid":"OPENID", # "scope":"SCOPE" # } r = request.get( 'https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s&lang=zh_CN' % (d['access_token'], d['openid']) ) # print(r.text) dd = json.load(r.text) # { # "openid":" OPENID", # "nickname": NICKNAME, # "sex":"1", # "province":"PROVINCE", # "city":"CITY", # "country":"COUNTRY", # "headimgurl": "http://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/46", # "privilege":[ "PRIVILEGE1" "PRIVILEGE2" ], # "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL" # } request.user.userprofile.openid = dd['openid'] request.user.userprofile.nickname = dd['nickname'] request.user.userprofile.wsex = dd['sex'] request.user.userprofile.province = dd['province'] request.user.userprofile.city = dd['city'] request.user.userprofile.country = dd['country'] request.user.userprofile.headimgurl = dd['headimgurl'] request.user.userprofile.privilege = dd['privilege'] request.user.userprofile.unionid = dd['unionid'] request.user.userprofile.save() return HttpResponseRedirect('/weixin/')
到公衆號平臺 設置-> 公衆號設置 -> 功能設置 -> 網頁受權域名 的配置選項中,把剛剛的回調的 url 的域名添加上,有一個認證文件。
他說要把文件傳到服務器上,可是這個對於 django 不太方便,能夠直接寫一個 view 返回這個文件內容就能夠了,我直接放到 urls 文件裏了。
from django.urls import path from django.http import HttpResponse def weixin_verify(request): return HttpResponse('PJRLUusp1NXyuD70') urlpatterns = [ # 微信開發 path('weixin/', account.views.weixin_page), path('weixin/bind/', account.views.weixinbind), path('weixin/bind/callback/', account.views.weixinbind_callback), path('MP_verify_PJRLUusp1NXyuD70.txt', weixin_verify), ]
from django.urls import path from django.http import HttpResponse def weixin_verify(request): return HttpResponse('PJRLUusp1NXyuD70') urlpatterns = [ # 微信開發 path('weixin/', account.views.weixin_page), path('weixin/bind/', account.views.weixinbind), path('weixin/bind/callback/', account.views.weixinbind_callback), path('MP_verify_PJRLUusp1NXyuD70.txt', weixin_verify), ]
import requests from django.shortcuts import render from django.http import HttpResponse, HttpResponseRedirect import time import json def weixin_page(request): return render(request, 'weixin_index.html') def weixinbind(request): return HttpResponseRedirect('https://open.weixin.qq.com/connect/oauth2/authorize?appid=XXXXX&redirect_uri=https%3A%2F%2FXXXXX.com%2Fweixin%2Fbind%2Fcallback%2F&response_type=code&scope=snsapi_userinfo#wechat_redirect') def weixinbind_callback(request): code = request.GET.get('code') state = request.GET.get('state') print(code, state) APPID = 'xxxxx' secret = 'xxxxx' r = requests.get( 'https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code' % (APPID, secret, code) ) print(r.text) d = json.loads(r.text) # { # "access_token":"ACCESS_TOKEN", # "expires_in":7200, # "refresh_token":"REFRESH_TOKEN", # "openid":"OPENID", # "scope":"SCOPE" # } r = requests.get( 'https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s&lang=zh_CN' % (d['access_token'], d['openid']) ) print(r.content) dd = json.loads(r.content.decode('utf8')) # { # "openid":" OPENID", # "nickname": NICKNAME, # "sex":"1", # "province":"PROVINCE", # "city":"CITY", # "country":"COUNTRY", # "headimgurl": "http://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/46", # "privilege":[ "PRIVILEGE1" "PRIVILEGE2" ], # "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL" # } request.user.userprofile.openid = dd['openid'] request.user.userprofile.nickname = dd['nickname'] request.user.userprofile.wsex = dd['sex'] request.user.userprofile.province = dd['province'] request.user.userprofile.city = dd['city'] request.user.userprofile.country = dd['country'] request.user.userprofile.headimgurl = dd['headimgurl'] request.user.userprofile.privilege = json.dumps(dd['privilege']) #request.user.userprofile.unionid = dd['unionid'] request.user.userprofile.refresh_token_time = time.time() request.user.userprofile.save() return HttpResponseRedirect('/weixin/')
from django.db import models from django.contrib.auth.models import User class UserProfile(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE, null=True, default=None) # 微信開發 openid = models.CharField("微信 openid", max_length=32, default='') nickname = models.CharField("微信暱稱", max_length=256, default='') wsex = models.CharField("微信性別", max_length=3, default='') province = models.CharField("微信省份", max_length=50, default='') city = models.CharField("微信城市", max_length=50, default='') country = models.CharField("微信國家", max_length=50, default='') headimgurl = models.CharField("微信頭像", max_length=200, default='') privilege = models.CharField("微信權限", max_length=3, default='') unionid = models.CharField("微信 unionid", max_length=32, default='') refresh_token = models.CharField("微信 refresh_token", max_length=512, default='') refresh_token_time = models.IntegerField(default=0)
html 部分省略了
微信登陸失敗 redirect_uri域名與後臺配置不一致,錯誤碼10003
到公衆號平臺 設置-> 公衆號設置 -> 功能設置 -> 網頁受權域名 的配置選項中,修改受權回調域名。
見上文 添加回調域名。
歡迎來到個人博客:https://codeplot.top/
技術分類文章