摘要:在授權(quán)頁面里面,登錄并確認(rèn)授權(quán)。從騰訊提供的按鈕下載放到你的登錄頁面即可。騰訊不給得到真實(shí)號(hào)可能是出于保護(hù)隱私的考慮。再回頭編輯,添加回調(diào)地址的處理方法逆向解析第三方登錄,回調(diào)函數(shù)登錄之后,會(huì)跳轉(zhuǎn)到這里。
準(zhǔn)備工作_OAuth2.0
接入QQ登錄前,網(wǎng)站需首先進(jìn)行申請(qǐng),獲得對(duì)應(yīng)的appid與appkey,以保證后續(xù)流程中可正確對(duì)網(wǎng)站與用戶進(jìn)行驗(yàn)證與授權(quán)。
在開發(fā)的過程中,發(fā)現(xiàn)獲取不到QQ號(hào),只能獲取一個(gè)OpenID的東西。最后采取存儲(chǔ)這個(gè)OpenID并綁定對(duì)應(yīng)賬號(hào)的方式。
所以需要?jiǎng)?chuàng)建對(duì)應(yīng)的模型,即創(chuàng)建一個(gè)應(yīng)用管理第三方登錄。
QQ登錄功能開發(fā)流程如下圖:
打開QQ互聯(lián),進(jìn)入管理中心。注冊(cè)一下應(yīng)用開發(fā)者,并添加網(wǎng)站應(yīng)用,獲得對(duì)應(yīng)的appid與appkey。
申請(qǐng)appid和appkey的用途
appid:應(yīng)用的唯一標(biāo)識(shí)。在OAuth2.0認(rèn)證過程中,appid的值即為oauth_consumer_key的值。
appkey:appid對(duì)應(yīng)的密鑰,訪問用戶資源時(shí)用來驗(yàn)證應(yīng)用的合法性。在OAuth2.0認(rèn)證過程中,appkey的值即為oauth_consumer_secret的值。
理解回調(diào)地址需要了解一下OAuth協(xié)議。
在你的網(wǎng)站頁面里面,打開授權(quán)頁面(這個(gè)授權(quán)頁面不是回調(diào)地址)。在授權(quán)頁面里面,登錄QQ并確認(rèn)授權(quán)。
授權(quán)之后,會(huì)得到一個(gè)授權(quán)碼?;卣{(diào)地址就是用于接收這個(gè)授權(quán)碼。
授權(quán)碼以GET的方式返回,例如?http://www.junxi.site/web/oau...
通過這種方式,可以獲取授權(quán)碼,所以需要提供一個(gè)地址。這個(gè)地址先寫一個(gè)暫時(shí)沒有的地址,后面開發(fā)的時(shí)候,再給這個(gè)地址寫對(duì)應(yīng)的響應(yīng)方法。
這個(gè)QQ按鈕是提供QQ登錄的入口。從騰訊提供的QQ按鈕下載放到你的登錄頁面即可。
不用看幫助文檔里面的什么前端代碼。這些用不上,只需要加一個(gè)固定的訪問鏈接,再重定向即可。
前端頁面此處的代碼如下:
qq_login鏈接在下面第3步創(chuàng)建web應(yīng)用里面設(shè)置。
第3步、創(chuàng)建web應(yīng)用怎么創(chuàng)建應(yīng)用就不細(xì)說了,這是基本功。這里我已經(jīng)創(chuàng)建了一個(gè)名稱為web的django app應(yīng)用。
創(chuàng)建完成之后,打開models.py文件,編寫模型:
#!/usr/bin/env python # _*_ coding:utf-8 _*_ __author__ = "junxi" import sys reload(sys) sys.setdefaultencoding("utf8") class OAuthQQ(models.Model): """QQ and User Bind""" user = models.ForeignKey(UserProfile) # 關(guān)聯(lián)用戶信息表 qq_openid = models.CharField(max_length=64) # QQ的關(guān)聯(lián)OpenID # def __str__(self): # return self.user
該模型用于存儲(chǔ)QQ登錄返回的OpenID值。這個(gè)OpenID值是用QQ號(hào)一一對(duì)應(yīng)。騰訊不給得到真實(shí)QQ號(hào)可能是出于保護(hù)隱私的考慮。
在總的urls路由中,加入這個(gè)應(yīng)用路由。(總路由在和工程名一樣的文件夾中的urls.py文件。這種方式對(duì)urls管理比較清晰)
from django.conf.urls import url, include from django.contrib import admin import web.urls import web.views urlpatterns = [ url(r"^admin/", admin.site.urls), url(r"^web/", include(web.urls)), ]
路由控制根據(jù)自己的工程自己寫即可。
打開web應(yīng)用目錄下urls.py文件,先寫一下需要哪些鏈接地址:
from django.conf.urls import url from .views import * urlpatterns = [ url(r"^oauth/qq/login/$", login, name="qq_login"), url(r"^oauth/qq/check/$", login, name="qq_check"), url(r"^oauth/bind/account/$", login, name="bind_account"), ]
qq_login和qq_check,分別是打開授權(quán)頁面和回調(diào)地址。
bind_account是綁定用戶的頁面。
大致思路是授權(quán)之后,得到OpenID。判斷這個(gè)OpenID是否存在數(shù)據(jù)庫中。若存在,則直接登錄對(duì)應(yīng)的用戶即可;若不存在,則打開這個(gè)綁定郵箱頁面,綁定對(duì)應(yīng)的用戶。
為了管理好OAuth,在web應(yīng)用的文件夾下創(chuàng)建oauth_client.py文件。把相關(guān)的OAuth操作方法集成在一起。編輯oauth_client.py文件:
#!/usr/bin/env python # _*_ coding:utf-8 _*_ __author__ = "junxi" import json import urllib, urllib2, urlparse class OAuthQQ: def __init__(self, client_id, client_key, redirect_uri): self.client_id = client_id self.client_key = client_key self.redirect_uri = redirect_uri def get_auth_url(self): """獲取授權(quán)頁面的網(wǎng)址""" params = {"client_id": self.client_id, "response_type": "code", "redirect_uri": self.redirect_uri, "scope": "get_user_info", "state": 1} url = "https://graph.qq.com/oauth2.0/authorize?%s" % urllib.urlencode(params) return url
創(chuàng)建一個(gè)類,需要申請(qǐng)QQ登錄的APP_ID、APP_KEY和回調(diào)地址。這些都是固定的,我把這幾個(gè)常量放入到settings.py中。settings.py添加如下常量,具體的值請(qǐng)?jiān)谀愕纳暾?qǐng)頁面查找(這里還需要提一下,本地調(diào)試的方法。因?yàn)槭跈?quán)之后是調(diào)整到部署之后的網(wǎng)站上,而部署的網(wǎng)站還沒開發(fā)響應(yīng)的代碼,無法響應(yīng)對(duì)應(yīng)的地址。這里我是本地測試環(huán)境,強(qiáng)制綁定Hosts域名文件解析):
# OAuth設(shè)置 QQ_APP_ID = "XXXXXX" QQ_KEY = "XXXXXX" QQ_RECALL_URL = "http://www.junxi.site/web/oauth/qq/check"
回到OAuthQQ類,現(xiàn)里面有個(gè)get_auth_url方法。該方法是獲取打開授權(quán)頁面的鏈接地址。(可參考官方幫助,寫得不夠清晰)
接著,在編輯web應(yīng)用的views.py文件,加入qq_login對(duì)應(yīng)的響應(yīng)方法:
from django.shortcuts import HttpResponseRedirect from django.conf import settings from oauth_client import OAuthQQ def qq_login(request): oauth_qq = OAuthQQ(settings.QQ_APP_ID, settings.QQ_KEY, settings.QQ_RECALL_URL) #獲取 得到Authorization Code的地址 url = oauth_qq.get_auth_url() #重定向到授權(quán)頁面 return HttpResponseRedirect(url)
到這里為止,就完成了點(diǎn)擊QQ登錄按鈕,跳轉(zhuǎn)到授權(quán)頁面。
登錄授權(quán)之后,授權(quán)頁面會(huì)自動(dòng)跳轉(zhuǎn)到我們?cè)O(shè)置的回調(diào)地址。例如 http://www.junxi.site/web/oau...
我們可以獲取這個(gè)地址上面的GET參數(shù)。先假設(shè)我們可以順利獲取到,繼續(xù)完善OAuthQQ類。拿到這個(gè)授權(quán)碼之后,需要用該碼獲取騰訊的access_token通行令牌。
打開oauth_client.py文件,在OAuthQQ類添加如下方法:
def get_access_token(self, code): """根據(jù)code獲取access_token""" params = {"grant_type": "authorization_code", "client_id": self.client_id, "client_secret": self.client_key, "code": code, "redirect_uri": self.redirect_uri} # 回調(diào)地址 url = "https://graph.qq.com/oauth2.0/token?%s" % urllib.urlencode(params) # 訪問該網(wǎng)址,獲取access_token response = urllib2.urlopen(url).read() result = urlparse.parse_qs(response, True) access_token = str(result["access_token"][0]) self.access_token = access_token return access_token
該方法使用了urllib2,在服務(wù)器后臺(tái)訪問對(duì)應(yīng)的鏈接,獲取access_token,并返回該值。因?yàn)槲液罄m(xù)不需要用access_token做其他動(dòng)作,直接一次性獲取QQ昵稱和OpenID。所以不用記錄這個(gè)通行令牌的有效期。
得到這個(gè)access_token之后,就可以做其他事了。首先需要獲取授權(quán)用戶的OpenID,因?yàn)轵v訊不允許獲取QQ號(hào)。只好退而求次,獲取并保存OpenID??蓞⒖脊俜轿臋n。
繼續(xù)給這個(gè)OAuthQQ添加獲取OpenID的方法和使用OpenID獲取QQ基本信息的方法:
def get_open_id(self): """獲取QQ的OpenID""" params = {"access_token": self.access_token} url = "https://graph.qq.com/oauth2.0/me?%s" % urllib.urlencode(params) response = urllib2.urlopen(url).read() v_str = str(response)[9:-3] # 去掉callback的字符 v_json = json.loads(v_str) openid = v_json["openid"] self.openid = openid return openid def get_qq_info(self): """獲取QQ用戶的資料信息""" params = {"access_token": self.access_token, "oauth_consumer_key": self.client_id, "openid": self.openid} url = "https://graph.qq.com/user/get_user_info?%s" % urllib.urlencode(params) response = urllib2.urlopen(url).read() return json.loads(response)
騰訊返回OpenID和QQ基本信息的內(nèi)容格式都不一樣。
再回頭編輯views.py,添加回調(diào)地址的處理方法:
from django.shortcuts import render, HttpResponseRedirect, HttpResponse, reverse # reverse url逆向解析 from django.http import JsonResponse from . import models from .form import * import json import time from django.conf import settings from oauth_client import OAuthQQ def qq_check(request): # 第三方QQ登錄,回調(diào)函數(shù) """登錄之后,會(huì)跳轉(zhuǎn)到這里。需要判斷code和state""" request_code = request.GET.get("code") oauth_qq = OAuthQQ(settings.QQ_APP_ID, settings.QQ_KEY, settings.QQ_RECALL_URL) # 獲取access_token access_token = oauth_qq.get_access_token(request_code) time.sleep(0.05) # 稍微休息一下,避免發(fā)送urlopen的10060錯(cuò)誤 open_id = oauth_qq.get_open_id() print open_id # 檢查open_id是否存在 qq_open_id = models.OAuthQQ.objects.filter(qq_openid=str(open_id)) print qq_open_id if qq_open_id: # 存在則獲取對(duì)應(yīng)的用戶,并登錄 user = qq_open_id[0].user.username print user request.session["username"] = user return HttpResponseRedirect("/web/") else: # 不存在,則跳轉(zhuǎn)到綁定用戶頁面 infos = oauth_qq.get_qq_info() # 獲取用戶信息 url = "%s?open_id=%s&nickname=%s" % (reverse("bind_account"), open_id, infos["nickname"]) return HttpResponseRedirect(url)
按照思路,授權(quán)之后,調(diào)整到處理授權(quán)結(jié)果的頁面。獲取授權(quán)碼之后,用get_access_token方法得到access_token。
再用access_token獲取OpenID??映霈F(xiàn)了,若不加time.sleep(0.05)休息一下的話,會(huì)得到urlopen 10060錯(cuò)誤。
獲取到open_id之后,再判斷一下數(shù)據(jù)庫中是否存在。若存在,則已經(jīng)關(guān)聯(lián)對(duì)應(yīng)的用戶了,直接登錄該用戶。
若open_id不存在,則跳轉(zhuǎn)到綁定用戶的頁面。該頁面需要知道open_id和QQ昵稱(為什么需要QQ昵稱,下一步會(huì)提到)。通過GET方式,把這兩個(gè)參數(shù)寫在鏈接上即可傳遞過去。
本地調(diào)試,先本地打開授權(quán)頁面授權(quán),得到一個(gè)回調(diào)地址?;卣{(diào)地址上有授權(quán)碼,如下圖:
第5步、綁定用戶上面提到若open_id在數(shù)據(jù)庫中不存在,則打開綁定用戶頁面。該頁面我設(shè)計(jì)成html表單,在templates下新建qq-bind-account.html文件。如下代碼:
{% extends "base.html" %} {% block title %}QQ和賬戶綁定 {% endblock %} {% block head-js %} {% endblock %} {% block nav %} {% endblock %} {% block content %}
? 2017 JunXi. All Rights Reserved
接著,在views.py繼續(xù)編輯,添加表單處理的對(duì)應(yīng)方法:
def bind_account(request): # 綁定賬戶 open_id = request.GET.get("open_id") nickname = request.GET.get("nickname") if request.method == "POST" and request.POST: data = request.POST # 接收到前臺(tái)form表單傳過來的注冊(cè)賬戶信息 user = models.UserProfile() username = data["username"] password = data["password"].split(",")[0] user.username = username password = hash_sha256(password, username) user.password = password user.nickname = data["nickname"] user.departments_id = 1 user.save() oauthqq = models.OAuthQQ() oauthqq.qq_openid = open_id oauthqq.user_id = models.UserProfile.objects.get(username=username).id oauthqq.save() response = HttpResponseRedirect("/web/") request.session["username"] = username # 設(shè)置session return response # 返回首頁 return render(request, "qq-bind-account.html", locals())
訪問測試:
打開首頁
點(diǎn)擊QQ登錄
獲取授權(quán)并登錄
寫完代碼之后,本地測試可以通過。最后再部署到服務(wù)器并在QQ互聯(lián)提交審核。一般審核要1~2天左右。若審核不通過,又不明白審核說明,就直接找客服問問。
-----<我是分割線,下面是項(xiàng)目在pycharm中的展示>-----
-----<我是分割線,下面是urls.py、view.py、oauth_client.py完整的代碼>-----
urls.py
urlpatterns = [ url(r"^oauth/qq/login", qq_login, name="qq_login"), url(r"^oauth/qq/check", qq_check, name="qq_check"), url(r"^oauth/bind/account", bind_account, name="bind_account"), ]
views.py
#!/usr/bin/env python # _*_ coding:utf-8 _*_ __author__ = "junxi" from django.shortcuts import render, HttpResponseRedirect, HttpResponse, reverse # reverse url逆向解析 from django.http import JsonResponse from . import models from .form import * from script.salt_api import salt from script.web_ssh import webssh from django.contrib.auth.hashers import make_password, check_password # from django.forms.models import model_to_dict from django.core import serializers import datetime import json import hashlib import re import time import os from django.conf import settings from oauth_client import OAuthQQ def hash_sha256(password, username): # sha256加密 sha256 = hashlib.sha256() sha256.update((password + username).encode("utf-8")) sha256_password = sha256.hexdigest() return sha256_password def qq_login(request): # 第三方QQ登錄 oauth_qq = OAuthQQ(settings.QQ_APP_ID, settings.QQ_KEY, settings.QQ_RECALL_URL) # 獲取 得到Authorization Code的地址 url = oauth_qq.get_auth_url() # 重定向到授權(quán)頁面 return HttpResponseRedirect(url) def qq_check(request): # 第三方QQ登錄,回調(diào)函數(shù) """登錄之后,會(huì)跳轉(zhuǎn)到這里。需要判斷code和state""" request_code = request.GET.get("code") oauth_qq = OAuthQQ(settings.QQ_APP_ID, settings.QQ_KEY, settings.QQ_RECALL_URL) # 獲取access_token access_token = oauth_qq.get_access_token(request_code) time.sleep(0.05) # 稍微休息一下,避免發(fā)送urlopen的10060錯(cuò)誤 open_id = oauth_qq.get_open_id() print open_id # 檢查open_id是否存在 qq_open_id = models.OAuthQQ.objects.filter(qq_openid=str(open_id)) print qq_open_id if qq_open_id: # 存在則獲取對(duì)應(yīng)的用戶,并登錄 user = qq_open_id[0].user.username print user request.session["username"] = user return HttpResponseRedirect("/web/") else: # 不存在,則跳轉(zhuǎn)到綁定用戶頁面 infos = oauth_qq.get_qq_info() # 獲取用戶信息 url = "%s?open_id=%s&nickname=%s" % (reverse("bind_account"), open_id, infos["nickname"]) return HttpResponseRedirect(url) def bind_account(request): # 綁定賬戶 open_id = request.GET.get("open_id") nickname = request.GET.get("nickname") if request.method == "POST" and request.POST: data = request.POST # 接收到前臺(tái)form表單傳過來的注冊(cè)賬戶信息 user = models.UserProfile() username = data["username"] password = data["password"].split(",")[0] user.username = username password = hash_sha256(password, username) user.password = password user.nickname = data["nickname"] user.departments_id = 1 user.save() oauthqq = models.OAuthQQ() oauthqq.qq_openid = open_id oauthqq.user_id = models.UserProfile.objects.get(username=username).id oauthqq.save() response = HttpResponseRedirect("/web/") request.session["username"] = username # 設(shè)置session return response # 返回首頁 return render(request, "qq-bind-account.html", locals())
oauth_client.py
#!/usr/bin/env python # _*_ coding:utf-8 _*_ __author__ = "junxi" import json import urllib, urllib2, urlparse class OAuthQQ: def __init__(self, client_id, client_key, redirect_uri): self.client_id = client_id self.client_key = client_key self.redirect_uri = redirect_uri def get_auth_url(self): """獲取授權(quán)頁面的網(wǎng)址""" params = {"client_id": self.client_id, "response_type": "code", "redirect_uri": self.redirect_uri, "scope": "get_user_info", "state": 1} url = "https://graph.qq.com/oauth2.0/authorize?%s" % urllib.urlencode(params) return url def get_access_token(self, code): """根據(jù)code獲取access_token""" params = {"grant_type": "authorization_code", "client_id": self.client_id, "client_secret": self.client_key, "code": code, "redirect_uri": self.redirect_uri} # 回調(diào)地址 url = "https://graph.qq.com/oauth2.0/token?%s" % urllib.urlencode(params) # 訪問該網(wǎng)址,獲取access_token response = urllib2.urlopen(url).read() result = urlparse.parse_qs(response, True) access_token = str(result["access_token"][0]) self.access_token = access_token return access_token def get_open_id(self): """獲取QQ的OpenID""" params = {"access_token": self.access_token} url = "https://graph.qq.com/oauth2.0/me?%s" % urllib.urlencode(params) response = urllib2.urlopen(url).read() v_str = str(response)[9:-3] # 去掉callback的字符 v_json = json.loads(v_str) openid = v_json["openid"] self.openid = openid return openid def get_qq_info(self): """獲取QQ用戶的資料信息""" params = {"access_token": self.access_token, "oauth_consumer_key": self.client_id, "openid": self.openid} url = "https://graph.qq.com/user/get_user_info?%s" % urllib.urlencode(params) response = urllib2.urlopen(url).read() return json.loads(response)
參考文章
。。。。。。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/41595.html
摘要:寫在前面本周剛在項(xiàng)目中實(shí)現(xiàn)了微信第三方網(wǎng)站掃碼登錄。準(zhǔn)備與注意事項(xiàng)微信公眾平臺(tái)跟微信開放平臺(tái)是兩個(gè)不同的平臺(tái),別搞混了。參數(shù)在微信開放平臺(tái)中查看。 寫在前面 本周剛在項(xiàng)目中實(shí)現(xiàn)了微信第三方網(wǎng)站掃碼登錄。因?yàn)榈谝淮螌懴嚓P(guān)項(xiàng)目,所以遇到了很多坑。所以寫這篇文章是希望像我之前那樣的小白也能從容的開發(fā),不要浪費(fèi)無謂的時(shí)間,這篇文章盡量寫的詳細(xì)簡單。準(zhǔn)備與注意事項(xiàng) 微信公眾平臺(tái)跟微信開放平臺(tái)是...
摘要:使用微博進(jìn)行第三方登錄進(jìn)入微博開放平臺(tái)申請(qǐng)應(yīng)用。根據(jù)微博的接口寫的微博登錄的一個(gè)第一步生成一個(gè)網(wǎng)頁地址,訪問后是微博第三方登錄的頁面,登錄會(huì)返回一個(gè)授權(quán)碼必填申請(qǐng)應(yīng)用時(shí)分配的。 OAuth2.0是什么 ?OAuth的英文全稱是Open Authorization,它是一種開放授權(quán)協(xié)議。OAuth目前共有2個(gè)版本,2007年12月的1.0版(之后有一個(gè)修正版1.0a)和2010年4月的2...
摘要:前言之前讓網(wǎng)頁公司制作新官網(wǎng)的時(shí)候規(guī)劃有第三方賬號(hào)的登錄功能,但由于當(dāng)時(shí)的一些開放平臺(tái)申請(qǐng)步驟比較繁瑣尤其是微信開放平臺(tái),所以一直拖延著,到了最近只能自己添加相關(guān)的功能。 前言 之前讓網(wǎng)頁公司制作新官網(wǎng)的時(shí)候規(guī)劃有第三方賬號(hào)的登錄功能,但由于當(dāng)時(shí)的一些開放平臺(tái)申請(qǐng)步驟比較繁瑣(尤其是微信開放平臺(tái)),所以一直拖延著,到了最近只能自己添加相關(guān)的功能。 由于是剛接觸Python和Django...
摘要:本章講如何幫助健忘癥患者,重置用戶密碼。實(shí)際上不僅內(nèi)置了密碼重置,還包括登錄登出密碼修改等功能??偨Y(jié)本章學(xué)習(xí)了使用第三方庫,高效完成了重置密碼的功能。有疑問請(qǐng)?jiān)诙刨惖膫€(gè)人網(wǎng)站留言,我會(huì)盡快回復(fù)。 隨著技術(shù)的發(fā)展,驗(yàn)證用戶身份的手段越來越多,指紋、面容、聲紋應(yīng)有盡有,但密碼依然是最重要的手段。 互聯(lián)網(wǎng)處處都有密碼的身影,甚至變成了現(xiàn)代人的一種負(fù)擔(dān)。像筆者這樣的,動(dòng)輒幾十個(gè)賬號(hào)密碼,忘記其...
摘要:我接觸已經(jīng)很久了,其中微信的就是我貢獻(xiàn)的代碼,然而當(dāng)時(shí)做的時(shí)候比較年輕,而且這個(gè)項(xiàng)目處于一個(gè)很大的代碼重構(gòu)中,借這次機(jī)會(huì)重新用正確的姿勢接入了一下三方登錄,可以當(dāng)做一個(gè)學(xué)習(xí)接入三方的。 為什么要接入三方登錄 如果你的微信服務(wù)器要做復(fù)雜的邏輯,比如html5、給用戶提供高級(jí)的服務(wù),并且有很好看的頁面等等,這種時(shí)候你就需要一個(gè)正常的web服務(wù)器,用戶打通就需要做三方登錄了。 而如果你決定直...
閱讀 1127·2021-11-15 18:00
閱讀 2840·2021-09-22 15:18
閱讀 1998·2021-09-04 16:45
閱讀 781·2019-08-30 15:55
閱讀 3902·2019-08-30 13:10
閱讀 1374·2019-08-30 11:06
閱讀 2011·2019-08-29 12:51
閱讀 2326·2019-08-26 13:55