您现在的位置是:网站首页 > 分类 > 文章详情

django调用第三方github进行登录

T2018年8月1日 15:27265人围观
简介django创建user应用,调用第三方github来实现登录功能

1、进入manage.py所在的目录,创建用户中心app,命名为user

python manage.py startapp user
2、创建app后
1)在django下的settings文件中添加:
INSTALLED_APPS = [
    'user',  # 添加这个
]
2)在django下的urls.py中添加:
urlpatterns = [
    url(r'', include('user.urls')),     # 添加这个
]
3、在user这个app下的models.py中编写orm:

from django.db import models
from django.contrib.auth.models import User


# django的ORM
class OAuth_type(models.Model):
    type_name = models.CharField(max_length=12)
    title = models.CharField(max_length=12)
    img = models.ImageField(upload_to='uploadImages')

    def __str__(self):
        return self.type_name


class OAuth_ex(models.Model):
    user = models.ForeignKey(User)
    openid = models.CharField(max_length=64, default='')
    oauth_type = models.ForeignKey(OAuth_type, default=1)

    def __str__(self):
        return u'<%s>' % (self.user)

4、配置调用github所需要的id,key,callbak_url:

# github
GITHUB_APP_ID = '***'

GITHUB_KEY = '************'

GITHUB_CALLBACK_URL = 'http://127.0.0.1:8000/oauth/git_check'  # 这里本地调试就配上127.0.0.1:8000,如果是正式环境就需要将之改成对应域名

5、在user这个app下,新建一个py文件,命名为oauth_client.py,主要是定义一些调用第三方的接口方法:

import json
import urllib.request  # 这里需要导入urllib.request,之前导入urllib一致失败,找了好久!不然python3找不到具体需要导入那个方法
import pdb


class OAuth_Base(object):    # 基类,将相同的方法写入到此类中
    # 初始化,载入对应的应用id、秘钥和回调地址
    def __init__(self, client_id, client_key, redirect_url):
        self.client_id = client_id
        self.client_key = client_key
        self.redirect_url = redirect_url

    def _get(self, url, data):      # get方法
        # urlencode()主要作用就是将url附上要提交的数据,经过urlencode()转换后的data数据为?x=x
        request_url = '%s?%s' % (url, urllib.parse.urlencode(data))
        # 1、urlopen()的data参数默认为None,如果none就以get方式请求;
        # 2、当data参数不为空的时候,urlopen()提交方式为Post
        response = urllib.request.urlopen(request_url)

        return response.read()

    def _post(self, url, data):    # post方法
        # Post的数据必须是bytes或者iterable of bytes,不能是str,如果是str需要进行encode()编码
        request = urllib.request.Request(url, data=urllib.parse.urlencode(data).encode(encoding='UTF8'))
        response = urllib.request.urlopen(request)
        return response.read()

    # 下面的方法,不同的登录平台会有细微差别,需要继承基类后重写方法
    def get_auth_url(self):   # 获取code
        pass

    def get_access_token(self, code):   # 获取access token
        pass

    def get_open_id(self):    # 获取openid
        pass

    def get_user_info(self):   # 获取用户信息
        pass

    def get_email(self):   # 获取用户邮箱
        pass


