微信公眾號開發之微信網頁授權獲取使用者個人資訊
說明:該篇部落格是博主一字一碼編寫的,實屬不易,請尊重原創,謝謝大家!
一丶概述
-
微信網頁授權
如果使用者在微信客戶端中訪問第三方網頁,公眾號可以通過微信網頁授權機制,來獲取使用者基本資訊,進而實現業務邏輯。
現在,我們要實現一個微信內網頁,通過微信訪問網頁時,網頁會展示微信使用者的個人資訊。因為涉及到使用者的個人資訊,所以需要有使用者授權才可以。當用戶授權後,我們的網頁伺服器(開發者伺服器)會拿到使用者的“授權書”(code),我們用這個code向微信伺服器領取訪問令牌(accecc_token)和使用者的身份號碼(openid),然後憑藉access_token和openid向微信伺服器提取使用者的個人資訊。
- 第一步:使用者同意授權,獲取code
- 第二步:通過code換取網頁授權access_token
- 第三步:拉取使用者資訊(需scope為 snsapi_userinfo)
那麼,如何拿到使用者的授權code呢?
授權是由微信發起讓使用者進行確認,在這個過程中是微信在與使用者進行互動,所以使用者應該先訪問微信的內容,使用者確認後再由微信將使用者導向到我們的網頁連結地址,並攜帶上code引數。我們把這個過程叫做網頁回撥,類似於我們在程式編寫時用到的回撥函式,都是回撥的思想。
- 關於網頁授權回撥域名的說明
1、在微信公眾號請求使用者網頁授權之前,開發者需要先到公眾平臺官網中的“開發 - 介面許可權 - 網頁服務 - 網頁帳號 - 網頁授權獲取使用者基本資訊”的配置選項中,修改授權回撥域名。請注意,這裡填寫的是域名(是一個字串),而不是URL,因此請勿加 http:// 等協議頭;關於網頁授權回撥域名的說明
2、授權回撥域名配置規範為全域名,比如需要網頁授權的域名為:www.qq.com,配置以後此域名下面的頁面http://www.qq.com/music.html 、 http://www.qq.com/login.html 都可以進行OAuth2.0鑑權。但http://pay.qq.com 、 http://music.qq.com 、 http://qq.com無法進行OAuth2.0鑑權
3、如果公眾號登入授權給了第三方開發者來進行管理,則不必做任何設定,由第三方代替公眾號實現網頁授權即可。
第一步:使用者同意授權,獲取code
在確保微信公眾賬號擁有授權作用域(scope引數)的許可權的前提下(服務號獲得高階介面後,預設擁有scope引數中的snsapi_base和snsapi_userinfo),引導關注者開啟如下頁面:
尤其注意:由於授權操作安全等級較高,所以在發起授權請求時,微信會對授權連結做正則強匹配校驗,如果連結的引數順序不對,授權頁面將無法正常訪問
參考連結(請在微信客戶端中開啟此連結體驗):
scope為snsapi_base
https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx520c15f417810387&redirect_uri=https%3A%2F%2Fchong.qq.com%2Fphp%2Findex.php%3Fd%3D%26c%3DwxAdapter%26m%3DmobileDeal%26showwxpaytitle%3D1%26vb2ctag%3D4_2030_5_1194_60&response_type=code&scope=snsapi_base&state=123#wechat_redirect
scope為snsapi_userinfo
https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxf0e81c3bee622d60&redirect_uri=http%3A%2F%2Fnba.bluewebgame.com%2Foauth_response.php&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirec
尤其注意:跳轉回調redirect_uri,應當使用https連結來確保授權code的安全性。
引數說明
引數 | 是否必須 | 說明 |
---|---|---|
appid | 是 | 公眾號的唯一標識 |
redirect_uri | 是 | 授權後重定向的回撥連結地址, 請使用 urlEncode 對連結進行處理 |
response_type | 是 | 返回型別,請填寫code |
scope | 是 | 應用授權作用域,snsapi_base (不彈出授權頁面,直接跳轉,只能獲取使用者openid),snsapi_userinfo (彈出授權頁面,可通過openid拿到暱稱、性別、所在地。並且, 即使在未關注的情況下,只要使用者授權,也能獲取其資訊 ) |
state | 否 | 重定向後會帶上state引數,開發者可以填寫a-zA-Z0-9的引數值,最多128位元組 |
#wechat_redirect | 是 | 無論直接開啟還是做頁面302重定向時候,必須帶此引數 |
下圖為scope等於snsapi_userinfo時的授權頁面:
使用者同意授權後
如果使用者同意授權,頁面將跳轉至 redirect_uri/?code=CODE&state=STATE。
code說明 : code作為換取access_token的票據,每次使用者授權帶上的code將不一樣,code只能使用一次,5分鐘未被使用自動過期。
第二步:通過code換取網頁授權access_token
首先請注意,這裡通過code換取的是一個特殊的網頁授權access_token,與基礎支援中的access_token(該access_token用於呼叫其他介面)不同。公眾號可通過下述介面來獲取網頁授權access_token。如果網頁授權的作用域為snsapi_base,則本步驟中獲取到網頁授權access_token的同時,也獲取到了openid,snsapi_base式的網頁授權流程即到此為止。
尤其注意:由於公眾號的secret和獲取到的access_token安全級別都非常高,必須只儲存在伺服器,不允許傳給客戶端。後續重新整理access_token、通過access_token獲取使用者資訊等步驟,也必須從伺服器發起。
請求方法
獲取code後,請求以下連結獲取access_token: https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
引數說明
引數 | 是否必須 | 說明 |
---|---|---|
appid | 是 | 公眾號的唯一標識 |
secret | 是 | 公眾號的appsecret |
code | 是 | 填寫第一步獲取的code引數 |
grant_type | 是 | 填寫為authorization_code |
返回說明
正確時返回的JSON資料包如下:
{ "access_token":"ACCESS_TOKEN",
"expires_in":7200,
"refresh_token":"REFRESH_TOKEN",
"openid":"OPENID",
"scope":"SCOPE" }
引數 | 描述 |
---|---|
access_token | 網頁授權介面呼叫憑證,注意:此access_token與基礎支援的access_token不同 |
expires_in | access_token介面呼叫憑證超時時間,單位(秒) |
refresh_token | 使用者重新整理access_token |
openid | 使用者唯一標識,請注意,在未關注公眾號時,使用者訪問公眾號的網頁,也會產生一個使用者和公眾號唯一的OpenID |
scope | 使用者授權的作用域,使用逗號(,)分隔 |
錯誤時微信會返回JSON資料包如下(示例為Code無效錯誤):
{"errcode":40029,"errmsg":"invalid code"}
第三步:拉取使用者資訊(需scope為 snsapi_userinfo)
如果網頁授權作用域為snsapi_userinfo,則此時開發者可以通過access_token和openid拉取使用者資訊了。
請求方法
http:GET(請使用https協議) https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
引數說明
引數 | 描述 |
---|---|
access_token | 網頁授權介面呼叫憑證,注意:此access_token與基礎支援的access_token不同 |
openid | 使用者的唯一標識 |
lang | 返回國家地區語言版本,zh_CN 簡體,zh_TW 繁體,en 英語 |
返回說明
正確時返回的JSON資料包如下:
{ "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"
}
引數 | 描述 |
---|---|
openid | 使用者的唯一標識 |
nickname | 使用者暱稱 |
sex | 使用者的性別,值為1時是男性,值為2時是女性,值為0時是未知 |
province | 使用者個人資料填寫的省份 |
city | 普通使用者個人資料填寫的城市 |
country | 國家,如中國為CN |
headimgurl | 使用者頭像,最後一個數值代表正方形頭像大小(有0、46、64、96、132數值可選,0代表640*640正方形頭像),使用者沒有頭像時該項為空。若使用者更換頭像,原有頭像URL將失效。 |
privilege | 使用者特權資訊,json 陣列,如微信沃卡使用者為(chinaunicom) |
unionid | 只有在使用者將公眾號繫結到微信開放平臺帳號後,才會出現該欄位。 |
錯誤時微信會返回JSON資料包如下(示例為openid無效):
{"errcode":40003,"errmsg":" invalid openid "}
二丶程式碼實現
- 思路分析
- 首選在我們的flask程式中需要定義一個檢視函式路由規則為/wechat8007/index,定義微信伺服器重定向網址redirect_uri為伺服器域名+/wechat8007/index(例如http://www.xxxx.com/wechat8007/index),通過訪問微信提供的引導頁面,讓使用者同意授權,然後重定向到我們定義的網址,此時微信伺服器就會給我們的服務一個code,我們的伺服器再通過code向微信伺服器換取網頁授權access_token(存取令牌),如果網頁授權作用域為snsapi_userinfo,則此時可以通過access_token和openid拉取使用者資訊了。
- step1 同意授權,這一塊不需要程式碼實現,只需要提供授權連結即可
- step2 定義檢視函式,當用戶同意授權,頁面將跳轉至 redirect_uri/?code=CODE&state=STATE ,在flask程式中定義一個是檢視函式介面index,讓使用者同意授權後,去訪問的檢視
- 上一篇部落格定義的wechat檢視,是由微信伺服器訪問,現在定義的index檢視為使用者訪問的
@app.route("/wechat8007/index")
def index():
"""讓使用者通過微信訪問的網頁頁面檢視"""
- step3 剛開始還沒拉去使用者資料時,可直接返回一個模板
return render_template("index.html")
- step4 從微信伺服器中獲取使用者的資料資料,將使用者的資料資料填充到index.html模板中
- 1.獲取code引數
code = request.args.get("code")
- 2.當code不存在時,返回字串
if not code:
return u"缺失code引數"
- 3.向微信伺服器傳送http請求,獲取access_token,在獲取之前要在程式碼定義全域性變數WECHAT_APPID以及WECHAT_APPSECRET的值,用來填充https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code 微信提供獲取access_token連結地址中的APPID以及SECRET和CODE的值,再通過python中的urllib2庫向這個url傳送請求,read方法讀取文字內容獲取json格式的字串,然後使用json當中的loads方法將json格式的字串轉換為字典
url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code" %(WECHAT_APPID,WECHAT_APPSECRET,code)
response = urllib2.urlopen(url)
# 獲取響應體資料,微信返回的json資料
json_str = response.read()
resp_dict = json.loads(json_str)
- 4.提取access_token,首先對獲取到的響應體資料進行判斷,如果不存在,直接返回提示字串,存在則通過get方式拿去字典中的access_token鍵的值以及使用者編號openid的值
if "errcode" in resp_dict:
return u"獲取access_token失敗"
access_token = resp_dict.get("access_token")
open_id = resp_dict.get("openid") # 使用者的編號
- step5 向微信伺服器傳送http請求,獲取使用者的資料資料
url = "https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s&lang=zh_CN" %(access_token,open_id)
response = urllib2.urlopen(url)
# 讀取微信傳回的json的響應體資料
user_json_str = response.read()
user_dict_data = json.loads(user_json_str)
- step6 判斷微信返回的響應體資料中是否有errorcode欄位,如果存在則返回失敗資訊,不存在說明微信返回的json資料為正確資料,則將該資料傳給index.html模板,當用戶訪問 http://xxx/wechat8007/index地址時,會渲染出我們定義的index.html模板
if "errcode" in user_dict_data:
return u"獲取使用者資訊失敗"
else:
# 將使用者的資料資料填充到頁面中
return render_template("index.html", user=user_dict_data)
- step7 當前目錄下建立templates模板目錄,在該目錄中建立index.html檔案 ,程式碼如下
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>{{user["nickname"]}}的個人主頁</title>
</head>
<body>
<img alt="頭像" src="{{user['headimgurl']}}" width="60">
<table>
<tr>
<th>openid</th>
<td>{{user["openid"]}}</td>
</tr>
<tr>
<th>暱稱</th>
<td>{{user["nickname"]}}</td>
</tr>
<tr>
<th>性別</th>
<td>
{% if 1 == user["sex"] %}
男
{% elif 2 == user["sex"] %}
女
{% else %}
未知
{% endif %}
</td>
</tr>
<tr>
<th>省份</th>
<td>{{user["province"]}}</td>
</tr>
<tr>
<th>城市</th>
<td>{{user["city"]}}</td>
</tr>
<tr>
<th>國家</th>
<td>{{user["country"]}}</td>
</tr>
</table>
</body>
</html>
三丶部署測試
- step1 將程式碼推送到伺服器上
成功推送到伺服器上
- step2 在伺服器上進入虛擬環境,執行此程式
- step3 拼接微信提供網址 https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
- 注:正式公眾號redirect_uri地址必須為伺服器的域名地址,不能是IP地址
- 在拼接redirect_uri時,伺服器的域名為http://www.xxx.com/wechat8007/index,微信提供的授權地址http://中不可能包含一個redirect_uri為http://www.xxx.com/wechat8007/index使用者同意授權後跳轉的地址,所以需要對重定向地址http://www.xxx.com/wechat8007/index 進行轉義
- 使用python urllib庫中的quote方法進行轉義
In [1]: import urllib
In [2]: urllib.quote("http://www.xxx.com/wechat8007/index")
Out[2]: 'http%3A//www.xxx.com/wechat8007/index'
- 拼接好的使用者訪問的url地址為
- step4 可以將該網址生成二維碼,使用微信掃一掃,也可以在介面公眾號直接傳送此連結地址
- 使用谷歌瀏覽器的二維碼外掛,將網址生成對應的二維碼(這裡以百度首頁網址為例)
- 直接在瀏覽器中輸入此地址會提示請在微信客戶端開啟連結
- step5 測試,在手機微信上開啟此連結,出現授權登入提示,點選允許即可獲取使用者個人資訊
點選允許後,進入如下介面
點選繼續訪問,則出現博主個人的微信資訊了,如下圖
此時檢視伺服器上程式執行日誌
四丶完整程式碼
# coding:utf-8
from flask import Flask, request, render_template
import json, urllib2
WECHAT_APPID = "yourappid"
WECHAT_APPSECRET = "yoursecret"
app = Flask(__name__)
@app.route("/wechat8007/index")
def index():
code = request.args.get("code")
if not code:
return u"缺失code引數"
url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code" % (WECHAT_APPID, WECHAT_APPSECRET, code)
response = urllib2.urlopen(url)
json_str = response.read()
resp_dict = json.loads(json_str)
if "errcode" in resp_dict:
return u"獲取access_token失敗"
access_token = resp_dict.get("access_token")
open_id = resp_dict.get("openid")
url = "https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s&lang=zh_CN" % (access_token, open_id)
response = urllib2.urlopen(url)
user_json_str = response.read()
user_dict_data = json.loads(user_json_str)
if "errcode" in user_dict_data:
return u"獲取使用者資訊失敗"
else:
return render_template("index.html", user=user_dict_data)
if __name__ == '__main__':
app.run(port=8007, debug=True)