social-auth-app-django模块是专门用于Django的第三方登录OAuth2协议模块
目前流行的第三方登录都采用了OAuth2协议
安装
pip install social-auth-app-django
social-auth-app-django最新版本目前不支持飞书登录,本文将以飞书登录的方式介绍social-auth-app-django模块的使用
首先你需要去飞书开发平台创建一个你的应用,获取到App ID和App Secret

使用配置
1.将social_django添加到app配置,settings.py

2.输入命令migrate来生成第三方登录需要的表
python manage.py migrate social_django

3.配置需要的第三方登录模块,settings.py
第三方登录模块放在social_core插件下的backends目录,里面有很多第三方登录模块,比如,微信,微博,QQ,Facebook,GitHub,亚马逊等,没有飞书

# 第三方登录设置邮箱和用户名和手机号均可登录
AUTHENTICATION_BACKENDS = (
#第三方登录配置之微博登录
'social_core.backends.weibo.WeiboOAuth2',
#第三方登录配置之QQ登录
'social_core.backends.qq.QQOAuth2',
#第三方登录配置之微信登录,微信有两种:oauth2 和 oauth2app,我们使用auth2
'social_core.backends.weixin.WeixinOAuth2',
'common.feishu.feishu.FeiShuOAuth2', # 使用自定义飞书登录
'django.contrib.auth.backends.ModelBackend',
)
# 第三方登录相关
#微博应用的key与secret
SOCIAL_AUTH_WEIBO_KEY = ''
SOCIAL_AUTH_WEIBO_SECRET = ''
#QQ应用的key与secret
SOCIAL_AUTH_QQ_KEY = ''
SOCIAL_AUTH_QQ_SECRET = ''
#微信应用的key与secret
SOCIAL_AUTH_WEIXIN_KEY = ''
SOCIAL_AUTH_WEIXIN_SECRET = ''
#飞书应用的key与secret
SOCIAL_AUTH_FEISHU_KEY = ''
SOCIAL_AUTH_FEISHU_SECRET = ''
# 第三方登录成功后跳转到首页
SOCIAL_AUTH_LOGIN_REDIRECT_URL = 'http://xx.xxxx.cn/#/home/project/api/'
4.编写飞书OAuth2类
# -*- coding: utf-8 -*-
# @Time : 2020-11-06 22:10
# @Author : chenshiyang
# @Email : chenshiyang@blued.com
# @File : feishu.py
# @Software: PyCharm
import json
from urllib import parse
from requests import HTTPError
from social_core.backends.oauth import BaseOAuth2
from social_core.exceptions import AuthCanceled, AuthUnknownError
class FeiShuOAuth2(BaseOAuth2):
"""feishu OAuth authentication backend"""
name = 'feishu'
AUTHORIZATION_URL = 'https://open.feishu.cn/open-apis/authen/v1/index'
ACCESS_TOKEN_URL = 'https://open.feishu.cn/open-apis/authen/v1/access_token'
APP_ACCESS_TOKEN = 'https://open.feishu.cn/open-apis/auth/v3/app_access_token/internal/'
REFRESH_TOKEN_URL = 'https://open.feishu.cn/open-apis/authen/v1/refresh_access_token'
ACCESS_TOKEN_METHOD = 'POST'
REDIRECT_STATE = False
CACHE = {}
EXTRA_DATA = [
('username', 'name'),
('avatar', 'avatar_big'),
]
def get_user_details(self, response):
"""Return user details from feishu. API URL is:
"""
if self.setting('DOMAIN_AS_USERNAME'):
username = response.get('domain', '')
else:
username = response.get('name', '')
return {
'username': username,
'email': response.get('email', ''),
# 'avatar': response.get('avatar_big', ''),
'feishu_userid': response.get('user_id', ''),
}
def user_data(self, access_token, *args, **kwargs):
"""获取用户信息
Args:
access_token:
*args:
**kwargs:
Returns:
"""
data = self.get_json('https://open.feishu.cn/open-apis/authen/v1/user_info', headers={
'Content-Type': 'application/json',
'Authorization': f'Bearer {access_token}',
})
data = data['data']
nickname = data.get('name')
if nickname:
data['name'] = nickname.encode(
'raw_unicode_escape'
).decode('utf-8')
return data
def auth_params(self, state=None):
"""获取回调地址
Args:
state:
Returns:
"""
appid, secret = self.get_key_and_secret()
redirect_uri = self.get_redirect_uri(state)
params = {
'app_id': appid,
'redirect_uri': parse.quote(redirect_uri)
}
return params
def app_access_token(self):
"""获取 app_access_token(企业自建应用)
Returns:
{
"code":0,
"msg":"ok",
"app_access_token":"xxxxx",
"expire":7200, // 过期时间,单位为秒(两小时失效)
"tenant_access_token":"xxxxx"
}
"""
appid, secret = self.get_key_and_secret()
try:
response = self.request_access_token(
self.APP_ACCESS_TOKEN,
data={'app_id': appid, 'app_secret': secret},
headers=self.auth_headers(),
method=self.ACCESS_TOKEN_METHOD
)
except HTTPError as err:
if err.response.status_code == 400:
raise AuthCanceled(self, response=err.response)
else:
raise
except KeyError:
raise AuthUnknownError(self)
if 'errcode' in response:
raise AuthCanceled(self)
return response['app_access_token']
def auth_complete_params(self, state=None):
"""飞书code效验
Args:
state:
Returns:
"""
appid, secret = self.get_key_and_secret()
return {
'grant_type': 'authorization_code', # request auth code
'code': self.data.get('code', ''), # server response code
'app_id': appid,
'secret': secret,
'app_access_token': self.app_access_token()
}
def refresh_token_params(self, token, *args, **kwargs):
"""刷新token
Args:
token:
*args:
**kwargs:
Returns:
"""
return {
'app_access_token': token,
'refresh_token': kwargs['refresh_token'],
'grant_type': 'refresh_token',
}
def auth_complete(self, *args, **kwargs):
"""Completes login process, must return user instance
获取用户身份信息
"""
self.process_error(self.data)
try:
data = self.auth_complete_params()
response = self.request_access_token(
self.ACCESS_TOKEN_URL,
data=json.dumps(data),
headers={'Content-Type': 'application/json'},
method=self.ACCESS_TOKEN_METHOD
)
if response['code'] == 0:
self.CACHE['response'] = response
else:
response = self.CACHE['response']
except HTTPError as err:
if err.response.status_code == 400:
raise AuthCanceled(self, response=err.response)
else:
raise
except KeyError:
raise AuthUnknownError(self)
if 'errcode' in response:
raise AuthCanceled(self)
self.process_error(response)
return self.do_auth(response['data']['access_token'], response=response['data'],
*args, **kwargs)
5.配置第三方登录路由url