# Github类
class OAuth_GITHUB(OAuth_Base):
    def get_auth_url(self):
        params = {
            'client_id': self.client_id,
            'redirect_uri': self.redirect_url,
            'scope': 'user:email',  # 赋予读取邮箱的权限
            'state': 1,  # 防止跨站点攻击,其实应该是一个随机数
            'allow_signup': True  # 默认Ture就好了
        }
        # 请求github开放的登录接口
        url = 'https://github.com/login/oauth/authorize?%s' % urllib.parse.urlencode(params)
        return url

    def get_access_token(self, code):
        params = {
            'client_id': self.client_id,
            'client_secret': self.client_key,
            'code': code,  # 获取github登录时请求中带的参数code放到这里
            'redirect_url': self.redirect_url,
            'state': 1
        }
        # 请求github开放的获取token接口
        # response返回值类似:b'access_token=ea36760726575d972d36f3c32d05f3785069d112&scope=user%3Aemail&token_type=bearer'
        response = self._post('https://github.com/login/oauth/access_token', params)  # 此处为post方法
        # 通过urllib.parse.parse_qs将reponse转换成如下字典格式:
        # {b'access_token': [b'ea36760726575d972d36f3c32d05f3785069d105'],
        # b'scope': [b'user:email'], b'token_type': [b'bearer']}
        result = urllib.parse.parse_qs(response, True)
        self.access_token = result[b'access_token'][0]
        return self.access_token

    # github不需要获取openid,因此不需要get_open_id()方法
    def get_user_info(self):
        params = {'access_token': self.access_token}
        # 请求github开放的获取用户信息接口(带上token就可以请求成功了)
        response = self._get('https://api.github.com/user', params)
        # json.dumps:dict转成str
        # json.loads:str转成dict
        result = json.loads(response.decode('utf-8'))  # decode意思是以utf8编码对字符串str进行解码,获得字符串类型对象
        self.openid = result.get('id', '')
        return result

    def get_email(self):
        params = {'access_token': self.access_token}
        # 请求github开放的获取用户邮箱接口(带上token就可以请求成功了)
        response = self._get('https://api.github.com/user/emails', params)
        # response返回的是一个list,所以后面在转换成dict后,取数据时候使用了result[0]['email']
        result = json.loads(response.decode('utf-8'))
        return result[0]['email']

6、在user这个app下的urls.py中新增路由:

from django.conf.urls import url
from . import views


# 请求地址和controller的映射
app_name = 'user'  # 这个是命名空间,多个app时候需要区别下
urlpatterns = [
    url(r'git_login', views.git_login, name='git_login'),  # 第三方github登录
    url(r'git_check', views.git_check, name='git_check'),  
    url(r'user_center', views.user_center, name='user_center'),
    url(r'check_is_login', views.check_is_login, name='check_is_login'),
]

7、在user这个app下的views.py中定义登录git/获取github返回信息/检查是否登录方法:

from django.shortcuts import render, render_to_response
from django.http import HttpResponseRedirect
from django.conf import settings
from .oauth_client import OAuth_Base,OAuth_GITHUB
from .models import OAuth_ex, OAuth_type
from django.contrib.auth import login as auth_login
from user.models import User
from django.core.urlresolvers import reverse
import time
import uuid
from .forms import BindEmail
from django.http import HttpResponse
import pdb


def git_login(request):   # 获取code
    oauth_git = OAuth_GITHUB(settings.GITHUB_APP_ID, settings.GITHUB_KEY, settings.GITHUB_CALLBACK_URL)
    url = oauth_git.get_auth_url()
    return HttpResponseRedirect(url)


