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

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

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地址:待更新

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352

推荐阅读更多精彩内容