|
|
|
|
@ -15,73 +15,73 @@ logger = logging.getLogger(__name__)
|
|
|
|
|
class OAuthAccessTokenException(Exception):
|
|
|
|
|
'''
|
|
|
|
|
oauth授权失败异常
|
|
|
|
|
'''
|
|
|
|
|
''' # zy: 核心异常类 - 专门处理OAuth授权过程中的令牌获取失败
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class BaseOauthManager(metaclass=ABCMeta):
|
|
|
|
|
"""获取用户授权"""
|
|
|
|
|
AUTH_URL = None
|
|
|
|
|
AUTH_URL = None # zy: 关键URL - OAuth授权页面地址,子类必须实现
|
|
|
|
|
"""获取token"""
|
|
|
|
|
TOKEN_URL = None
|
|
|
|
|
TOKEN_URL = None # zy: 关键URL - 获取访问令牌的API地址,子类必须实现
|
|
|
|
|
"""获取用户信息"""
|
|
|
|
|
API_URL = None
|
|
|
|
|
API_URL = None # zy: 关键URL - 获取用户信息的API地址,子类必须实现
|
|
|
|
|
'''icon图标名'''
|
|
|
|
|
ICON_NAME = None
|
|
|
|
|
ICON_NAME = None # zy: 标识符 - OAuth服务商类型标识,用于匹配配置
|
|
|
|
|
|
|
|
|
|
def __init__(self, access_token=None, openid=None):
|
|
|
|
|
self.access_token = access_token
|
|
|
|
|
self.openid = openid
|
|
|
|
|
self.access_token = access_token # zy: 核心属性 - OAuth访问令牌
|
|
|
|
|
self.openid = openid # zy: 核心属性 - 用户在OAuth服务商的唯一ID
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def is_access_token_set(self):
|
|
|
|
|
return self.access_token is not None
|
|
|
|
|
return self.access_token is not None # zy: 重要检查 - 判断访问令牌是否已设置
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def is_authorized(self):
|
|
|
|
|
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.access_token is not None and self.openid is not None # zy: 核心检查 - 判断是否已完成授权
|
|
|
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
|
def get_authorization_url(self, nexturl='/'):
|
|
|
|
|
pass
|
|
|
|
|
pass # zy: 抽象方法 - 生成授权URL,子类必须实现
|
|
|
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
|
def get_access_token_by_code(self, code):
|
|
|
|
|
pass
|
|
|
|
|
pass # zy: 抽象方法 - 通过授权码获取访问令牌,子类必须实现
|
|
|
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
|
def get_oauth_userinfo(self):
|
|
|
|
|
pass
|
|
|
|
|
pass # zy: 抽象方法 - 获取用户信息,子类必须实现
|
|
|
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
|
def get_picture(self, metadata):
|
|
|
|
|
pass
|
|
|
|
|
pass # zy: 抽象方法 - 从元数据中提取头像,子类必须实现
|
|
|
|
|
|
|
|
|
|
def do_get(self, url, params, headers=None):
|
|
|
|
|
rsp = requests.get(url=url, params=params, headers=headers)
|
|
|
|
|
logger.info(rsp.text)
|
|
|
|
|
rsp = requests.get(url=url, params=params, headers=headers) # zy: 核心方法 - 执行GET请求
|
|
|
|
|
logger.info(rsp.text) # zy: 重要日志 - 记录API响应
|
|
|
|
|
return rsp.text
|
|
|
|
|
|
|
|
|
|
def do_post(self, url, params, headers=None):
|
|
|
|
|
rsp = requests.post(url, params, headers=headers)
|
|
|
|
|
logger.info(rsp.text)
|
|
|
|
|
rsp = requests.post(url, params, headers=headers) # zy: 核心方法 - 执行POST请求
|
|
|
|
|
logger.info(rsp.text) # zy: 重要日志 - 记录API响应
|
|
|
|
|
return rsp.text
|
|
|
|
|
|
|
|
|
|
def get_config(self):
|
|
|
|
|
value = OAuthConfig.objects.filter(type=self.ICON_NAME)
|
|
|
|
|
return value[0] if value else None
|
|
|
|
|
value = OAuthConfig.objects.filter(type=self.ICON_NAME) # zy: 关键查询 - 获取对应类型的OAuth配置
|
|
|
|
|
return value[0] if value else None # zy: 返回第一个配置或None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class WBOauthManager(BaseOauthManager):
|
|
|
|
|
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'
|
|
|
|
|
AUTH_URL = 'https://api.weibo.com/oauth2/authorize' # zy: 微博授权URL
|
|
|
|
|
TOKEN_URL = 'https://api.weibo.com/oauth2/access_token' # zy: 微博令牌获取URL
|
|
|
|
|
API_URL = 'https://api.weibo.com/2/users/show.json' # zy: 微博用户信息API
|
|
|
|
|
ICON_NAME = 'weibo' # zy: 微博标识
|
|
|
|
|
|
|
|
|
|
def __init__(self, access_token=None, openid=None):
|
|
|
|
|
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 ''
|
|
|
|
|
self.client_id = config.appkey if config else '' # zy: 关键配置 - 应用Key
|
|
|
|
|
self.client_secret = config.appsecret if config else '' # zy: 关键配置 - 应用Secret
|
|
|
|
|
self.callback_url = config.callback_url if config else '' # zy: 关键配置 - 回调地址
|
|
|
|
|
super(
|
|
|
|
|
WBOauthManager,
|
|
|
|
|
self).__init__(
|
|
|
|
|
@ -92,13 +92,12 @@ class WBOauthManager(BaseOauthManager):
|
|
|
|
|
params = {
|
|
|
|
|
'client_id': self.client_id,
|
|
|
|
|
'response_type': 'code',
|
|
|
|
|
'redirect_uri': self.callback_url + '&next_url=' + nexturl
|
|
|
|
|
'redirect_uri': self.callback_url + '&next_url=' + nexturl # zy: 重要参数 - 携带next_url用于跳转
|
|
|
|
|
}
|
|
|
|
|
url = self.AUTH_URL + "?" + urllib.parse.urlencode(params)
|
|
|
|
|
url = self.AUTH_URL + "?" + urllib.parse.urlencode(params) # zy: 生成完整的授权URL
|
|
|
|
|
return url
|
|
|
|
|
|
|
|
|
|
def get_access_token_by_code(self, code):
|
|
|
|
|
|
|
|
|
|
params = {
|
|
|
|
|
'client_id': self.client_id,
|
|
|
|
|
'client_secret': self.client_secret,
|
|
|
|
|
@ -106,19 +105,19 @@ class WBOauthManager(BaseOauthManager):
|
|
|
|
|
'code': code,
|
|
|
|
|
'redirect_uri': self.callback_url
|
|
|
|
|
}
|
|
|
|
|
rsp = self.do_post(self.TOKEN_URL, params)
|
|
|
|
|
rsp = self.do_post(self.TOKEN_URL, params) # zy: 关键调用 - 获取访问令牌
|
|
|
|
|
|
|
|
|
|
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()
|
|
|
|
|
self.access_token = str(obj['access_token']) # zy: 保存访问令牌
|
|
|
|
|
self.openid = str(obj['uid']) # zy: 保存用户ID
|
|
|
|
|
return self.get_oauth_userinfo() # zy: 重要流程 - 获取令牌后立即获取用户信息
|
|
|
|
|
else:
|
|
|
|
|
raise OAuthAccessTokenException(rsp)
|
|
|
|
|
raise OAuthAccessTokenException(rsp) # zy: 令牌获取失败抛出异常
|
|
|
|
|
|
|
|
|
|
def get_oauth_userinfo(self):
|
|
|
|
|
if not self.is_authorized:
|
|
|
|
|
return None
|
|
|
|
|
return None # zy: 安全检查 - 确保已授权
|
|
|
|
|
params = {
|
|
|
|
|
'uid': self.openid,
|
|
|
|
|
'access_token': self.access_token
|
|
|
|
|
@ -126,24 +125,24 @@ class WBOauthManager(BaseOauthManager):
|
|
|
|
|
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']
|
|
|
|
|
user.type = 'weibo'
|
|
|
|
|
user.token = self.access_token
|
|
|
|
|
user = OAuthUser() # zy: 创建OAuth用户对象
|
|
|
|
|
user.metadata = rsp # zy: 保存原始响应数据
|
|
|
|
|
user.picture = datas['avatar_large'] # zy: 设置用户头像
|
|
|
|
|
user.nickname = datas['screen_name'] # zy: 设置用户昵称
|
|
|
|
|
user.openid = datas['id'] # zy: 设置开放ID
|
|
|
|
|
user.type = 'weibo' # zy: 设置类型
|
|
|
|
|
user.token = self.access_token # zy: 设置访问令牌
|
|
|
|
|
if 'email' in datas and datas['email']:
|
|
|
|
|
user.email = datas['email']
|
|
|
|
|
user.email = datas['email'] # zy: 设置邮箱(如果有)
|
|
|
|
|
return user
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(e)
|
|
|
|
|
logger.error('weibo oauth error.rsp:' + rsp)
|
|
|
|
|
logger.error('weibo oauth error.rsp:' + rsp) # zy: 重要错误日志
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
def get_picture(self, metadata):
|
|
|
|
|
datas = json.loads(metadata)
|
|
|
|
|
return datas['avatar_large']
|
|
|
|
|
return datas['avatar_large'] # zy: 从元数据中提取头像URL
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ProxyManagerMixin:
|
|
|
|
|
@ -151,27 +150,27 @@ class ProxyManagerMixin:
|
|
|
|
|
if os.environ.get("HTTP_PROXY"):
|
|
|
|
|
self.proxies = {
|
|
|
|
|
"http": os.environ.get("HTTP_PROXY"),
|
|
|
|
|
"https": os.environ.get("HTTP_PROXY")
|
|
|
|
|
"https": os.environ.get("HTTP_PROXY") # zy: 重要配置 - 设置HTTP代理
|
|
|
|
|
}
|
|
|
|
|
else:
|
|
|
|
|
self.proxies = None
|
|
|
|
|
|
|
|
|
|
def do_get(self, url, params, headers=None):
|
|
|
|
|
rsp = requests.get(url=url, params=params, headers=headers, proxies=self.proxies)
|
|
|
|
|
rsp = requests.get(url=url, params=params, headers=headers, proxies=self.proxies) # zy: 带代理的GET请求
|
|
|
|
|
logger.info(rsp.text)
|
|
|
|
|
return rsp.text
|
|
|
|
|
|
|
|
|
|
def do_post(self, url, params, headers=None):
|
|
|
|
|
rsp = requests.post(url, params, headers=headers, proxies=self.proxies)
|
|
|
|
|
rsp = requests.post(url, params, headers=headers, proxies=self.proxies) # zy: 带代理的POST请求
|
|
|
|
|
logger.info(rsp.text)
|
|
|
|
|
return rsp.text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class GoogleOauthManager(ProxyManagerMixin, BaseOauthManager):
|
|
|
|
|
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'
|
|
|
|
|
AUTH_URL = 'https://accounts.google.com/o/oauth2/v2/auth' # zy: Google授权URL
|
|
|
|
|
TOKEN_URL = 'https://www.googleapis.com/oauth2/v4/token' # zy: Google令牌URL
|
|
|
|
|
API_URL = 'https://www.googleapis.com/oauth2/v3/userinfo' # zy: Google用户信息API
|
|
|
|
|
ICON_NAME = 'google' # zy: Google标识
|
|
|
|
|
|
|
|
|
|
def __init__(self, access_token=None, openid=None):
|
|
|
|
|
config = self.get_config()
|
|
|
|
|
@ -189,7 +188,7 @@ class GoogleOauthManager(ProxyManagerMixin, BaseOauthManager):
|
|
|
|
|
'client_id': self.client_id,
|
|
|
|
|
'response_type': 'code',
|
|
|
|
|
'redirect_uri': self.callback_url,
|
|
|
|
|
'scope': 'openid email',
|
|
|
|
|
'scope': 'openid email', # zy: 重要参数 - 请求openid和email权限
|
|
|
|
|
}
|
|
|
|
|
url = self.AUTH_URL + "?" + urllib.parse.urlencode(params)
|
|
|
|
|
return url
|
|
|
|
|
@ -200,7 +199,6 @@ class GoogleOauthManager(ProxyManagerMixin, BaseOauthManager):
|
|
|
|
|
'client_secret': self.client_secret,
|
|
|
|
|
'grant_type': 'authorization_code',
|
|
|
|
|
'code': code,
|
|
|
|
|
|
|
|
|
|
'redirect_uri': self.callback_url
|
|
|
|
|
}
|
|
|
|
|
rsp = self.do_post(self.TOKEN_URL, params)
|
|
|
|
|
@ -209,9 +207,9 @@ class GoogleOauthManager(ProxyManagerMixin, BaseOauthManager):
|
|
|
|
|
|
|
|
|
|
if 'access_token' in obj:
|
|
|
|
|
self.access_token = str(obj['access_token'])
|
|
|
|
|
self.openid = str(obj['id_token'])
|
|
|
|
|
self.openid = str(obj['id_token']) # zy: Google使用id_token作为openid
|
|
|
|
|
logger.info(self.ICON_NAME + ' oauth ' + rsp)
|
|
|
|
|
return self.access_token
|
|
|
|
|
return self.access_token # zy: 返回访问令牌
|
|
|
|
|
else:
|
|
|
|
|
raise OAuthAccessTokenException(rsp)
|
|
|
|
|
|
|
|
|
|
@ -223,13 +221,12 @@ class GoogleOauthManager(ProxyManagerMixin, BaseOauthManager):
|
|
|
|
|
}
|
|
|
|
|
rsp = self.do_get(self.API_URL, params)
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
|
|
datas = json.loads(rsp)
|
|
|
|
|
user = OAuthUser()
|
|
|
|
|
user.metadata = rsp
|
|
|
|
|
user.picture = datas['picture']
|
|
|
|
|
user.picture = datas['picture'] # zy: Google头像字段
|
|
|
|
|
user.nickname = datas['name']
|
|
|
|
|
user.openid = datas['sub']
|
|
|
|
|
user.openid = datas['sub'] # zy: Google用户ID字段
|
|
|
|
|
user.token = self.access_token
|
|
|
|
|
user.type = 'google'
|
|
|
|
|
if datas['email']:
|
|
|
|
|
@ -246,10 +243,10 @@ class GoogleOauthManager(ProxyManagerMixin, BaseOauthManager):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class GitHubOauthManager(ProxyManagerMixin, BaseOauthManager):
|
|
|
|
|
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'
|
|
|
|
|
AUTH_URL = 'https://github.com/login/oauth/authorize' # zy: GitHub授权URL
|
|
|
|
|
TOKEN_URL = 'https://github.com/login/oauth/access_token' # zy: GitHub令牌URL
|
|
|
|
|
API_URL = 'https://api.github.com/user' # zy: GitHub用户信息API
|
|
|
|
|
ICON_NAME = 'github' # zy: GitHub标识
|
|
|
|
|
|
|
|
|
|
def __init__(self, access_token=None, openid=None):
|
|
|
|
|
config = self.get_config()
|
|
|
|
|
@ -266,8 +263,8 @@ class GitHubOauthManager(ProxyManagerMixin, BaseOauthManager):
|
|
|
|
|
params = {
|
|
|
|
|
'client_id': self.client_id,
|
|
|
|
|
'response_type': 'code',
|
|
|
|
|
'redirect_uri': f'{self.callback_url}&next_url={next_url}',
|
|
|
|
|
'scope': 'user'
|
|
|
|
|
'redirect_uri': f'{self.callback_url}&next_url={next_url}', # zy: 使用f-string格式化URL
|
|
|
|
|
'scope': 'user' # zy: 请求user权限范围
|
|
|
|
|
}
|
|
|
|
|
url = self.AUTH_URL + "?" + urllib.parse.urlencode(params)
|
|
|
|
|
return url
|
|
|
|
|
@ -278,13 +275,12 @@ class GitHubOauthManager(ProxyManagerMixin, BaseOauthManager):
|
|
|
|
|
'client_secret': self.client_secret,
|
|
|
|
|
'grant_type': 'authorization_code',
|
|
|
|
|
'code': code,
|
|
|
|
|
|
|
|
|
|
'redirect_uri': self.callback_url
|
|
|
|
|
}
|
|
|
|
|
rsp = self.do_post(self.TOKEN_URL, params)
|
|
|
|
|
|
|
|
|
|
from urllib import parse
|
|
|
|
|
r = parse.parse_qs(rsp)
|
|
|
|
|
r = parse.parse_qs(rsp) # zy: 重要 - GitHub返回的是查询字符串格式
|
|
|
|
|
if 'access_token' in r:
|
|
|
|
|
self.access_token = (r['access_token'][0])
|
|
|
|
|
return self.access_token
|
|
|
|
|
@ -292,14 +288,13 @@ class GitHubOauthManager(ProxyManagerMixin, BaseOauthManager):
|
|
|
|
|
raise OAuthAccessTokenException(rsp)
|
|
|
|
|
|
|
|
|
|
def get_oauth_userinfo(self):
|
|
|
|
|
|
|
|
|
|
rsp = self.do_get(self.API_URL, params={}, headers={
|
|
|
|
|
"Authorization": "token " + self.access_token
|
|
|
|
|
"Authorization": "token " + self.access_token # zy: 关键 - GitHub需要在header中传递token
|
|
|
|
|
})
|
|
|
|
|
try:
|
|
|
|
|
datas = json.loads(rsp)
|
|
|
|
|
user = OAuthUser()
|
|
|
|
|
user.picture = datas['avatar_url']
|
|
|
|
|
user.picture = datas['avatar_url'] # zy: GitHub头像字段
|
|
|
|
|
user.nickname = datas['name']
|
|
|
|
|
user.openid = datas['id']
|
|
|
|
|
user.type = 'github'
|
|
|
|
|
@ -319,10 +314,10 @@ class GitHubOauthManager(ProxyManagerMixin, BaseOauthManager):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class FaceBookOauthManager(ProxyManagerMixin, BaseOauthManager):
|
|
|
|
|
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'
|
|
|
|
|
AUTH_URL = 'https://www.facebook.com/v16.0/dialog/oauth' # zy: Facebook授权URL(指定API版本)
|
|
|
|
|
TOKEN_URL = 'https://graph.facebook.com/v16.0/oauth/access_token' # zy: Facebook令牌URL
|
|
|
|
|
API_URL = 'https://graph.facebook.com/me' # zy: Facebook用户信息API
|
|
|
|
|
ICON_NAME = 'facebook' # zy: Facebook标识
|
|
|
|
|
|
|
|
|
|
def __init__(self, access_token=None, openid=None):
|
|
|
|
|
config = self.get_config()
|
|
|
|
|
@ -340,7 +335,7 @@ class FaceBookOauthManager(ProxyManagerMixin, BaseOauthManager):
|
|
|
|
|
'client_id': self.client_id,
|
|
|
|
|
'response_type': 'code',
|
|
|
|
|
'redirect_uri': self.callback_url,
|
|
|
|
|
'scope': 'email,public_profile'
|
|
|
|
|
'scope': 'email,public_profile' # zy: 请求邮箱和公开资料权限
|
|
|
|
|
}
|
|
|
|
|
url = self.AUTH_URL + "?" + urllib.parse.urlencode(params)
|
|
|
|
|
return url
|
|
|
|
|
@ -349,9 +344,7 @@ class FaceBookOauthManager(ProxyManagerMixin, BaseOauthManager):
|
|
|
|
|
params = {
|
|
|
|
|
'client_id': self.client_id,
|
|
|
|
|
'client_secret': self.client_secret,
|
|
|
|
|
# 'grant_type': 'authorization_code',
|
|
|
|
|
'code': code,
|
|
|
|
|
|
|
|
|
|
'redirect_uri': self.callback_url
|
|
|
|
|
}
|
|
|
|
|
rsp = self.do_post(self.TOKEN_URL, params)
|
|
|
|
|
@ -367,7 +360,7 @@ class FaceBookOauthManager(ProxyManagerMixin, BaseOauthManager):
|
|
|
|
|
def get_oauth_userinfo(self):
|
|
|
|
|
params = {
|
|
|
|
|
'access_token': self.access_token,
|
|
|
|
|
'fields': 'id,name,picture,email'
|
|
|
|
|
'fields': 'id,name,picture,email' # zy: 重要 - 指定需要返回的字段
|
|
|
|
|
}
|
|
|
|
|
try:
|
|
|
|
|
rsp = self.do_get(self.API_URL, params)
|
|
|
|
|
@ -381,7 +374,7 @@ class FaceBookOauthManager(ProxyManagerMixin, BaseOauthManager):
|
|
|
|
|
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'])
|
|
|
|
|
user.picture = str(datas['picture']['data']['url']) # zy: Facebook头像嵌套在data对象中
|
|
|
|
|
return user
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(e)
|
|
|
|
|
@ -393,11 +386,11 @@ class FaceBookOauthManager(ProxyManagerMixin, BaseOauthManager):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class QQOauthManager(BaseOauthManager):
|
|
|
|
|
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'
|
|
|
|
|
ICON_NAME = 'qq'
|
|
|
|
|
AUTH_URL = 'https://graph.qq.com/oauth2.0/authorize' # zy: QQ授权URL
|
|
|
|
|
TOKEN_URL = 'https://graph.qq.com/oauth2.0/token' # zy: QQ令牌URL
|
|
|
|
|
API_URL = 'https://graph.qq.com/user/get_user_info' # zy: QQ用户信息API
|
|
|
|
|
OPEN_ID_URL = 'https://graph.qq.com/oauth2.0/me' # zy: 关键 - QQ需要单独获取openid
|
|
|
|
|
ICON_NAME = 'qq' # zy: QQ标识
|
|
|
|
|
|
|
|
|
|
def __init__(self, access_token=None, openid=None):
|
|
|
|
|
config = self.get_config()
|
|
|
|
|
@ -429,7 +422,7 @@ class QQOauthManager(BaseOauthManager):
|
|
|
|
|
}
|
|
|
|
|
rsp = self.do_get(self.TOKEN_URL, params)
|
|
|
|
|
if rsp:
|
|
|
|
|
d = urllib.parse.parse_qs(rsp)
|
|
|
|
|
d = urllib.parse.parse_qs(rsp) # zy: 重要 - QQ返回查询字符串格式
|
|
|
|
|
if 'access_token' in d:
|
|
|
|
|
token = d['access_token']
|
|
|
|
|
self.access_token = token[0]
|
|
|
|
|
@ -447,18 +440,18 @@ class QQOauthManager(BaseOauthManager):
|
|
|
|
|
rsp = rsp.replace(
|
|
|
|
|
'callback(', '').replace(
|
|
|
|
|
')', '').replace(
|
|
|
|
|
';', '')
|
|
|
|
|
';', '') # zy: 关键处理 - 清理JSONP响应格式
|
|
|
|
|
obj = json.loads(rsp)
|
|
|
|
|
openid = str(obj['openid'])
|
|
|
|
|
self.openid = openid
|
|
|
|
|
return openid
|
|
|
|
|
|
|
|
|
|
def get_oauth_userinfo(self):
|
|
|
|
|
openid = self.get_open_id()
|
|
|
|
|
openid = self.get_open_id() # zy: 重要 - 先获取openid
|
|
|
|
|
if openid:
|
|
|
|
|
params = {
|
|
|
|
|
'access_token': self.access_token,
|
|
|
|
|
'oauth_consumer_key': self.client_id,
|
|
|
|
|
'oauth_consumer_key': self.client_id, # zy: QQ需要传递client_id作为oauth_consumer_key
|
|
|
|
|
'openid': self.openid
|
|
|
|
|
}
|
|
|
|
|
rsp = self.do_get(self.API_URL, params)
|
|
|
|
|
@ -473,7 +466,7 @@ class QQOauthManager(BaseOauthManager):
|
|
|
|
|
if 'email' in obj:
|
|
|
|
|
user.email = obj['email']
|
|
|
|
|
if 'figureurl' in obj:
|
|
|
|
|
user.picture = str(obj['figureurl'])
|
|
|
|
|
user.picture = str(obj['figureurl']) # zy: QQ头像字段
|
|
|
|
|
return user
|
|
|
|
|
|
|
|
|
|
def get_picture(self, metadata):
|
|
|
|
|
@ -483,22 +476,22 @@ class QQOauthManager(BaseOauthManager):
|
|
|
|
|
|
|
|
|
|
@cache_decorator(expiration=100 * 60)
|
|
|
|
|
def get_oauth_apps():
|
|
|
|
|
configs = OAuthConfig.objects.filter(is_enable=True).all()
|
|
|
|
|
configs = OAuthConfig.objects.filter(is_enable=True).all() # zy: 关键查询 - 只获取启用的OAuth配置
|
|
|
|
|
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]
|
|
|
|
|
configtypes = [x.type for x in configs] # zy: 提取配置类型列表
|
|
|
|
|
applications = BaseOauthManager.__subclasses__() # zy: 重要 - 获取所有子类(OAuth管理器)
|
|
|
|
|
apps = [x() for x in applications if x().ICON_NAME.lower() in configtypes] # zy: 创建对应的管理器实例
|
|
|
|
|
return apps
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_manager_by_type(type):
|
|
|
|
|
applications = get_oauth_apps()
|
|
|
|
|
applications = get_oauth_apps() # zy: 获取所有可用的OAuth应用
|
|
|
|
|
if applications:
|
|
|
|
|
finds = list(
|
|
|
|
|
filter(
|
|
|
|
|
lambda x: x.ICON_NAME.lower() == type.lower(),
|
|
|
|
|
lambda x: x.ICON_NAME.lower() == type.lower(), # zy: 按类型过滤
|
|
|
|
|
applications))
|
|
|
|
|
if finds:
|
|
|
|
|
return finds[0]
|
|
|
|
|
return None
|
|
|
|
|
return finds[0] # zy: 返回第一个匹配的管理器
|
|
|
|
|
return None # zy: 未找到返回None
|