资讯专栏INFORMATION COLUMN

Django增加QQ第三方登录

Heier / 2559人阅读

摘要:在授权页面里面,登录并确认授权。从腾讯提供的按钮下载放到你的登录页面即可。腾讯不给得到真实号可能是出于保护隐私的考虑。再回头编辑,添加回调地址的处理方法逆向解析第三方登录,回调函数登录之后,会跳转到这里。

准备工作_OAuth2.0

接入QQ登录前,网站需首先进行申请,获得对应的appid与appkey,以保证后续流程中可正确对网站与用户进行验证与授权。

在开发的过程中,发现获取不到QQ号,只能获取一个OpenID的东西。最后采取存储这个OpenID并绑定对应账号的方式。
所以需要创建对应的模型,即创建一个应用管理第三方登录。

QQ登录功能开发流程如下图:

第1步、QQ互联注册网站应用

打开QQ互联,进入管理中心。注册一下应用开发者,并添加网站应用,获得对应的appid与appkey。

申请appid和appkey的用途
appid:应用的唯一标识。在OAuth2.0认证过程中,appid的值即为oauth_consumer_key的值。
appkey:appid对应的密钥,访问用户资源时用来验证应用的合法性。在OAuth2.0认证过程中,appkey的值即为oauth_consumer_secret的值。

理解回调地址需要了解一下OAuth协议。
在你的网站页面里面,打开授权页面(这个授权页面不是回调地址)。在授权页面里面,登录QQ并确认授权。
授权之后,会得到一个授权码。回调地址就是用于接收这个授权码。
授权码以GET的方式返回,例如 http://www.junxi.site/web/oau...
通过这种方式,可以获取授权码,所以需要提供一个地址。这个地址先写一个暂时没有的地址,后面开发的时候,再给这个地址写对应的响应方法。

第2步、放置QQ按钮

这个QQ按钮是提供QQ登录的入口。从腾讯提供的QQ按钮下载放到你的登录页面即可。

不用看帮助文档里面的什么前端代码。这些用不上,只需要加一个固定的访问链接,再重定向即可。
前端页面此处的代码如下:

qq_login链接在下面第3步创建web应用里面设置。

第3步、创建web应用

怎么创建应用就不细说了,这是基本功。这里我已经创建了一个名称为web的django app应用。
创建完成之后,打开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)   # 关联用户信息表
    qq_openid = models.CharField(max_length=64)   # QQ的关联OpenID
    
    # def __str__(self):
    #    return self.user

该模型用于存储QQ登录返回的OpenID值。这个OpenID值是用QQ号一一对应。腾讯不给得到真实QQ号可能是出于保护隐私的考虑。

在总的urls路由中,加入这个应用路由。(总路由在和工程名一样的文件夹中的urls.py文件。这种方式对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)),
]

路由控制根据自己的工程自己写即可。

打开web应用目录下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,分别是打开授权页面和回调地址。
bind_account是绑定用户的页面。
大致思路是授权之后,得到OpenID。判断这个OpenID是否存在数据库中。若存在,则直接登录对应的用户即可;若不存在,则打开这个绑定邮箱页面,绑定对应的用户。

第4步、开发OAuth登录功能

为了管理好OAuth,在web应用的文件夹下创建oauth_client.py文件。把相关的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):
        """获取授权页面的网址"""
        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

创建一个类,需要申请QQ登录的APP_ID、APP_KEY和回调地址。这些都是固定的,我把这几个常量放入到settings.py中。settings.py添加如下常量,具体的值请在你的申请页面查找(这里还需要提一下,本地调试的方法。因为授权之后是调整到部署之后的网站上,而部署的网站还没开发响应的代码,无法响应对应的地址。这里我是本地测试环境,强制绑定Hosts域名文件解析):

# OAuth设置
QQ_APP_ID = "XXXXXX"
QQ_KEY = "XXXXXX"
QQ_RECALL_URL = "http://www.junxi.site/web/oauth/qq/check"

回到OAuthQQ类,现里面有个get_auth_url方法。该方法是获取打开授权页面的链接地址。(可参考官方帮助,写得不够清晰)

接着,在编辑web应用的views.py文件,加入qq_login对应的响应方法:

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()
    #重定向到授权页面
    return HttpResponseRedirect(url)

到这里为止,就完成了点击QQ登录按钮,跳转到授权页面。
登录授权之后,授权页面会自动跳转到我们设置的回调地址。例如 http://www.junxi.site/web/oau...
我们可以获取这个地址上面的GET参数。先假设我们可以顺利获取到,继续完善OAuthQQ类。拿到这个授权码之后,需要用该码获取腾讯的access_token通行令牌。

打开oauth_client.py文件,在OAuthQQ类添加如下方法:

    def get_access_token(self, code):
        """根据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}    # 回调地址
        url = "https://graph.qq.com/oauth2.0/token?%s" % urllib.urlencode(params)

        # 访问该网址,获取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,在服务器后台访问对应的链接,获取access_token,并返回该值。因为我后续不需要用access_token做其他动作,直接一次性获取QQ昵称和OpenID。所以不用记录这个通行令牌的有效期。

得到这个access_token之后,就可以做其他事了。首先需要获取授权用户的OpenID,因为腾讯不允许获取QQ号。只好退而求次,获取并保存OpenID。可参考官方文档。
继续给这个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基本信息的内容格式都不一样。

再回头编辑views.py,添加回调地址的处理方法:

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登录,回调函数
        """登录之后,会跳转到这里。需要判断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)  # 稍微休息一下,避免发送urlopen的10060错误
        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:
            # 存在则获取对应的用户,并登录
            user = qq_open_id[0].user.username
            print user
            request.session["username"] = user
            return HttpResponseRedirect("/web/")
        else:
            # 不存在,则跳转到绑定用户页面
            infos = oauth_qq.get_qq_info()  # 获取用户信息
            url = "%s?open_id=%s&nickname=%s" % (reverse("bind_account"), open_id, infos["nickname"])
            return HttpResponseRedirect(url)