6.配置 TEMPLATES,settings.py
配置这里,当用户登录的时候,如果用户不存在,会自动在用户表创建用户,并且关联用户信息

7.配置后,启动程序,根据url来构造请求地址和,回调地址
文件路径在social_django下
urlpatterns = [
# authentication / association
# <backend>是一个变量接收要使用登录模块的名称小写
# 请求URL
url(r'^login/(?P<backend>[^/]+){0}$'.format(extra), views.auth,
name='begin'),
# 回调URL
url(r'^complete/(?P<backend>[^/]+){0}$'.format(extra), views.complete,
name='complete'),
# disconnection
url(r'^disconnect/(?P<backend>[^/]+){0}$'.format(extra), views.disconnect,
name='disconnect'),
url(r'^disconnect/(?P<backend>[^/]+)/(?P<association_id>\d+){0}$'
.format(extra), views.disconnect, name='disconnect_individual'),
]
请求URL构造为:http://域名或者ip/login/使用模块名称小写/
如:http://127.0.0.1:8000/danlan/login/feishu/
回调URL构造为:http://域名或者ip/complete/使用模块名称小写/
如:http://127.0.0.1:8000/danlan/complete/feishu/
8.飞书应用后台添加安全重定向url
添加重定向 URL 作为免登授权码跳转地址。其他重定向 URL 将无法获取免登授权码

9.配置成功后前端调用

10.DRF配置
前后端分离的项目中将token放进响应cookie中实现登录跳转之后自动登录,此处我们需要修改social_core/actions.py 文件来适配DRF


需要在文件头部导入
from rest_framework_jwt.serializers import jwt_payload_handler, jwt_encode_handler
具体代码如下
# return backend.strategy.redirect(url)
response = backend.strategy.redirect(url)
payload = jwt_payload_handler(user)
response.set_cookie("username", user.username, max_age=24 * 3600)
response.set_cookie("avatar", user.avatar, max_age=24 * 3600)
response.set_cookie("id", user.id, max_age=24 * 3600)
response.set_cookie("token", jwt_encode_handler(payload), max_age=24 *3600)
return response
11.常见报错
- 飞书重定向地址需要转义
- 飞书登录时user表里有相同的相同邮箱(用户名密码注册过的)会登录失败







Comments | NOTHING