简书地址:https://www.jianshu.com/p/35c2083a90d7
前言:个人在学院易班技术部工作,要求开发的项目要和易班结合,于是我在原来的在线问卷系统中添加了易班联合登录模块,这也是本篇文章介绍的重点。
易班开放平台:https://open.yiban.cn/,申请开发者账号,创建网络应用,这里不过多介绍这个。
为了方便本地测试,我们把网站和回调地址设置成本地地址:
授权机制的具体介绍:
https://open.yiban.cn/wiki/index.php?page=授权机制说明
归纳起来大体分成四个部分:
前两步重定向由前端完成,获取用户令牌后的操作在后端完成,目的是从API中获取易班id和用户名
命令行执行python manage.py startapp oauth
创建联合登录的应用
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'),
]
由于之前已经建立好用户登录注册模型了,为了减少改动,我们创建一个新模型将用户和易班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页面
首先修改前面创建的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获取用户信息,然后按提示绑定账户
使用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 %}
{% endblock %}
{% block content %}
登录
欢迎你,易班用户{{ user_name }}
请绑定后继续
{% if not user.is_authenticated %}
{% else %}
用户 {{ user.username }} 已登录,请勿重复登录
{% endif %}
{% endblock %}
绑定页面:
代码github地址:待更新