美文网首页
用Django2.1开发易班联合登录

用Django2.1开发易班联合登录

作者: thinson | 来源:发表于2018-08-25 09:41 被阅读0次

    前言:个人在学院易班技术部工作,要求开发的项目要和易班结合,于是我在原来的在线问卷系统中添加了易班联合登录模块,这也是本篇文章介绍的重点。

    1. 前期准备

    易班开放平台:https://open.yiban.cn/,申请开发者账号,创建网络应用,这里不过多介绍这个。
    为了方便本地测试,我们把网站和回调地址设置成本地地址:

    网站应用信息设置

    2. 网站后端开发

    • 弄懂易班的授权机制

    授权机制的具体介绍:
    https://open.yiban.cn/wiki/index.php?page=%E6%8E%88%E6%9D%83%E6%9C%BA%E5%88%B6%E8%AF%B4%E6%98%8E

    授权过程介绍

    归纳起来大体分成四个部分:

    • 重定向至授权页面
    • 页面返回用户令牌code
    • 用用户令牌code换区用户授权凭证
    • 用access_token调用API获取相关信息。

    前两步重定向由前端完成,获取用户令牌后的操作在后端完成,目的是从API中获取易班id和用户名

    • 创建APP

    命令行执行python manage.py startapp oauth创建联合登录的应用

    • 配置url

    Prj目录下的urls.py的urlpattens中增加path('oauth/', include('oauth.urls')),
    oauth的urls.py

    from django.urls import path
    from . import views
    
    urlpatterns = [
        path('login/', views.yb_login, name='yb_login'),
        path('check/', views.yb_check, name='yb_check'),
        path('bind/', views.yb_bind, name='yb_bind'),
    ]
    
    • 编辑models

    由于之前已经建立好用户登录注册模型了,为了减少改动,我们创建一个新模型将用户和易班id关联。

    models.py

    from django.db import models
    from django.contrib.auth.models import User
    
    # Create your models here.
    class OAuthyb(models.Model):
        """yb and User Bind"""
        user = models.ForeignKey(User, on_delete=models.DO_NOTHING)   # 关联用户信息表
        yb_id = models.CharField(max_length=64)   # yb_id
    
    • 实现重定向到授权页面

    在oauth目录下创建新文件oauth_client.py,这个文件用来封装联合登录类
    编辑oauth_client.py

    import json
    from urllib import parse
    import requests
    
    class OAuth:
        def __init__(self, APPID, APPSECRET, CALLBACK):
            self.app_id = APPID
            self.key = APPSECRET
            self.redirect_url = CALLBACK
    
        def get_auth_url(self):
            params = {
                'client_id': self.app_id,
                'redirect_uri': self.redirect_url,
                'state': 1,
            }
            url = 'https://oauth.yiban.cn/code/html?%s' % parse.urlencode(params)
            return url
    

    这里把联合登录封装成一个类,含有一些必须属性,同时get_auth_url这个方法用于获取重定向url,为什么这样操作,参照官方文档第一步。

    settings添加

    APPID=XXX
    APPSECRET=XXX
    CALLBACK=XXX
    # 这里的都是你申请的信息,注意要大写,否则会出现很多BUG
    

    编辑views.py

    from django.shortcuts import render
    from django.conf import settings
    from .oauth_client import OAuth
    from django.shortcuts import HttpResponseRedirect
    from .models import OAuthyb
    from django.contrib import auth
    from django.urls import reverse
    from .forms import YBLoginForm
    
    # Create your views here.
    
    def yb_login(request):
        oauth_yb = OAuth(settings.APPID, settings.APPSECRET, settings.CALLBACK)
        url = oauth_yb.get_auth_url()
        return HttpResponseRedirect(url)
    

    前面这些引入的文件,有的是后面用到的,这一步完成后,就可以重定向到授权页面了:
    访问本地页面 http://127.0.0.1:8000/oauth/login

    授权页面
    授权完成后会携带用户令牌重定向到设置的CALLBACK页面
    • 编写回调页面处理函数获取access_token和user_info

    首先修改前面创建的OAuth类文件:
    oauth_client.py

    import json
    from urllib import parse
    import requests
    
    class OAuth:
        def __init__(self, APPID, APPSECRET, CALLBACK):
            self.app_id = APPID
            self.key = APPSECRET
            self.redirect_url = CALLBACK
        #获取授权页面的url
        def get_auth_url(self):
            params = {
                'client_id': self.app_id,
                'redirect_uri': self.redirect_url,
                'state': 1,
            }
            url = 'https://oauth.yiban.cn/code/html?%s' % parse.urlencode(params)
            return url
       #获取access_token的值,写入类并回调 
        def get_access_token(self, code):
            headers = {
                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36',
                'X-Requested-With': 'XMLHttpRequest'
            }
            params = {
                'code': code,
                'client_id': self.app_id,
                'client_secret': self.key,
                'redirect_uri': self.redirect_url,
            }
            url = 'https://oauth.yiban.cn/token/info?%s' % parse.urlencode(params)
            response = requests.get(url=url, headers=headers)
            response.encoding = 'utf-8'
            result = json.loads(response.text)
            access_token = str(result['access_token'])
            self.access_token = access_token
            return access_token
        # 获取用户信息,转换为json文件并回调
        def get_yb_info(self):
            headers = {
                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36',
                'X-Requested-With': 'XMLHttpRequest'
            }
            params = {
                'access_token': self.access_token,
            }
            url = 'https://openapi.yiban.cn/user/me?%s' % parse.urlencode(params)
            response = requests.get(url=url, headers=headers)
            response.encoding = 'utf-8'
            result = json.loads(response.text)
            return result
    

    增加的方法功能都写在注释里面了,所有的流程都是参照着官方文档来的,用到了requests库来在后端获取access_token和用户信息

    编辑views.py

    def yb_check(request):
        request_code = request.GET.get('code')
        oauth_yb = OAuth(settings.APPID, settings.APPSECRET, settings.CALLBACK)
        access_token = oauth_yb.get_access_token(request_code)
    
        print('access_key=%s' % access_token)
    
        # 获取用户信息
        infos = oauth_yb.get_yb_info()
        if infos['status'] == 'success':
            user_id = infos['info']['yb_userid']
            user_name = infos['info']['yb_username']
            # ORM查找易班id是否已绑定
            id_models = OAuthyb.objects.filter(yb_id=user_id)
            # 已绑定,直接登录
            if id_models:
                user = id_models[0].user
                user.backend = 'django.contrib.auth.backends.ModelBackend'
                auth.login(request, user)
                return HttpResponseRedirect(reverse('home'))
            # 未绑定,携带信息跳转到跳转到绑定页面
            else:
                url = '%s?user_id=%s&user_name=%s' % (reverse('yb_bind'), user_id, user_name)
                return HttpResponseRedirect(url)
        # 获取用户信息失败返回错误信息
        else:
            context = {}
            context['message'] = infos['info']['msgCN']
            return render(request, 'message.html', context)
    

    思路就是获取access_token后携带access_token获取用户信息,然后按提示绑定账户

    • 用户绑定易班id

    使用djangoform。只含有和已注册用户绑定,绑定未注册账户尚未完善

    新建forms.py

    from django import forms
    from django.contrib.auth.models import User
    from django.contrib import auth
    from .models import OAuthyb
    
    class YBLoginForm(forms.Form):
        username = forms.CharField(label='用户名',
                                   widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder':'请输入用户名'}))
        password = forms.CharField(label='密码',
                                   widget=forms.PasswordInput(attrs={'class': 'form-control', 'placeholder':'请输入密码'}))
        user_id = forms.CharField(label='', widget=forms.HiddenInput(attrs={'id': 'user_id'}))
    
        def clean(self):
            username = self.cleaned_data['username']
            password = self.cleaned_data['password']
            user = auth.authenticate(username=username, password=password)
            if user is None:
                raise forms.ValidationError("用户名或者密码不正确")
            else:
                self.cleaned_data['user'] = user
            return self.cleaned_data
    
        def clean_user_id(self):
            user_id = self.cleaned_data['user_id']
            if OAuthyb.objects.filter(user_id=user_id).exists():
                raise forms.ValidationError('该ID已绑定其他账号')
            return user_id
    
    

    views.py增加

    def yb_bind(request):
        if request.method == 'POST':
            login_form = YBLoginForm(request.POST)
            if login_form.is_valid():
                user_id = login_form.cleaned_data['user_id']
                user = login_form.cleaned_data['user']
                oauth_obj = OAuthyb()
                oauth_obj.yb_id = user_id
                oauth_obj.user = user
                oauth_obj.save()
                auth.login(request, user)
                return HttpResponseRedirect(reverse('home'))
        else:
            login_form = YBLoginForm()
        context = {}
        context['login_form'] = login_form
        context['user_id'] = request.GET.get('user_id')
        context['user_name'] = request.GET.get('user_name')
        return render(request, 'yb_login_bind.html', context)
    

    yb_login_bind.html

    {% extends 'base.html' %}
    {% block title%}
        在线填表系统|登录
    {% endblock %}
    {% block css_header %}
        <link rel="stylesheet" href="/static/home.css">
    {% endblock %}
    {% block content %}
        <div class="row">
            <div class="col-xs-4 col-xs-offset-4">
                <div class="panel panel-default">
                    <div class="panel-heading">
                    <h3 class="panel-title">登录</h3>
                </div>
                <div class="panel-body">
                <h4> 欢迎你,易班用户<b class="text-primary">{{ user_name }}</b><br>请绑定后继续</h4>
                {% if not user.is_authenticated %}
                    <form action="" method="POST">
                        {% csrf_token %}
                        {% for field in login_form %}
                            <label for="{{ field.id_for_label }}">{{ field.label }}</label>
                            {{ field }}
                            <p class="text-danger">{{ field.errors.as_text }}</p>
                        {% endfor %}
                        <span class="pull-left text-danger">{{ login_form.non_field_errors }}</span>
                        <input type="submit" value="登录" class="btn btn-default pull-right">
                    </form>
                {% else %}
                    <div><span>用户  <strong>{{ user.username }}</strong>  已登录,请勿重复登录</span></div>
                {% endif %}
                </div>
            </div>
            </div>
        </div>
        <script src="/static/bootstrap-3.3.7/js/jquery.min.js"></script>
        <script type="text/javascript">
            $(document).ready(function() {
                $('#user_id').val({{ user_id }})
            });
        </script>
    {% endblock %}
    

    绑定页面:

    绑定页面
    • 最终效果:

    易班登录效果演示

    代码github地址:待更新

    相关文章

      网友评论

          本文标题:用Django2.1开发易班联合登录

          本文链接:https://www.haomeiwen.com/subject/ttywiftx.html