按照思路,授权之后,调整到处理授权结果的页面。获取授权码之后,用get_access_token方法得到access_token。
再用access_token获取OpenID。坑出现了,若不加time.sleep(0.05)休息一下的话,会得到urlopen 10060错误。
获取到open_id之后,再判断一下数据库中是否存在。若存在,则已经关联对应的用户了,直接登录该用户。
若open_id不存在,则跳转到绑定用户的页面。该页面需要知道open_id和QQ昵称(为什么需要QQ昵称,下一步会提到)。通过GET方式,把这两个参数写在链接上即可传递过去。

本地调试,先本地打开授权页面授权,得到一个回调地址。回调地址上有授权码,如下图:

第5步、绑定用户

上面提到若open_id在数据库中不存在,则打开绑定用户页面。该页面我设计成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

{% endblock %}

接着,在views.py继续编辑,添加表单处理的对应方法:

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 # 接收到前台form表单传过来的注册账户信息
        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  # 设置session
        return response  # 返回首页
    return render(request, "qq-bind-account.html", locals())

访问测试:
打开首页

点击QQ登录

获取授权并登录

写完代码之后,本地测试可以通过。最后再部署到服务器并在QQ互联提交审核。一般审核要1~2天左右。若审核不通过,又不明白审核说明,就直接找客服问问。

-----<我是分割线,下面是项目在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()
    # 重定向到授权页面
    return HttpResponseRedirect(url)


def qq_check(request):  # 第三方QQ登录,回调函数
        """登录之后,会跳转到这里。需要判断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)  # 稍微休息一下,避免发送urlopen的10060错误
        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:
            # 存在则获取对应的用户,并登录
            user = qq_open_id[0].user.username
            print user
            request.session["username"] = user
            return HttpResponseRedirect("/web/")
        else:
            # 不存在,则跳转到绑定用户页面
            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 # 接收到前台form表单传过来的注册账户信息
        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  # 设置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):
        """获取授权页面的网址"""
        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):
        """根据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}    # 回调地址
        url = "https://graph.qq.com/oauth2.0/token?%s" % urllib.urlencode(params)

        # 访问该网址,获取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)



参考文章
。。。。。。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/41595.html

相关文章

  • python实现微信三方网站扫码登录(Django)

    摘要:写在前面本周刚在项目中实现了微信第三方网站扫码登录。准备与注意事项微信公众平台跟微信开放平台是两个不同的平台,别搞混了。参数在微信开放平台中查看。 写在前面 本周刚在项目中实现了微信第三方网站扫码登录。因为第一次写相关项目,所以遇到了很多坑。所以写这篇文章是希望像我之前那样的小白也能从容的开发,不要浪费无谓的时间,这篇文章尽量写的详细简单。准备与注意事项 微信公众平台跟微信开放平台是...

    lemanli 评论0 收藏0
  • Django中使用三方登录

    摘要:使用微博进行第三方登录进入微博开放平台申请应用。根据微博的接口写的微博登录的一个第一步生成一个网页地址,访问后是微博第三方登录的页面,登录会返回一个授权码必填申请应用时分配的。 OAuth2.0是什么  OAuth的英文全称是Open Authorization,它是一种开放授权协议。OAuth目前共有2个版本,2007年12月的1.0版(之后有一个修正版1.0a)和2010年4月的2...

    shadowbook 评论0 收藏0
  • Django使用Social-Auth实现微信三方网站扫码登录

    摘要:前言之前让网页公司制作新官网的时候规划有第三方账号的登录功能,但由于当时的一些开放平台申请步骤比较繁琐尤其是微信开放平台,所以一直拖延着,到了最近只能自己添加相关的功能。 前言 之前让网页公司制作新官网的时候规划有第三方账号的登录功能,但由于当时的一些开放平台申请步骤比较繁琐(尤其是微信开放平台),所以一直拖延着,到了最近只能自己添加相关的功能。 由于是刚接触Python和Django...

    fox_soyoung 评论0 收藏0
  • Django搭建个人博客:重置用户密码

    摘要:本章讲如何帮助健忘症患者,重置用户密码。实际上不仅内置了密码重置,还包括登录登出密码修改等功能。总结本章学习了使用第三方库,高效完成了重置密码的功能。有疑问请在杜赛的个人网站留言,我会尽快回复。 随着技术的发展,验证用户身份的手段越来越多,指纹、面容、声纹应有尽有,但密码依然是最重要的手段。 互联网处处都有密码的身影,甚至变成了现代人的一种负担。像笔者这样的,动辄几十个账号密码,忘记其...

    mumumu 评论0 收藏0
  • 微信公众号开发小记——3.接入三方登录

    摘要:我接触已经很久了,其中微信的就是我贡献的代码,然而当时做的时候比较年轻,而且这个项目处于一个很大的代码重构中,借这次机会重新用正确的姿势接入了一下三方登录,可以当做一个学习接入三方的。 为什么要接入三方登录 如果你的微信服务器要做复杂的逻辑,比如html5、给用户提供高级的服务,并且有很好看的页面等等,这种时候你就需要一个正常的web服务器,用户打通就需要做三方登录了。 而如果你决定直...

    aisuhua 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<