# github登录成功后,会进入的此方法(为什么呢,初步测试应当是需要配置的回调URL中的最后一个/后面的值和这个方法名一致或则包含方法名)
def git_check(request):
    type = '1'
    request_code = request.GET.get('code')
    oauth_git = OAuth_GITHUB(settings.GITHUB_APP_ID, settings.GITHUB_KEY, settings.GITHUB_CALLBACK_URL)
    try:
        access_token = oauth_git.get_access_token(request_code)   # 获取access token
        time.sleep(0.1)    # 此处需要休息一下,避免发送urlopen的10060错误
    except:  # 获取令牌失败,反馈失败信息
        data = dict()
        data['goto_url'] = '/'
        data['goto_time'] = 10000
        data['goto_page'] = True
        data['message_title'] = '登录失败'
        data['message'] = '获取授权失败,请确认是否允许授权,并重试。若问题无法解决,请联系网站管理人员'
        return render_to_response('blog/response.html', data)
    infos = oauth_git.get_user_info()   # 获取用户信息
    nickname = infos.get('login', '')
    image_url = infos.get('avatar_url', '')
    # oauth_git.openid这样可以直接取到方法中的返回的self.openid
    open_id = str(oauth_git.openid)
    signature = infos.get('bio', '')
    if not signature:
        signature = "无个性签名"
    githubs = OAuth_ex.objects.filter(openid=open_id, oauth_type=type)   # 查询是否该第三方账户已绑定本网站账号
    if githubs:   # 若已绑定,直接登录
        auth_login(request, githubs[0].user, backend='django.contrib.auth.backends.ModelBackend')
        return HttpResponseRedirect('/')
    else:   # 否则尝试获取用户邮箱用于绑定账号
        try:
            email = oauth_git.get_email()
        except:   # 若获取失败,则跳转到绑定用户界面,让用户手动输入邮箱,目前可以先不做,我这里直接跳回主页,因为github注册邮箱肯定有的
             return HttpResponseRedirect('/')
    users = User.objects.filter(email=email)   # 若获取到邮箱,则查询是否存在本站用户
    if users:   # 若存在,则直接绑定
        user = users[0]
    else:   # 若不存在,则新建本站用户
        while User.objects.filter(username=nickname):   # 防止用户名重复
            nickname = nickname + '*'
        user = User(username=nickname, email=email)
        pwd = str(uuid.uuid1())   # 随机设置用户密码
        user.set_password(pwd)
        user.is_active = True
        user.save()
    oauth_type = OAuth_type.objects.get(id=type)  # 找到oauth_type中的对应type的id(可能有多个登录方式,所以有这表)
    oauth_ex = OAuth_ex(user=user, openid=open_id, oauth_type=oauth_type)
    oauth_ex.save()    # 保存后登陆
    auth_login(request, user, backend='django.contrib.auth.backends.ModelBackend')
    data = dict()    # 反馈登陆结果
    data['goto_url'] = '/'
    data['goto_time'] = 10000
    data['goto_page'] = True
    data['message_title'] = '绑定用户成功'
    data['image_url'] = image_url
    data['message'] = u'绑定成功!您的用户名为:<b>%s</b>。您现在可以同时使用本站账号和此第三方账号登录本站了!' % nickname
    return render_to_response('blog/response.html', data)


# 检查是否为登录状态
def check_is_login(request):
    if request.user.is_authenticated():
        user = request.user.username
        returnText = u'''<a href="/blog/user_center">你好,%s</a>''' % (user)
    else:
        returnText = u''' <a href="/login">登录</a>'''
    return HttpResponse(returnText, content_type="application/json")


# 用户中心(进入用户中心时判断下是否已经登录了,如果已经登录了才放行)
# django中使用request.user.is_authenticated()来判断是否已登录
def user_center(request):
    if request.user.is_authenticated():
        user = request.user
        return render_to_response('blog/usercenter.html', locals())
    else:
        return render_to_response('/login.html')

8、增加html相关页面response.html,主要是github授权成功后会跳转到此页面,同时这里也包括了使用jquery请求check_is_login接口获取是否已登录,最后用ajax方式来更新登录展示的样式:

