研究微信的一系列開(kāi)發(fā)已經(jīng)一段時(shí)間了,將自己的開(kāi)發(fā)過(guò)程記錄了下來(lái),這次先介紹的是如何在微信的內(nèi)置瀏覽器上通過(guò)授權(quán)獲取用戶(hù)信息。具體實(shí)現(xiàn)的是,用戶(hù)關(guān)注了公眾號(hào),點(diǎn)擊公眾號(hào)下方的菜單在微信中進(jìn)入公眾號(hào)的網(wǎng)站(比如你公司的網(wǎng)站首頁(yè)),在進(jìn)入的時(shí)候通過(guò)授權(quán)獲取用戶(hù)信息。
要實(shí)現(xiàn)上述功能必須得有一個(gè)具有網(wǎng)頁(yè)授權(quán)功能的微信公眾號(hào)或者服務(wù)號(hào),進(jìn)入 微信公眾平臺(tái),在開(kāi)發(fā)–>接口權(quán)限看到如下就說(shuō)明有授權(quán)功能,沒(méi)有的話(huà)得去申請(qǐng):
實(shí)現(xiàn)過(guò)程
服務(wù)器
先購(gòu)買(mǎi)服務(wù)器,然后設(shè)置服務(wù)器的域名,比如設(shè)置域名為:www.show.netcome.net
。
配置公眾號(hào)收集信息
首先得有一個(gè)有網(wǎng)頁(yè)授權(quán)功能的公眾號(hào),然后在開(kāi)發(fā)–>基本配置–>公眾號(hào)開(kāi)發(fā)信息里找到公眾號(hào)的開(kāi)發(fā)者ID(AppID)和開(kāi)發(fā)者密碼(AppSecret)
并記錄下來(lái),其次在設(shè)置–>公眾號(hào)設(shè)置–>功能設(shè)置–>網(wǎng)頁(yè)授權(quán)域名下填寫(xiě)上面的域名(注意不要加http://等協(xié)議頭,下面寫(xiě)得很清楚)并將下圖中藍(lán)色的txt文件下載下來(lái),后面需要用到:
接口流程
要想獲取用戶(hù)信息就得要用戶(hù)同意,這個(gè)過(guò)程分為主要的三步,第一先請(qǐng)求 code:code作為換取access_token的票據(jù),每次用戶(hù)授權(quán)帶上的code將不一樣,code只能使用一次,5分鐘未被使用自動(dòng)過(guò)期。第二步通過(guò)剛請(qǐng)求來(lái)的 code去請(qǐng)求access_token和openid,第三部就是通過(guò)剛獲取的access_token和openid來(lái)請(qǐng)求用戶(hù)信息。
python代碼如下(如果你想知道細(xì)節(jié),可以去查看官方文檔):
# -*- coding: utf-8 -*-# ----------------------------------------------# @Time : 18-3-21 下午1:36# @Author : YYJ# @File : WechatAPI.py# @CopyRight: ZDWL# ----------------------------------------------import hashlibimport randomimport timefrom urllib import parsefrom xml.etree.ElementTree import fromstringimport requestsfrom src.beauty.main.wechat.config import wechatConfigclass WechatAPI(object): def __init__(self): self.config = wechatConfig self._access_token = None self._openid = None @staticmethod def process_response_login(rsp): """解析微信登錄返回的json數(shù)據(jù),返回相對(duì)應(yīng)的dict, 錯(cuò)誤信息""" if 200 != rsp.status_code: return None, {'code': rsp.status_code, 'msg': 'http error'} try: content = rsp.json() except Exception as e: return None, {'code': 9999, 'msg': e} if 'errcode' in content and content['errcode'] != 0: return None, {'code': content['errcode'], 'msg': content['errmsg']} return content, None @staticmethod def create_time_stamp(): """產(chǎn)生時(shí)間戳""" now = time.time() return int(now) @staticmethod def create_nonce_str(length=32): """產(chǎn)生隨機(jī)字符串,不長(zhǎng)于32位""" chars = "abcdefghijklmnopqrstuvwxyz0123456789" strs = [] for x in range(length): strs.append(chars[random.randrange(0, len(chars))]) return "".join(strs) @staticmethod def xml_to_array(xml): """將xml轉(zhuǎn)為array""" array_data = {} root = fromstring(xml) for child in root: value = child.text array_data[child.tag] = value return array_data def array_to_xml(self, dic, sign_name=None): """array轉(zhuǎn)xml""" if sign_name is not None: dic[sign_name] = self.get_sign() xml = ["<xml>"] for k in dic.keys(): xml.append("<{0}>{1}</{0}>".format(k, dic[k])) xml.append("</xml>") return "".join(xml)class WechatLogin(WechatAPI): def get_code_url(self): """微信內(nèi)置瀏覽器獲取網(wǎng)頁(yè)授權(quán)code的url""" url = self.config.defaults.get('wechat_browser_code') + ( '?appid=%s&redirect_uri=%s&response_type=code&scope=%s&state=%s#wechat_redirect' % (self.config.APPID, parse.quote(self.config.REDIRECT_URI), self.config.SCOPE, self.config.STATE if self.config.STATE else '')) return url def get_code_url_pc(self): """pc瀏覽器獲取網(wǎng)頁(yè)授權(quán)code的url""" url = self.config.defaults.get('pc_QR_code') + ( '?appid=%s&redirect_uri=%s&response_type=code&scope=%s&state=%s#wechat_redirect' % (self.config.APPID, parse.quote(self.config.REDIRECT_URI), self.config.PC_LOGIN_SCOPE, self.config.STATE if self.config.STATE else '')) return url def get_access_token(self, code): """獲取access_token""" params = { 'appid': self.config.APPID, 'secret': self.config.APPSECRET, 'code': code, 'grant_type': 'authorization_code' } token, err = self.process_response_login(requests .get(self.config.defaults.get('wechat_browser_access_token'), params=params)) if not err: self._access_token = token['access_token'] self._openid = token['openid'] return self._access_token, self._openid def get_user_info(self, access_token, openid): """獲取用戶(hù)信息""" params = { 'access_token': access_token, 'openid': openid, 'lang': self.config.LANG } return self.process_response_login(requests .get(self.config.defaults.get('wechat_browser_user_info'), params=params))
上面導(dǎo)入了一個(gè)參數(shù)文件,內(nèi)容填寫(xiě)如下:
# -*- coding: utf-8 -*-# ----------------------------------------------# @Time : 18-3-21 上午11:50# @Author : YYJ# @File : wechatConfig.py# @CopyRight: ZDWL# ----------------------------------------------"""微信公眾號(hào)和商戶(hù)平臺(tái)信息配置文件"""# ----------------------------------------------微信公眾號(hào)---------------------------------------------- ## 公眾號(hào)appidAPPID = 'appid'# 公眾號(hào)AppSecretAPPSECRET = 'appsecret'# ----------------------------------------------微信商戶(hù)平臺(tái)---------------------------------------------- ## 商戶(hù)idMCH_ID = 'mch_id'API_KEY = 'api秘鑰'# ----------------------------------------------回調(diào)頁(yè)面---------------------------------------------- ## 用戶(hù)授權(quán)獲取code后的回調(diào)頁(yè)面,如果需要實(shí)現(xiàn)驗(yàn)證登錄就必須填寫(xiě)REDIRECT_URI = 'http://www.show.netcome.net/index'PC_LOGIN_REDIRECT_URI = 'http://www.show.netcome.net/index'defaults = { # 微信內(nèi)置瀏覽器獲取code微信接口 'wechat_browser_code': 'https://open.weixin.qq.com/connect/oauth2/authorize', # 微信內(nèi)置瀏覽器獲取access_token微信接口 'wechat_browser_access_token': 'https://api.weixin.qq.com/sns/oauth2/access_token', # 微信內(nèi)置瀏覽器獲取用戶(hù)信息微信接口 'wechat_browser_user_info': 'https://api.weixin.qq.com/sns/userinfo', # pc獲取登錄二維碼接口 'pc_QR_code': 'https://open.weixin.qq.com/connect/qrconnect', # pc獲取登錄二維碼接口 # 'pc_QR_code': 'https://api.weixin.qq.com/sns/userinfo',}SCOPE = 'snsapi_userinfo'PC_LOGIN_SCOPE = 'snsapi_login'STATE = ''LANG = 'zh_CN'
以上信息中你必須填上APPID、APPSECRET、REDIRECT_URI,其中SCOPE可以為snsapi_userinfo或者snsapi_base。
然后在你Django的views.py中寫(xiě)下面三個(gè)view:
from django.views.generic import Viewfrom loginfrom django.http import HttpResponseRedirectfrom django.http import HttpResponse, HttpResponseServerErrorfrom django.shortcuts import render, redirectfrom src.beauty.main.wechat.utils.WechatAPI import WechatLoginclass WechatViewSet(View): wechat_api = WechatLogin()class AuthView(WechatViewSet): def get(self, request): url = self.wechat_api.get_code_url() return redirect(url)class GetInfoView(WechatViewSet): def get(self, request): if 'code' in request.GET: code = request.GET['code'] token, openid = self.wechat_api.get_access_token(code) if token is None or openid is None: return HttpResponseServerError('get code error') user_info, error = self.wechat_api.get_user_info(token, openid) if error: return HttpResponseServerError('get access_token error') user_data = { 'nickname': user_info['nickname'], 'sex': user_info['sex'], 'province': user_info['province'].encode('iso8859-1').decode('utf-8'), 'city': user_info['city'].encode('iso8859-1').decode('utf-8'), 'country': user_info['country'].encode('iso8859-1').decode('utf-8'), 'avatar': user_info['headimgurl'], 'openid': user_info['openid'] } user = BeautyUsers.objects.filter(is_effective=True).filter(wechat=user_data['openid']) if user.count() == 0: user = BeautyUsers.objects.create(username=user_data['nickname'], wechat_avatar=user_data['avatar'], wechat=user_data['openid'], password='') login(request, user) else: login(request, user.first()) # 授權(quán)登錄成功,進(jìn)入主頁(yè) return home(request)
還有一個(gè)urls.py文件做如下寫(xiě)法:
from django.conf.urls import urlfrom src.beauty.main.wechat.apps.index import viewsfrom src.beauty.main.wechat.apps.index.views import AuthView, GetInfoViewurlpatterns = [ url(r'^$', views.home), url(r'^auth/$', AuthView.as_view()), url(r'^index$', GetInfoView.as_view()),]
解釋一下,第一個(gè)url路由是你驗(yàn)證過(guò)后要進(jìn)入的網(wǎng)頁(yè),第二個(gè)為驗(yàn)證授權(quán)的請(qǐng)求頁(yè)面,用戶(hù)在公眾號(hào)中點(diǎn)擊菜單就會(huì)請(qǐng)求第二個(gè)http://www.show.netcome.net/auth/路由,在這個(gè)路由里(AuthView),我們重定向了,去請(qǐng)求了微信的接口,為了獲取當(dāng)前點(diǎn)擊用戶(hù)的code值,請(qǐng)求成功后微信會(huì)轉(zhuǎn)向回調(diào)頁(yè)面(在參數(shù)文件中配置的http://www.show.netcome.net/index)也就是來(lái)請(qǐng)求上面我們的第三個(gè)路由。注意請(qǐng)求code成功后微信會(huì)以get的方式回調(diào)我們的這個(gè)路由,并且攜帶code和state的值,格式如:http://www.show.netcome.net/index/?code=CODE&state=STATE
,我們就可以在GetInfoView這個(gè)view中獲取到code值,然后去請(qǐng)求access_token和openid(self.wechat_api.get_access_token(code)函數(shù),里面有requests.get(url)的http請(qǐng)求,也就是在客戶(hù)端請(qǐng)求中嵌入了一個(gè)服務(wù)端請(qǐng)求,這里是服務(wù)端發(fā)起一個(gè)http去請(qǐng)求微信服務(wù)端的這個(gè)獲取access_token的接口,服務(wù)端請(qǐng)求到access_token的話(huà)就再發(fā)起請(qǐng)求去獲取用戶(hù)信息),通過(guò)請(qǐng)求到的access_token和openid去請(qǐng)求用戶(hù)信息(self.wechat_api.get_user_info(token, openid)函數(shù),里面也包含了一個(gè)內(nèi)嵌的服務(wù)器端http請(qǐng)求)。
在公眾號(hào)的功能–>自定義菜單中修改如下:
然后將你的項(xiàng)目,通過(guò)nginx+uwsgi部署到服務(wù)器上就可以了(配置方法見(jiàn)我另一篇博客),獲取到的信息在views.py的GetInfoView中,其中獲取到的nickname不要使用.encode(‘iso8859-1’).decode(‘utf-8’)編碼為utf-8保存到數(shù)據(jù)庫(kù),因?yàn)槲⑿抨欠Q(chēng)中可以包含表情,表情由4個(gè)字節(jié)組成,utf-8為三字節(jié),所以一utf-8編碼保存在數(shù)據(jù)庫(kù)會(huì)出錯(cuò),這里不用編碼直接保存,在拿出來(lái)使用的時(shí)候再對(duì)其進(jìn)行utf-8編碼就可以正常顯示了連表情也可以顯示在微信自帶瀏覽器的網(wǎng)頁(yè)上哦。
有問(wèn)題歡迎留言交流。
另外,哪位大佬有空閑的公眾號(hào)(有開(kāi)發(fā)權(quán)限的)來(lái)一起開(kāi)發(fā)網(wǎng)站的,私,不勝感激。。
聯(lián)系客服