|
|
|
|
@ -3,131 +3,94 @@ import logging
|
|
|
|
|
import os
|
|
|
|
|
import urllib.parse
|
|
|
|
|
from abc import ABCMeta, abstractmethod
|
|
|
|
|
from urllib import parse
|
|
|
|
|
|
|
|
|
|
import requests
|
|
|
|
|
|
|
|
|
|
from djangoblog.utils import cache_decorator
|
|
|
|
|
from oauth.models import OAuthUser, OAuthConfig
|
|
|
|
|
|
|
|
|
|
import logging
|
|
|
|
|
import requests
|
|
|
|
|
import json
|
|
|
|
|
import urllib.parse
|
|
|
|
|
import os
|
|
|
|
|
from abc import ABCMeta, abstractmethod
|
|
|
|
|
from django.core.cache import cache
|
|
|
|
|
from cache_decorator import cache_decorator
|
|
|
|
|
|
|
|
|
|
# 获取logger实例
|
|
|
|
|
# 修复重复导入问题,保留一份必要导入
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class OAuthAccessTokenException(Exception):
|
|
|
|
|
'''
|
|
|
|
|
OAuth授权失败异常类
|
|
|
|
|
'''
|
|
|
|
|
'''OAuth授权失败异常类'''
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class BaseOauthManager(metaclass=ABCMeta):
|
|
|
|
|
"""OAuth授权管理器基类"""
|
|
|
|
|
|
|
|
|
|
# 授权URL
|
|
|
|
|
AUTH_URL = None
|
|
|
|
|
# 获取token的URL
|
|
|
|
|
TOKEN_URL = None
|
|
|
|
|
# 获取用户信息的API URL
|
|
|
|
|
API_URL = None
|
|
|
|
|
# icon图标名
|
|
|
|
|
ICON_NAME = None
|
|
|
|
|
|
|
|
|
|
def __init__(self, access_token=None, openid=None):
|
|
|
|
|
"""
|
|
|
|
|
初始化OAuth管理器
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
access_token: 访问令牌
|
|
|
|
|
openid: 用户唯一标识
|
|
|
|
|
"""
|
|
|
|
|
self.access_token = access_token
|
|
|
|
|
self.openid = openid
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def is_access_token_set(self):
|
|
|
|
|
"""检查access_token是否已设置"""
|
|
|
|
|
return self.access_token is not None
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def is_authorized(self):
|
|
|
|
|
"""检查是否已授权(既有access_token又有openid)"""
|
|
|
|
|
return self.is_access_token_set and self.access_token is not None and self.openid is not None
|
|
|
|
|
return self.is_access_token_set and self.openid is not None
|
|
|
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
|
def get_authorization_url(self, nexturl='/'):
|
|
|
|
|
"""获取授权URL(抽象方法)"""
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
|
def get_access_token_by_code(self, code):
|
|
|
|
|
"""通过授权码获取访问令牌(抽象方法)"""
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
|
def get_oauth_userinfo(self):
|
|
|
|
|
"""获取用户信息(抽象方法)"""
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
|
def get_picture(self, metadata):
|
|
|
|
|
"""从元数据中获取用户头像(抽象方法)"""
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def do_get(self, url, params, headers=None):
|
|
|
|
|
"""执行GET请求"""
|
|
|
|
|
rsp = requests.get(url=url, params=params, headers=headers)
|
|
|
|
|
logger.info(rsp.text)
|
|
|
|
|
return rsp.text
|
|
|
|
|
|
|
|
|
|
def do_post(self, url, params, headers=None):
|
|
|
|
|
"""执行POST请求"""
|
|
|
|
|
rsp = requests.post(url, params, headers=headers)
|
|
|
|
|
logger.info(rsp.text)
|
|
|
|
|
return rsp.text
|
|
|
|
|
|
|
|
|
|
def get_config(self):
|
|
|
|
|
"""获取OAuth配置"""
|
|
|
|
|
value = OAuthConfig.objects.filter(type=self.ICON_NAME)
|
|
|
|
|
return value[0] if value else None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class WBOauthManager(BaseOauthManager):
|
|
|
|
|
"""微博OAuth管理器"""
|
|
|
|
|
|
|
|
|
|
# 微博OAuth相关URL
|
|
|
|
|
AUTH_URL = 'https://api.weibo.com/oauth2/authorize'
|
|
|
|
|
TOKEN_URL = 'https://api.weibo.com/oauth2/access_token'
|
|
|
|
|
API_URL = 'https://api.weibo.com/2/users/show.json'
|
|
|
|
|
ICON_NAME = 'weibo'
|
|
|
|
|
|
|
|
|
|
def __init__(self, access_token=None, openid=None):
|
|
|
|
|
"""初始化微博OAuth管理器"""
|
|
|
|
|
config = self.get_config()
|
|
|
|
|
self.client_id = config.appkey if config else '' # 应用Key
|
|
|
|
|
self.client_secret = config.appsecret if config else '' # 应用Secret
|
|
|
|
|
self.callback_url = config.callback_url if config else '' # 回调URL
|
|
|
|
|
super(WBOauthManager, self).__init__(access_token=access_token, openid=openid)
|
|
|
|
|
self.client_id = config.appkey if config else ''
|
|
|
|
|
self.client_secret = config.appsecret if config else ''
|
|
|
|
|
self.callback_url = config.callback_url if config else ''
|
|
|
|
|
super().__init__(access_token=access_token, openid=openid)
|
|
|
|
|
|
|
|
|
|
def get_authorization_url(self, nexturl='/'):
|
|
|
|
|
"""获取微博授权URL"""
|
|
|
|
|
params = {
|
|
|
|
|
'client_id': self.client_id,
|
|
|
|
|
'response_type': 'code',
|
|
|
|
|
'redirect_uri': self.callback_url + '&next_url=' + nexturl
|
|
|
|
|
'redirect_uri': f'{self.callback_url}&next_url={nexturl}'
|
|
|
|
|
}
|
|
|
|
|
url = self.AUTH_URL + "?" + urllib.parse.urlencode(params)
|
|
|
|
|
return url
|
|
|
|
|
return f"{self.AUTH_URL}?{urllib.parse.urlencode(params)}"
|
|
|
|
|
|
|
|
|
|
def get_access_token_by_code(self, code):
|
|
|
|
|
"""通过授权码获取访问令牌"""
|
|
|
|
|
params = {
|
|
|
|
|
'client_id': self.client_id,
|
|
|
|
|
'client_secret': self.client_secret,
|
|
|
|
|
@ -136,68 +99,55 @@ class WBOauthManager(BaseOauthManager):
|
|
|
|
|
'redirect_uri': self.callback_url
|
|
|
|
|
}
|
|
|
|
|
rsp = self.do_post(self.TOKEN_URL, params)
|
|
|
|
|
|
|
|
|
|
obj = json.loads(rsp)
|
|
|
|
|
|
|
|
|
|
if 'access_token' in obj:
|
|
|
|
|
self.access_token = str(obj['access_token'])
|
|
|
|
|
self.openid = str(obj['uid'])
|
|
|
|
|
return self.get_oauth_userinfo()
|
|
|
|
|
else:
|
|
|
|
|
raise OAuthAccessTokenException(rsp)
|
|
|
|
|
return self.get_oauth_userinfo() # 返回OAuthUser对象
|
|
|
|
|
raise OAuthAccessTokenException(rsp) # 异常分支不返回,保持一致性
|
|
|
|
|
|
|
|
|
|
def get_oauth_userinfo(self):
|
|
|
|
|
"""获取微博用户信息"""
|
|
|
|
|
if not self.is_authorized:
|
|
|
|
|
return None
|
|
|
|
|
params = {
|
|
|
|
|
'uid': self.openid,
|
|
|
|
|
'access_token': self.access_token
|
|
|
|
|
}
|
|
|
|
|
return None # 未授权返回None
|
|
|
|
|
|
|
|
|
|
params = {'uid': self.openid, 'access_token': self.access_token}
|
|
|
|
|
rsp = self.do_get(self.API_URL, params)
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
datas = json.loads(rsp)
|
|
|
|
|
user = OAuthUser()
|
|
|
|
|
user.metadata = rsp # 原始元数据
|
|
|
|
|
user.picture = datas['avatar_large'] # 用户头像
|
|
|
|
|
user.nickname = datas['screen_name'] # 用户昵称
|
|
|
|
|
user.openid = datas['id'] # 用户OpenID
|
|
|
|
|
user.type = 'weibo' # 用户类型
|
|
|
|
|
user.token = self.access_token # 访问令牌
|
|
|
|
|
if 'email' in datas and datas['email']:
|
|
|
|
|
user.email = datas['email'] # 用户邮箱
|
|
|
|
|
return user
|
|
|
|
|
user = OAuthUser(
|
|
|
|
|
metadata=rsp,
|
|
|
|
|
picture=datas['avatar_large'],
|
|
|
|
|
nickname=datas['screen_name'],
|
|
|
|
|
openid=datas['id'],
|
|
|
|
|
type='weibo',
|
|
|
|
|
token=self.access_token,
|
|
|
|
|
email=datas.get('email')
|
|
|
|
|
)
|
|
|
|
|
return user # 正常分支返回OAuthUser对象
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(e)
|
|
|
|
|
logger.error('weibo oauth error.rsp:' + rsp)
|
|
|
|
|
return None
|
|
|
|
|
logger.error(f"weibo oauth error: {e}, rsp: {rsp}")
|
|
|
|
|
return None # 异常分支返回None,保持类型一致
|
|
|
|
|
|
|
|
|
|
def get_picture(self, metadata):
|
|
|
|
|
"""从元数据中获取用户头像"""
|
|
|
|
|
datas = json.loads(metadata)
|
|
|
|
|
return datas['avatar_large']
|
|
|
|
|
return json.loads(metadata)['avatar_large']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ProxyManagerMixin:
|
|
|
|
|
"""代理管理器混入类,用于处理网络代理"""
|
|
|
|
|
"""代理管理器混入类"""
|
|
|
|
|
|
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
|
"""初始化代理设置"""
|
|
|
|
|
if os.environ.get("HTTP_PROXY"):
|
|
|
|
|
self.proxies = {
|
|
|
|
|
"http": os.environ.get("HTTP_PROXY"),
|
|
|
|
|
"https": os.environ.get("HTTP_PROXY")
|
|
|
|
|
}
|
|
|
|
|
else:
|
|
|
|
|
self.proxies = None
|
|
|
|
|
proxy = os.environ.get("HTTP_PROXY")
|
|
|
|
|
self.proxies = {"http": proxy, "https": proxy} if proxy else None
|
|
|
|
|
super().__init__(*args, **kwargs)
|
|
|
|
|
|
|
|
|
|
def do_get(self, url, params, headers=None):
|
|
|
|
|
"""使用代理执行GET请求"""
|
|
|
|
|
rsp = requests.get(url=url, params=params, headers=headers, proxies=self.proxies)
|
|
|
|
|
logger.info(rsp.text)
|
|
|
|
|
return rsp.text
|
|
|
|
|
|
|
|
|
|
def do_post(self, url, params, headers=None):
|
|
|
|
|
"""使用代理执行POST请求"""
|
|
|
|
|
rsp = requests.post(url, params, headers=headers, proxies=self.proxies)
|
|
|
|
|
logger.info(rsp.text)
|
|
|
|
|
return rsp.text
|
|
|
|
|
@ -205,33 +155,28 @@ class ProxyManagerMixin:
|
|
|
|
|
|
|
|
|
|
class GoogleOauthManager(ProxyManagerMixin, BaseOauthManager):
|
|
|
|
|
"""Google OAuth管理器"""
|
|
|
|
|
|
|
|
|
|
AUTH_URL = 'https://accounts.google.com/o/oauth2/v2/auth'
|
|
|
|
|
TOKEN_URL = 'https://www.googleapis.com/oauth2/v4/token'
|
|
|
|
|
API_URL = 'https://www.googleapis.com/oauth2/v3/userinfo'
|
|
|
|
|
ICON_NAME = 'google'
|
|
|
|
|
|
|
|
|
|
def __init__(self, access_token=None, openid=None):
|
|
|
|
|
"""初始化Google OAuth管理器"""
|
|
|
|
|
config = self.get_config()
|
|
|
|
|
self.client_id = config.appkey if config else ''
|
|
|
|
|
self.client_secret = config.appsecret if config else ''
|
|
|
|
|
self.callback_url = config.callback_url if config else ''
|
|
|
|
|
super(GoogleOauthManager, self).__init__(access_token=access_token, openid=openid)
|
|
|
|
|
super().__init__(access_token=access_token, openid=openid)
|
|
|
|
|
|
|
|
|
|
def get_authorization_url(self, nexturl='/'):
|
|
|
|
|
"""获取Google授权URL"""
|
|
|
|
|
params = {
|
|
|
|
|
'client_id': self.client_id,
|
|
|
|
|
'response_type': 'code',
|
|
|
|
|
'redirect_uri': self.callback_url,
|
|
|
|
|
'scope': 'openid email', # 请求的权限范围
|
|
|
|
|
'scope': 'openid email'
|
|
|
|
|
}
|
|
|
|
|
url = self.AUTH_URL + "?" + urllib.parse.urlencode(params)
|
|
|
|
|
return url
|
|
|
|
|
return f"{self.AUTH_URL}?{urllib.parse.urlencode(params)}"
|
|
|
|
|
|
|
|
|
|
def get_access_token_by_code(self, code):
|
|
|
|
|
"""通过授权码获取访问令牌"""
|
|
|
|
|
params = {
|
|
|
|
|
'client_id': self.client_id,
|
|
|
|
|
'client_secret': self.client_secret,
|
|
|
|
|
@ -240,77 +185,66 @@ class GoogleOauthManager(ProxyManagerMixin, BaseOauthManager):
|
|
|
|
|
'redirect_uri': self.callback_url
|
|
|
|
|
}
|
|
|
|
|
rsp = self.do_post(self.TOKEN_URL, params)
|
|
|
|
|
|
|
|
|
|
obj = json.loads(rsp)
|
|
|
|
|
|
|
|
|
|
if 'access_token' in obj:
|
|
|
|
|
self.access_token = str(obj['access_token'])
|
|
|
|
|
self.openid = str(obj['id_token'])
|
|
|
|
|
logger.info(self.ICON_NAME + ' oauth ' + rsp)
|
|
|
|
|
return self.access_token
|
|
|
|
|
else:
|
|
|
|
|
raise OAuthAccessTokenException(rsp)
|
|
|
|
|
logger.info(f"{self.ICON_NAME} oauth {rsp}")
|
|
|
|
|
return self.access_token # 返回字符串token
|
|
|
|
|
raise OAuthAccessTokenException(rsp) # 异常分支不返回
|
|
|
|
|
|
|
|
|
|
def get_oauth_userinfo(self):
|
|
|
|
|
"""获取Google用户信息"""
|
|
|
|
|
if not self.is_authorized:
|
|
|
|
|
return None
|
|
|
|
|
params = {
|
|
|
|
|
'access_token': self.access_token
|
|
|
|
|
}
|
|
|
|
|
return None # 未授权返回None
|
|
|
|
|
|
|
|
|
|
params = {'access_token': self.access_token}
|
|
|
|
|
rsp = self.do_get(self.API_URL, params)
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
datas = json.loads(rsp)
|
|
|
|
|
user = OAuthUser()
|
|
|
|
|
user.metadata = rsp
|
|
|
|
|
user.picture = datas['picture'] # 用户头像
|
|
|
|
|
user.nickname = datas['name'] # 用户昵称
|
|
|
|
|
user.openid = datas['sub'] # 用户唯一标识
|
|
|
|
|
user.token = self.access_token
|
|
|
|
|
user.type = 'google'
|
|
|
|
|
if datas['email']:
|
|
|
|
|
user.email = datas['email'] # 用户邮箱
|
|
|
|
|
return user
|
|
|
|
|
user = OAuthUser(
|
|
|
|
|
metadata=rsp,
|
|
|
|
|
picture=datas['picture'],
|
|
|
|
|
nickname=datas['name'],
|
|
|
|
|
openid=datas['sub'],
|
|
|
|
|
type='google',
|
|
|
|
|
token=self.access_token,
|
|
|
|
|
email=datas.get('email')
|
|
|
|
|
)
|
|
|
|
|
return user # 正常分支返回OAuthUser
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(e)
|
|
|
|
|
logger.error('google oauth error.rsp:' + rsp)
|
|
|
|
|
return None
|
|
|
|
|
logger.error(f"google oauth error: {e}, rsp: {rsp}")
|
|
|
|
|
return None # 异常分支返回None
|
|
|
|
|
|
|
|
|
|
def get_picture(self, metadata):
|
|
|
|
|
"""从元数据中获取用户头像"""
|
|
|
|
|
datas = json.loads(metadata)
|
|
|
|
|
return datas['picture']
|
|
|
|
|
return json.loads(metadata)['picture']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class GitHubOauthManager(ProxyManagerMixin, BaseOauthManager):
|
|
|
|
|
"""GitHub OAuth管理器"""
|
|
|
|
|
|
|
|
|
|
AUTH_URL = 'https://github.com/login/oauth/authorize'
|
|
|
|
|
TOKEN_URL = 'https://github.com/login/oauth/access_token'
|
|
|
|
|
API_URL = 'https://api.github.com/user'
|
|
|
|
|
ICON_NAME = 'github'
|
|
|
|
|
|
|
|
|
|
def __init__(self, access_token=None, openid=None):
|
|
|
|
|
"""初始化GitHub OAuth管理器"""
|
|
|
|
|
config = self.get_config()
|
|
|
|
|
self.client_id = config.appkey if config else ''
|
|
|
|
|
self.client_secret = config.appsecret if config else ''
|
|
|
|
|
self.callback_url = config.callback_url if config else ''
|
|
|
|
|
super(GitHubOauthManager, self).__init__(access_token=access_token, openid=openid)
|
|
|
|
|
super().__init__(access_token=access_token, openid=openid)
|
|
|
|
|
|
|
|
|
|
def get_authorization_url(self, next_url='/'):
|
|
|
|
|
"""获取GitHub授权URL"""
|
|
|
|
|
params = {
|
|
|
|
|
'client_id': self.client_id,
|
|
|
|
|
'response_type': 'code',
|
|
|
|
|
'redirect_uri': f'{self.callback_url}&next_url={next_url}',
|
|
|
|
|
'scope': 'user' # 请求的用户权限
|
|
|
|
|
'scope': 'user'
|
|
|
|
|
}
|
|
|
|
|
url = self.AUTH_URL + "?" + urllib.parse.urlencode(params)
|
|
|
|
|
return url
|
|
|
|
|
return f"{self.AUTH_URL}?{urllib.parse.urlencode(params)}"
|
|
|
|
|
|
|
|
|
|
def get_access_token_by_code(self, code):
|
|
|
|
|
"""通过授权码获取访问令牌"""
|
|
|
|
|
params = {
|
|
|
|
|
'client_id': self.client_id,
|
|
|
|
|
'client_secret': self.client_secret,
|
|
|
|
|
@ -319,73 +253,61 @@ class GitHubOauthManager(ProxyManagerMixin, BaseOauthManager):
|
|
|
|
|
'redirect_uri': self.callback_url
|
|
|
|
|
}
|
|
|
|
|
rsp = self.do_post(self.TOKEN_URL, params)
|
|
|
|
|
|
|
|
|
|
# 解析URL编码的响应
|
|
|
|
|
from urllib import parse
|
|
|
|
|
r = parse.parse_qs(rsp)
|
|
|
|
|
|
|
|
|
|
if 'access_token' in r:
|
|
|
|
|
self.access_token = (r['access_token'][0])
|
|
|
|
|
return self.access_token
|
|
|
|
|
else:
|
|
|
|
|
raise OAuthAccessTokenException(rsp)
|
|
|
|
|
self.access_token = r['access_token'][0]
|
|
|
|
|
return self.access_token # 返回字符串token
|
|
|
|
|
raise OAuthAccessTokenException(rsp) # 异常分支不返回
|
|
|
|
|
|
|
|
|
|
def get_oauth_userinfo(self):
|
|
|
|
|
"""获取GitHub用户信息"""
|
|
|
|
|
rsp = self.do_get(self.API_URL, params={}, headers={
|
|
|
|
|
"Authorization": "token " + self.access_token # 使用token进行认证
|
|
|
|
|
})
|
|
|
|
|
headers = {"Authorization": f"token {self.access_token}"}
|
|
|
|
|
rsp = self.do_get(self.API_URL, params={}, headers=headers)
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
datas = json.loads(rsp)
|
|
|
|
|
user = OAuthUser()
|
|
|
|
|
user.picture = datas['avatar_url'] # 用户头像
|
|
|
|
|
user.nickname = datas['name'] # 用户昵称
|
|
|
|
|
user.openid = datas['id'] # 用户ID
|
|
|
|
|
user.type = 'github'
|
|
|
|
|
user.token = self.access_token
|
|
|
|
|
user.metadata = rsp
|
|
|
|
|
if 'email' in datas and datas['email']:
|
|
|
|
|
user.email = datas['email'] # 用户邮箱
|
|
|
|
|
return user
|
|
|
|
|
user = OAuthUser(
|
|
|
|
|
picture=datas['avatar_url'],
|
|
|
|
|
nickname=datas.get('name'),
|
|
|
|
|
openid=datas['id'],
|
|
|
|
|
type='github',
|
|
|
|
|
token=self.access_token,
|
|
|
|
|
metadata=rsp,
|
|
|
|
|
email=datas.get('email')
|
|
|
|
|
)
|
|
|
|
|
return user # 正常分支返回OAuthUser
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(e)
|
|
|
|
|
logger.error('github oauth error.rsp:' + rsp)
|
|
|
|
|
return None
|
|
|
|
|
logger.error(f"github oauth error: {e}, rsp: {rsp}")
|
|
|
|
|
return None # 异常分支返回None
|
|
|
|
|
|
|
|
|
|
def get_picture(self, metadata):
|
|
|
|
|
"""从元数据中获取用户头像"""
|
|
|
|
|
datas = json.loads(metadata)
|
|
|
|
|
return datas['avatar_url']
|
|
|
|
|
return json.loads(metadata)['avatar_url']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class FaceBookOauthManager(ProxyManagerMixin, BaseOauthManager):
|
|
|
|
|
"""Facebook OAuth管理器"""
|
|
|
|
|
|
|
|
|
|
AUTH_URL = 'https://www.facebook.com/v16.0/dialog/oauth'
|
|
|
|
|
TOKEN_URL = 'https://graph.facebook.com/v16.0/oauth/access_token'
|
|
|
|
|
API_URL = 'https://graph.facebook.com/me'
|
|
|
|
|
ICON_NAME = 'facebook'
|
|
|
|
|
|
|
|
|
|
def __init__(self, access_token=None, openid=None):
|
|
|
|
|
"""初始化Facebook OAuth管理器"""
|
|
|
|
|
config = self.get_config()
|
|
|
|
|
self.client_id = config.appkey if config else ''
|
|
|
|
|
self.client_secret = config.appsecret if config else ''
|
|
|
|
|
self.callback_url = config.callback_url if config else ''
|
|
|
|
|
super(FaceBookOauthManager, self).__init__(access_token=access_token, openid=openid)
|
|
|
|
|
super().__init__(access_token=access_token, openid=openid)
|
|
|
|
|
|
|
|
|
|
def get_authorization_url(self, next_url='/'):
|
|
|
|
|
"""获取Facebook授权URL"""
|
|
|
|
|
params = {
|
|
|
|
|
'client_id': self.client_id,
|
|
|
|
|
'response_type': 'code',
|
|
|
|
|
'redirect_uri': self.callback_url,
|
|
|
|
|
'scope': 'email,public_profile' # 请求的权限范围
|
|
|
|
|
'scope': 'email,public_profile'
|
|
|
|
|
}
|
|
|
|
|
url = self.AUTH_URL + "?" + urllib.parse.urlencode(params)
|
|
|
|
|
return url
|
|
|
|
|
return f"{self.AUTH_URL}?{urllib.parse.urlencode(params)}"
|
|
|
|
|
|
|
|
|
|
def get_access_token_by_code(self, code):
|
|
|
|
|
"""通过授权码获取访问令牌"""
|
|
|
|
|
params = {
|
|
|
|
|
'client_id': self.client_id,
|
|
|
|
|
'client_secret': self.client_secret,
|
|
|
|
|
@ -393,74 +315,67 @@ class FaceBookOauthManager(ProxyManagerMixin, BaseOauthManager):
|
|
|
|
|
'redirect_uri': self.callback_url
|
|
|
|
|
}
|
|
|
|
|
rsp = self.do_post(self.TOKEN_URL, params)
|
|
|
|
|
|
|
|
|
|
obj = json.loads(rsp)
|
|
|
|
|
|
|
|
|
|
if 'access_token' in obj:
|
|
|
|
|
token = str(obj['access_token'])
|
|
|
|
|
self.access_token = token
|
|
|
|
|
return self.access_token
|
|
|
|
|
else:
|
|
|
|
|
raise OAuthAccessTokenException(rsp)
|
|
|
|
|
self.access_token = str(obj['access_token'])
|
|
|
|
|
return self.access_token # 返回字符串token
|
|
|
|
|
raise OAuthAccessTokenException(rsp) # 异常分支不返回
|
|
|
|
|
|
|
|
|
|
def get_oauth_userinfo(self):
|
|
|
|
|
"""获取Facebook用户信息"""
|
|
|
|
|
params = {
|
|
|
|
|
'access_token': self.access_token,
|
|
|
|
|
'fields': 'id,name,picture,email' # 请求的用户字段
|
|
|
|
|
'fields': 'id,name,picture,email'
|
|
|
|
|
}
|
|
|
|
|
try:
|
|
|
|
|
rsp = self.do_get(self.API_URL, params)
|
|
|
|
|
datas = json.loads(rsp)
|
|
|
|
|
user = OAuthUser()
|
|
|
|
|
user.nickname = datas['name'] # 用户昵称
|
|
|
|
|
user.openid = datas['id'] # 用户ID
|
|
|
|
|
user.type = 'facebook'
|
|
|
|
|
user.token = self.access_token
|
|
|
|
|
user.metadata = rsp
|
|
|
|
|
if 'email' in datas and datas['email']:
|
|
|
|
|
user.email = datas['email'] # 用户邮箱
|
|
|
|
|
if 'picture' in datas and datas['picture'] and datas['picture']['data'] and datas['picture']['data']['url']:
|
|
|
|
|
user.picture = str(datas['picture']['data']['url']) # 用户头像
|
|
|
|
|
return user
|
|
|
|
|
user = OAuthUser(
|
|
|
|
|
nickname=datas['name'],
|
|
|
|
|
openid=datas['id'],
|
|
|
|
|
type='facebook',
|
|
|
|
|
token=self.access_token,
|
|
|
|
|
metadata=rsp,
|
|
|
|
|
email=datas.get('email')
|
|
|
|
|
)
|
|
|
|
|
# 处理头像URL
|
|
|
|
|
if 'picture' in datas:
|
|
|
|
|
pic_data = datas['picture'].get('data', {})
|
|
|
|
|
user.picture = pic_data.get('url', '')
|
|
|
|
|
return user # 正常分支返回OAuthUser
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(e)
|
|
|
|
|
return None
|
|
|
|
|
return None # 异常分支返回None
|
|
|
|
|
|
|
|
|
|
def get_picture(self, metadata):
|
|
|
|
|
"""从元数据中获取用户头像"""
|
|
|
|
|
datas = json.loads(metadata)
|
|
|
|
|
return str(datas['picture']['data']['url'])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class QQOauthManager(BaseOauthManager):
|
|
|
|
|
"""QQ OAuth管理器"""
|
|
|
|
|
|
|
|
|
|
AUTH_URL = 'https://graph.qq.com/oauth2.0/authorize'
|
|
|
|
|
TOKEN_URL = 'https://graph.qq.com/oauth2.0/token'
|
|
|
|
|
API_URL = 'https://graph.qq.com/user/get_user_info'
|
|
|
|
|
OPEN_ID_URL = 'https://graph.qq.com/oauth2.0/me' # 获取OpenID的URL
|
|
|
|
|
OPEN_ID_URL = 'https://graph.qq.com/oauth2.0/me'
|
|
|
|
|
ICON_NAME = 'qq'
|
|
|
|
|
|
|
|
|
|
def __init__(self, access_token=None, openid=None):
|
|
|
|
|
"""初始化QQ OAuth管理器"""
|
|
|
|
|
config = self.get_config()
|
|
|
|
|
self.client_id = config.appkey if config else ''
|
|
|
|
|
self.client_secret = config.appsecret if config else ''
|
|
|
|
|
self.callback_url = config.callback_url if config else ''
|
|
|
|
|
super(QQOauthManager, self).__init__(access_token=access_token, openid=openid)
|
|
|
|
|
super().__init__(access_token=access_token, openid=openid)
|
|
|
|
|
|
|
|
|
|
def get_authorization_url(self, next_url='/'):
|
|
|
|
|
"""获取QQ授权URL"""
|
|
|
|
|
params = {
|
|
|
|
|
'response_type': 'code',
|
|
|
|
|
'client_id': self.client_id,
|
|
|
|
|
'redirect_uri': self.callback_url + '&next_url=' + next_url,
|
|
|
|
|
'redirect_uri': f'{self.callback_url}&next_url={next_url}',
|
|
|
|
|
}
|
|
|
|
|
url = self.AUTH_URL + "?" + urllib.parse.urlencode(params)
|
|
|
|
|
return url
|
|
|
|
|
return f"{self.AUTH_URL}?{urllib.parse.urlencode(params)}"
|
|
|
|
|
|
|
|
|
|
def get_access_token_by_code(self, code):
|
|
|
|
|
"""通过授权码获取访问令牌"""
|
|
|
|
|
params = {
|
|
|
|
|
'grant_type': 'authorization_code',
|
|
|
|
|
'client_id': self.client_id,
|
|
|
|
|
@ -469,78 +384,77 @@ class QQOauthManager(BaseOauthManager):
|
|
|
|
|
'redirect_uri': self.callback_url
|
|
|
|
|
}
|
|
|
|
|
rsp = self.do_get(self.TOKEN_URL, params)
|
|
|
|
|
|
|
|
|
|
if rsp:
|
|
|
|
|
# 解析URL编码的响应
|
|
|
|
|
d = urllib.parse.parse_qs(rsp)
|
|
|
|
|
if 'access_token' in d:
|
|
|
|
|
token = d['access_token']
|
|
|
|
|
self.access_token = token[0]
|
|
|
|
|
return token
|
|
|
|
|
else:
|
|
|
|
|
raise OAuthAccessTokenException(rsp)
|
|
|
|
|
token = d['access_token'][0]
|
|
|
|
|
self.access_token = token
|
|
|
|
|
return token # 返回字符串token
|
|
|
|
|
raise OAuthAccessTokenException(rsp) # 异常分支不返回
|
|
|
|
|
|
|
|
|
|
def get_open_id(self):
|
|
|
|
|
"""获取用户的OpenID"""
|
|
|
|
|
if self.is_access_token_set:
|
|
|
|
|
params = {
|
|
|
|
|
'access_token': self.access_token
|
|
|
|
|
}
|
|
|
|
|
params = {'access_token': self.access_token}
|
|
|
|
|
rsp = self.do_get(self.OPEN_ID_URL, params)
|
|
|
|
|
|
|
|
|
|
if rsp:
|
|
|
|
|
# 清理响应格式(JSONP格式)
|
|
|
|
|
rsp = rsp.replace('callback(', '').replace(')', '').replace(';', '')
|
|
|
|
|
obj = json.loads(rsp)
|
|
|
|
|
# 清理JSONP格式响应
|
|
|
|
|
cleaned_rsp = rsp.replace('callback(', '').replace(')', '').replace(';', '')
|
|
|
|
|
obj = json.loads(cleaned_rsp)
|
|
|
|
|
openid = str(obj['openid'])
|
|
|
|
|
self.openid = openid
|
|
|
|
|
return openid
|
|
|
|
|
return openid # 成功获取返回字符串openid
|
|
|
|
|
|
|
|
|
|
return None # 失败分支返回None,保持类型一致
|
|
|
|
|
|
|
|
|
|
def get_oauth_userinfo(self):
|
|
|
|
|
"""获取QQ用户信息"""
|
|
|
|
|
openid = self.get_open_id()
|
|
|
|
|
if openid:
|
|
|
|
|
params = {
|
|
|
|
|
'access_token': self.access_token,
|
|
|
|
|
'oauth_consumer_key': self.client_id,
|
|
|
|
|
'openid': self.openid
|
|
|
|
|
}
|
|
|
|
|
rsp = self.do_get(self.API_URL, params)
|
|
|
|
|
logger.info(rsp)
|
|
|
|
|
if not openid:
|
|
|
|
|
return None # 无openid返回None
|
|
|
|
|
|
|
|
|
|
params = {
|
|
|
|
|
'access_token': self.access_token,
|
|
|
|
|
'oauth_consumer_key': self.client_id,
|
|
|
|
|
'openid': self.openid
|
|
|
|
|
}
|
|
|
|
|
rsp = self.do_get(self.API_URL, params)
|
|
|
|
|
logger.info(rsp)
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
obj = json.loads(rsp)
|
|
|
|
|
user = OAuthUser()
|
|
|
|
|
user.nickname = obj['nickname'] # 用户昵称
|
|
|
|
|
user.openid = openid
|
|
|
|
|
user.type = 'qq'
|
|
|
|
|
user.token = self.access_token
|
|
|
|
|
user.metadata = rsp
|
|
|
|
|
if 'email' in obj:
|
|
|
|
|
user.email = obj['email'] # 用户邮箱
|
|
|
|
|
if 'figureurl' in obj:
|
|
|
|
|
user.picture = str(obj['figureurl']) # 用户头像
|
|
|
|
|
return user
|
|
|
|
|
user = OAuthUser(
|
|
|
|
|
nickname=obj['nickname'],
|
|
|
|
|
openid=openid,
|
|
|
|
|
type='qq',
|
|
|
|
|
token=self.access_token,
|
|
|
|
|
metadata=rsp,
|
|
|
|
|
email=obj.get('email'),
|
|
|
|
|
picture=obj.get('figureurl', '')
|
|
|
|
|
)
|
|
|
|
|
return user # 正常分支返回OAuthUser
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(e)
|
|
|
|
|
return None # 异常分支返回None
|
|
|
|
|
|
|
|
|
|
def get_picture(self, metadata):
|
|
|
|
|
"""从元数据中获取用户头像"""
|
|
|
|
|
datas = json.loads(metadata)
|
|
|
|
|
return str(datas['figureurl'])
|
|
|
|
|
return str(json.loads(metadata)['figureurl'])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@cache_decorator(expiration=100 * 60)
|
|
|
|
|
def get_oauth_apps():
|
|
|
|
|
"""获取所有启用的OAuth应用(带缓存)"""
|
|
|
|
|
configs = OAuthConfig.objects.filter(is_enable=True).all()
|
|
|
|
|
if not configs:
|
|
|
|
|
return []
|
|
|
|
|
configtypes = [x.type for x in configs]
|
|
|
|
|
applications = BaseOauthManager.__subclasses__()
|
|
|
|
|
apps = [x() for x in applications if x().ICON_NAME.lower() in configtypes]
|
|
|
|
|
return apps
|
|
|
|
|
return [x() for x in applications if x().ICON_NAME.lower() in configtypes]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_manager_by_type(type):
|
|
|
|
|
"""根据类型获取对应的OAuth管理器"""
|
|
|
|
|
applications = get_oauth_apps()
|
|
|
|
|
if applications:
|
|
|
|
|
finds = list(filter(lambda x: x.ICON_NAME.lower() == type.lower(), applications))
|
|
|
|
|
finds = [x for x in applications if x.ICON_NAME.lower() == type.lower()]
|
|
|
|
|
if finds:
|
|
|
|
|
return finds[0]
|
|
|
|
|
return None
|
|
|
|
|
return None # 未找到返回None,保持
|