{% load staticfiles %}
{% load blog_tags %}
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>登录_近涛个人博客 - 是一个专注软件测试的网站</title>
<meta name="keywords" content="软件测试,IT,计算机" />
<meta name="description" content="近涛个人博客,是一个专注软件测试的网站,发表功能测试,性能测试,自动化测试相关的文章" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="{% static 'blog/css/base.css' %}">
<link rel="stylesheet" href="{% static 'blog/css/index.css' %}">
<link rel="stylesheet" href="{% static 'blog/css/m.css' %}">
{#引入网站图标#}
<link rel="shortcut icon" href="../../static/blog/images/favicon.ico">
<!--[if lt IE 9]>
    <script src="{% static 'blog/js/modernizr.js' %}"></script>
<![endif]-->

</head>
<body>
<header>
  <div id="mnav">
    <div class="logo"><a href="/">近涛个人博客</a></div>
    <h2 id="mnavh"><span class="navicon"></span></h2>
    <ul id="starlist">
      <li><a href="/">首页</a></li>
      <li><a href="{% url 'blog:category_list' %}">分类</a></li>
      <li><a href="{% url 'blog:life'%}">享生活</a></li>
      <li id="user_part"><a href="{% url 'blog:login'%}">登录</a></li>
    </ul>
  </div>
  <script>
window.onload = function ()
{
    var oH2 = document.getElementById("mnavh"); 
    var oUl = document.getElementById("starlist");  
    oH2.onclick = function ()
    {
        var style = oUl.style;
        style.display = style.display == "block" ? "none" : "block";
        oH2.className = style.display == "block" ? "open" : ""
    }
}
</script> 
</header>
<div class="line46"></div>
<article>
  <div class="blank"></div>
  <div class="leftbox">
  <div class="container">
    <div class="search">
        <h2 class="hometitle">{{message_title|safe}}</h2>
        <div class="blank"></div>
        {% if image_url %}
             <div><img style="margin:auto;width: 40px;height: 40px;" src="{{image_url|safe}}"></div>
        {% endif %}
        <div class="blank"></div>
        <div  style="text-align: center">{{message|safe}}</div>
        <div class="blank"></div>
        {% if goto_page %}
        <p style="text-align: center">
            本页面在 <b><span id="time_left"></span></b> 秒后自动跳转,若未跳转,请点击<a href="{{goto_url}}">此处</a>
        </p>
        {% endif %}
        <div class="blank"></div>
        <div class="blank"></div>
    </div>

    <div class="blank"></div>
    <div class="blank"></div>
    <div class="blank"></div>
    <div class="blank"></div>

  </div>

  </div>
  <div class="rightbox">

  </div>
</article>
<footer>
  <p>Design by <a href="/">近涛个人博客</a> <a href="/">ICP备17019539号-1</a></p>
</footer>
</body>
<script src="https://cdn.bootcss.com/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="https://cdn.bootcss.com/jquery/2.1.3/jquery.min.js"></script>
<script>
$(document).ready(function(){
    //检查登录状态
    $.ajax({
        type:"GET",
        url:"{% url 'user:check_is_login' %}",
        cache:false,
        dataType:'text',
        success:function(result){
            $("#user_part").html(result);
        },
        error:function(XMLHttpRequest, textStatus, errorThrown){
            $("#user_part").html('<a href="{% url 'blog:login'%}">登录</a>');
        }
    });
 });
</script>
<script type="text/javascript">
{% if goto_page %}   <!-- 自动页面跳转 -->
    $(function(){
        var time = {{goto_time}} / 1000;
        intervalid = window.setInterval(function(){
            if (time <= 0){
                clearInterval(intervalid);
                window.location = '{{goto_url}}';
            }
            $('#time_left').text(time);
            time -= 1;
        },1000);
    });
{% endif %}
</script>
</html>

9、增加html相关页面usercenter.html:

{% load staticfiles %}
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
{% block title %}
<title>用户中心-近涛个人博客 - 是一个专注软件测试的网站</title>
{% endblock title %}
<meta name="keywords" content="软件测试,IT,计算机" />
<meta name="description" content="近涛个人博客,是一个专注软件测试的网站,发表功能测试,性能测试,自动化测试相关的文章" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{% block css %}
<link rel="stylesheet" href="{% static 'blog/css/base.css' %}">
<link rel="stylesheet" href="{% static 'blog/css/index.css' %}">
<link rel="stylesheet" href="{% static 'blog/css/m.css' %}">
{% endblock css %}
{#引入网站图标#}
<link rel="shortcut icon" href="../../static/blog/images/favicon.ico">
<!--[if lt IE 9]>
    <script src="{% static 'blog/js/modernizr.js' %}"></script>
<![endif]-->

</head>
<body>
<header>
  <div id="mnav">
    <div class="logo"><a href="/">近涛个人博客</a></div>
    <h2 id="mnavh"><span class="navicon"></span></h2>
    <ul id="starlist">
      <li><a href="/">首页</a></li>
      <li><a href="{% url 'blog:category_list' %}">分类</a></li>
      <li><a href="{% url 'blog:life'%}">享生活</a></li>
      <li id="user_part"><a href="{% url 'blog:login'%}">登录</a></li>
    </ul>
  </div>
  <script>
window.onload = function ()
{
    var oH2 = document.getElementById("mnavh");
    var oUl = document.getElementById("starlist");
    oH2.onclick = function ()
    {
        var style = oUl.style;
        style.display = style.display == "block" ? "none" : "block";
        oH2.className = style.display == "block" ? "open" : ""
    }
}
</script>
</header>
<div class="line46"></div>
<article>
  <div class="blank"></div>
  <div class="leftbox">
  {% block left %}
    <div class="search">
      <h2 class="hometitle">用户信息</h2>
      <div class="blank"></div>
      <div>昵称:<span>{{ user.username }}</span></div>
      <div class="blank"></div>
      <div>邮箱:<span>{{ user.email }}</span></div>
      <div class="blank"></div>
      <h1>用户中心未完成,后续更新...<span></span></h1>
    </div>
    <div class="blank"></div>
  {% endblock left %}
  </div>
  <div class="rightbox">
  {% block right %}
    <div class="paihang">
      <div style="text-align: center">阿里云-云大使推广</div>
      <div class="ad"><a href="https://promotion.aliyun.com/ntms/yunparter/invite.html?userCode=yu7jteov" target="_blank"><img src="../../static/blog/images/yundashi.png"></a></div>
    </div>
    <div class="paihang">
      <div style="text-align: center">阿里云-云服务器推广</div>
      <div class="ad"><a href="https://promotion.aliyun.com/ntms/act/group/team.html?group=dPtcChC65d" target="_blank"><img src="../../static/blog/images/ad2.png"></a></div>
    </div>
    <div class="weixin">
      <h2 class="ab_title">微信公众号</h2>
      <ul>
        <img src="../../static/blog/images/wx.png">
      </ul>
    </div>
  {% endblock right %}
  </div>
</article>
<footer>
  <p>Design by <a href="/">近涛个人博客</a> <a href="/">ICP备17019539号-1</a></p>
</footer>
</body>
{% block js %}
    <script src="https://cdn.bootcss.com/jquery/2.1.3/jquery.min.js"></script>
<script>
$(document).ready(function(){
    //检查登录状态
    $.ajax({
        type:"GET",
        url:"{% url 'user:check_is_login' %}",
        cache:false,
        dataType:'text',
        success:function(result){
            $("#user_part").html(result);
        },
        error:function(XMLHttpRequest, textStatus, errorThrown){
            $("#user_part").html('<a href="{% url 'blog:login'%}">登录</a>');
        }
    });
 });
</script>
{% endblock js %}
</html>

10、在manage.py所在的目录下,创建数据库,只要执行以下命令:

python manage.py makemigrations
python manage.py migrate


调用过程:
1)请求github第三方登录接口
2)登录并授权后,会回调我们在github中设置的callbak_url
3)回调到callbak_url(也就是我们网站的url)后,会进入根据callbak_url中最后一个/后面的名字跟django中url所有路由进行比对,找到对应的方法,最后进行执行该方法并将第三方获取的数据进行存储展示

参考:

https://developer.github.com/v3/guides/basics-of-authentication/
https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps/
https://developer.github.com/apps/building-oauth-apps/understanding-scopes-for-oauth-apps/
http://yshblog.com/blog/70
https://blog.csdn.net/qq_16147665/article/details/48792325

文章评论

阿里云-云大使推广
阿里云-云服务器推广

微信公众号