nch注释 #34

Merged
phm9gvnzi merged 3 commits from nch_branch into master 3 months ago

@ -9,86 +9,115 @@ 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"""
# 获取token的URL
TOKEN_URL = None
"""获取用户信息"""
# 获取用户信息的API URL
API_URL = None
'''icon图标名'''
# 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
@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 ''
self.client_secret = config.appsecret if config else ''
self.callback_url = config.callback_url if config else ''
super(
WBOauthManager,
self).__init__(
access_token=access_token,
openid=openid)
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)
def get_authorization_url(self, nexturl='/'):
"""获取微博授权URL"""
params = {
'client_id': self.client_id,
'response_type': 'code',
@ -98,7 +127,7 @@ class WBOauthManager(BaseOauthManager):
return url
def get_access_token_by_code(self, code):
"""通过授权码获取访问令牌"""
params = {
'client_id': self.client_id,
'client_secret': self.client_secret,
@ -117,6 +146,7 @@ class WBOauthManager(BaseOauthManager):
raise OAuthAccessTokenException(rsp)
def get_oauth_userinfo(self):
"""获取微博用户信息"""
if not self.is_authorized:
return None
params = {
@ -127,14 +157,14 @@ class WBOauthManager(BaseOauthManager):
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.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']
user.email = datas['email'] # 用户邮箱
return user
except Exception as e:
logger.error(e)
@ -142,12 +172,16 @@ class WBOauthManager(BaseOauthManager):
return None
def get_picture(self, metadata):
"""从元数据中获取用户头像"""
datas = json.loads(metadata)
return datas['avatar_large']
class ProxyManagerMixin:
"""代理管理器混入类,用于处理网络代理"""
def __init__(self, *args, **kwargs):
"""初始化代理设置"""
if os.environ.get("HTTP_PROXY"):
self.proxies = {
"http": os.environ.get("HTTP_PROXY"),
@ -157,50 +191,52 @@ class ProxyManagerMixin:
self.proxies = None
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
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(GoogleOauthManager, self).__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
def get_access_token_by_code(self, code):
"""通过授权码获取访问令牌"""
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)
@ -216,6 +252,7 @@ class GoogleOauthManager(ProxyManagerMixin, BaseOauthManager):
raise OAuthAccessTokenException(rsp)
def get_oauth_userinfo(self):
"""获取Google用户信息"""
if not self.is_authorized:
return None
params = {
@ -223,17 +260,16 @@ 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.nickname = datas['name']
user.openid = datas['sub']
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']
user.email = datas['email'] # 用户邮箱
return user
except Exception as e:
logger.error(e)
@ -241,48 +277,50 @@ class GoogleOauthManager(ProxyManagerMixin, BaseOauthManager):
return None
def get_picture(self, metadata):
"""从元数据中获取用户头像"""
datas = json.loads(metadata)
return datas['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(GitHubOauthManager, self).__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
def get_access_token_by_code(self, code):
"""通过授权码获取访问令牌"""
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)
# 解析URL编码的响应
from urllib import parse
r = parse.parse_qs(rsp)
if 'access_token' in r:
@ -292,21 +330,21 @@ class GitHubOauthManager(ProxyManagerMixin, BaseOauthManager):
raise OAuthAccessTokenException(rsp)
def get_oauth_userinfo(self):
"""获取GitHub用户信息"""
rsp = self.do_get(self.API_URL, params={}, headers={
"Authorization": "token " + self.access_token
"Authorization": "token " + self.access_token # 使用token进行认证
})
try:
datas = json.loads(rsp)
user = OAuthUser()
user.picture = datas['avatar_url']
user.nickname = datas['name']
user.openid = datas['id']
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']
user.email = datas['email'] # 用户邮箱
return user
except Exception as e:
logger.error(e)
@ -314,44 +352,44 @@ class GitHubOauthManager(ProxyManagerMixin, BaseOauthManager):
return None
def get_picture(self, metadata):
"""从元数据中获取用户头像"""
datas = json.loads(metadata)
return datas['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(FaceBookOauthManager, self).__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
def get_access_token_by_code(self, code):
"""通过授权码获取访问令牌"""
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)
@ -365,52 +403,54 @@ class FaceBookOauthManager(ProxyManagerMixin, BaseOauthManager):
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']
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']
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']) # 用户头像
return user
except Exception as e:
logger.error(e)
return 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'
OPEN_ID_URL = 'https://graph.qq.com/oauth2.0/me' # 获取OpenID的URL
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(QQOauthManager, self).__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,
@ -420,6 +460,7 @@ class QQOauthManager(BaseOauthManager):
return url
def get_access_token_by_code(self, code):
"""通过授权码获取访问令牌"""
params = {
'grant_type': 'authorization_code',
'client_id': self.client_id,
@ -429,6 +470,7 @@ class QQOauthManager(BaseOauthManager):
}
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']
@ -438,22 +480,22 @@ class QQOauthManager(BaseOauthManager):
raise OAuthAccessTokenException(rsp)
def get_open_id(self):
"""获取用户的OpenID"""
if self.is_access_token_set:
params = {
'access_token': self.access_token
}
rsp = self.do_get(self.OPEN_ID_URL, params)
if rsp:
rsp = rsp.replace(
'callback(', '').replace(
')', '').replace(
';', '')
# 清理响应格式JSONP格式
rsp = rsp.replace('callback(', '').replace(')', '').replace(';', '')
obj = json.loads(rsp)
openid = str(obj['openid'])
self.openid = openid
return openid
def get_oauth_userinfo(self):
"""获取QQ用户信息"""
openid = self.get_open_id()
if openid:
params = {
@ -465,24 +507,26 @@ class QQOauthManager(BaseOauthManager):
logger.info(rsp)
obj = json.loads(rsp)
user = OAuthUser()
user.nickname = obj['nickname']
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']
user.email = obj['email'] # 用户邮箱
if 'figureurl' in obj:
user.picture = str(obj['figureurl'])
user.picture = str(obj['figureurl']) # 用户头像
return user
def get_picture(self, metadata):
"""从元数据中获取用户头像"""
datas = json.loads(metadata)
return str(datas['figureurl'])
@cache_decorator(expiration=100 * 60)
def get_oauth_apps():
"""获取所有启用的OAuth应用带缓存"""
configs = OAuthConfig.objects.filter(is_enable=True).all()
if not configs:
return []
@ -493,12 +537,10 @@ def get_oauth_apps():
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 = list(filter(lambda x: x.ICON_NAME.lower() == type.lower(), applications))
if finds:
return finds[0]
return None
return None

@ -2,24 +2,36 @@ from django.urls import path
from . import views
app_name = "oauth"
# OAuth应用的路由配置
app_name = "oauth" # 定义应用命名空间用于URL反向解析
urlpatterns = [
# OAuth授权入口点 - 启动第三方登录流程
path(
r'oauth/authorize',
views.authorize),
r'oauth/authorize', # 授权URL路径
views.authorize), # 对应的视图函数,处理授权逻辑
# 需要邮箱地址页面 - 当第三方登录未返回邮箱时要求用户输入
path(
r'oauth/requireemail/<int:oauthid>.html',
views.RequireEmailView.as_view(),
name='require_email'),
r'oauth/requireemail/<int:oauthid>.html', # 带oauthid参数的URL
views.RequireEmailView.as_view(), # 类视图,处理邮箱输入
name='require_email'), # URL名称用于反向解析
# 邮箱确认 - 验证用户输入的邮箱地址
path(
r'oauth/emailconfirm/<int:id>/<sign>.html',
views.emailconfirm,
name='email_confirm'),
r'oauth/emailconfirm/<int:id>/<sign>.html', # 带id和签名参数的URL
views.emailconfirm, # 视图函数,处理邮箱确认
name='email_confirm'), # URL名称用于反向解析
# 绑定成功页面 - 显示第三方账号绑定成功信息
path(
r'oauth/bindsuccess/<int:oauthid>.html',
views.bindsuccess,
name='bindsuccess'),
r'oauth/bindsuccess/<int:oauthid>.html', # 带oauthid参数的URL
views.bindsuccess, # 视图函数,显示绑定成功页面
name='bindsuccess'), # URL名称用于反向解析
# OAuth登录处理 - 处理第三方登录回调
path(
r'oauth/oauthlogin',
views.oauthlogin,
name='oauthlogin')]
r'oauth/oauthlogin', # OAuth登录回调URL路径
views.oauthlogin, # 视图函数,处理登录回调逻辑
name='oauthlogin') # URL名称用于反向解析
]

@ -23,17 +23,32 @@ from oauth.forms import RequireEmailForm
from .models import OAuthUser
from .oauthmanager import get_manager_by_type, OAuthAccessTokenException
# 获取logger实例用于记录日志
logger = logging.getLogger(__name__)
def get_redirecturl(request):
"""
获取重定向URL并进行安全验证
Args:
request: HTTP请求对象
Returns:
str: 安全的重定向URL
"""
# 从请求参数获取next_url默认为None
nexturl = request.GET.get('next_url', None)
# 如果nexturl为空或是登录页面则重定向到首页
if not nexturl or nexturl == '/login/' or nexturl == '/login':
nexturl = '/'
return nexturl
# 解析URL检查域名安全性
p = urlparse(nexturl)
if p.netloc:
site = get_current_site().domain
# 检查域名是否匹配当前站点,防止开放重定向攻击
if not p.netloc.replace('www.', '') == site.replace('www.', ''):
logger.info('非法url:' + nexturl)
return "/"
@ -41,26 +56,54 @@ def get_redirecturl(request):
def oauthlogin(request):
"""
OAuth登录入口 - 重定向到第三方授权页面
Args:
request: HTTP请求对象
Returns:
HttpResponseRedirect: 重定向响应
"""
# 获取OAuth类型如weibo、github等
type = request.GET.get('type', None)
if not type:
return HttpResponseRedirect('/')
# 获取对应的OAuth管理器
manager = get_manager_by_type(type)
if not manager:
return HttpResponseRedirect('/')
# 获取安全的重定向URL
nexturl = get_redirecturl(request)
# 获取第三方授权URL并重定向
authorizeurl = manager.get_authorization_url(nexturl)
return HttpResponseRedirect(authorizeurl)
def authorize(request):
"""
OAuth授权回调处理 - 处理第三方登录回调
Args:
request: HTTP请求对象
Returns:
HttpResponseRedirect: 重定向响应
"""
type = request.GET.get('type', None)
if not type:
return HttpResponseRedirect('/')
manager = get_manager_by_type(type)
if not manager:
return HttpResponseRedirect('/')
# 获取授权码
code = request.GET.get('code', None)
try:
# 使用授权码获取访问令牌
rsp = manager.get_access_token_by_code(code)
except OAuthAccessTokenException as e:
logger.warning("OAuthAccessTokenException:" + str(e))
@ -68,101 +111,139 @@ def authorize(request):
except Exception as e:
logger.error(e)
rsp = None
nexturl = get_redirecturl(request)
if not rsp:
# 如果获取token失败重新跳转到授权页面
return HttpResponseRedirect(manager.get_authorization_url(nexturl))
# 获取用户信息
user = manager.get_oauth_userinfo()
if user:
# 处理昵称为空的情况
if not user.nickname or not user.nickname.strip():
user.nickname = "djangoblog" + timezone.now().strftime('%y%m%d%I%M%S')
try:
# 检查是否已存在该OAuth用户
temp = OAuthUser.objects.get(type=type, openid=user.openid)
# 更新用户信息
temp.picture = user.picture
temp.metadata = user.metadata
temp.nickname = user.nickname
user = temp
except ObjectDoesNotExist:
pass
# facebook的token过长
# Facebook的token过长清空处理
if type == 'facebook':
user.token = ''
# 如果用户有邮箱,直接处理登录
if user.email:
with transaction.atomic():
with transaction.atomic(): # 使用事务保证数据一致性
author = None
try:
author = get_user_model().objects.get(id=user.author_id)
except ObjectDoesNotExist:
pass
if not author:
# 创建或获取用户
result = get_user_model().objects.get_or_create(email=user.email)
author = result[0]
if result[1]:
if result[1]: # 如果是新创建的用户
try:
get_user_model().objects.get(username=user.nickname)
except ObjectDoesNotExist:
author.username = user.nickname
else:
# 用户名冲突时生成唯一用户名
author.username = "djangoblog" + timezone.now().strftime('%y%m%d%I%M%S')
author.source = 'authorize'
author.save()
# 关联OAuth用户和系统用户
user.author = author
user.save()
# 发送OAuth用户登录信号
oauth_user_login_signal.send(
sender=authorize.__class__, id=user.id)
# 登录用户
login(request, author)
return HttpResponseRedirect(nexturl)
else:
# 没有邮箱保存OAuth用户信息并跳转到邮箱输入页面
user.save()
url = reverse('oauth:require_email', kwargs={
'oauthid': user.id
})
return HttpResponseRedirect(url)
else:
return HttpResponseRedirect(nexturl)
def emailconfirm(request, id, sign):
"""
邮箱确认处理 - 验证邮箱并完成用户绑定
Args:
request: HTTP请求对象
id: OAuth用户ID
sign: 安全签名
Returns:
HttpResponseRedirect: 重定向响应
"""
if not sign:
return HttpResponseForbidden()
# 验证签名安全性
if not get_sha256(settings.SECRET_KEY +
str(id) +
settings.SECRET_KEY).upper() == sign.upper():
return HttpResponseForbidden()
# 获取OAuth用户
oauthuser = get_object_or_404(OAuthUser, pk=id)
with transaction.atomic():
if oauthuser.author:
author = get_user_model().objects.get(pk=oauthuser.author_id)
else:
# 创建或获取系统用户
result = get_user_model().objects.get_or_create(email=oauthuser.email)
author = result[0]
if result[1]:
if result[1]: # 新创建的用户
author.source = 'emailconfirm'
author.username = oauthuser.nickname.strip() if oauthuser.nickname.strip(
) else "djangoblog" + timezone.now().strftime('%y%m%d%I%M%S')
author.save()
# 关联用户
oauthuser.author = author
oauthuser.save()
# 发送登录信号并登录用户
oauth_user_login_signal.send(
sender=emailconfirm.__class__,
id=oauthuser.id)
login(request, author)
# 发送绑定成功邮件
site = 'http://' + get_current_site().domain
content = _('''
<p>Congratulations, you have successfully bound your email address. You can use
%(oauthuser_type)s to directly log in to this website without a password.</p>
You are welcome to continue to follow this site, the address is
<a href="%(site)s" rel="bookmark">%(site)s</a>
Thank you again!
<p>恭喜您您已成功绑定邮箱您可以使用%(oauthuser_type)s直接登录本站无需密码</p>
欢迎您继续关注本站地址是<a href="%(site)s" rel="bookmark">%(site)s</a>
再次感谢
<br />
If the link above cannot be opened, please copy this link to your browser.
如果上面的链接无法打开请将此链接复制到浏览器
%(site)s
''') % {'oauthuser_type': oauthuser.type, 'site': site}
send_email(emailto=[oauthuser.email, ], title=_('Congratulations on your successful binding!'), content=content)
send_email(emailto=[oauthuser.email, ], title=_('恭喜您绑定成功!'), content=content)
# 跳转到绑定成功页面
url = reverse('oauth:bindsuccess', kwargs={
'oauthid': id
})
@ -171,12 +252,17 @@ def emailconfirm(request, id, sign):
class RequireEmailView(FormView):
form_class = RequireEmailForm
template_name = 'oauth/require_email.html'
"""
需要邮箱视图 - 处理用户输入邮箱地址
"""
form_class = RequireEmailForm # 使用的表单类
template_name = 'oauth/require_email.html' # 模板名称
def get(self, request, *args, **kwargs):
"""GET请求处理"""
oauthid = self.kwargs['oauthid']
oauthuser = get_object_or_404(OAuthUser, pk=oauthid)
# 如果已有邮箱,直接跳过(这里注释掉了重定向逻辑)
if oauthuser.email:
pass
# return HttpResponseRedirect('/')
@ -184,6 +270,7 @@ class RequireEmailView(FormView):
return super(RequireEmailView, self).get(request, *args, **kwargs)
def get_initial(self):
"""获取表单初始数据"""
oauthid = self.kwargs['oauthid']
return {
'email': '',
@ -191,41 +278,49 @@ class RequireEmailView(FormView):
}
def get_context_data(self, **kwargs):
"""获取模板上下文数据"""
oauthid = self.kwargs['oauthid']
oauthuser = get_object_or_404(OAuthUser, pk=oauthid)
# 添加用户头像到上下文
if oauthuser.picture:
kwargs['picture'] = oauthuser.picture
return super(RequireEmailView, self).get_context_data(**kwargs)
def form_valid(self, form):
"""表单验证通过后的处理"""
email = form.cleaned_data['email']
oauthid = form.cleaned_data['oauthid']
oauthuser = get_object_or_404(OAuthUser, pk=oauthid)
oauthuser.email = email
oauthuser.save()
# 生成安全签名
sign = get_sha256(settings.SECRET_KEY +
str(oauthuser.id) + settings.SECRET_KEY)
site = get_current_site().domain
if settings.DEBUG:
site = '127.0.0.1:8000'
# 构建邮箱确认URL
path = reverse('oauth:email_confirm', kwargs={
'id': oauthid,
'sign': sign
})
url = "http://{site}{path}".format(site=site, path=path)
# 发送确认邮件
content = _("""
<p>Please click the link below to bind your email</p>
<p>请点击下面的链接完成邮箱绑定</p>
<a href="%(url)s" rel="bookmark">%(url)s</a>
Thank you again!
再次感谢
<br />
如果上面的链接无法打开请将此链接复制到浏览器
<br />
If the link above cannot be opened, please copy this link to your browser.
<br />
%(url)s
""") % {'url': url}
send_email(emailto=[email, ], title=_('Bind your email'), content=content)
send_email(emailto=[email, ], title=_('绑定邮箱'), content=content)
# 跳转到绑定成功提示页面
url = reverse('oauth:bindsuccess', kwargs={
'oauthid': oauthid
})
@ -234,20 +329,31 @@ class RequireEmailView(FormView):
def bindsuccess(request, oauthid):
"""
绑定成功页面 - 显示绑定状态信息
Args:
request: HTTP请求对象
oauthid: OAuth用户ID
Returns:
HttpResponse: 渲染的响应
"""
type = request.GET.get('type', None)
oauthuser = get_object_or_404(OAuthUser, pk=oauthid)
# 根据类型显示不同的提示信息
if type == 'email':
title = _('Bind your email')
title = _('绑定邮箱')
content = _(
'Congratulations, the binding is just one step away. '
'Please log in to your email to check the email to complete the binding. Thank you.')
'恭喜您,绑定只差一步之遥。请登录您的邮箱查看邮件完成绑定。谢谢。')
else:
title = _('Binding successful')
title = _('绑定成功')
content = _(
"Congratulations, you have successfully bound your email address. You can use %(oauthuser_type)s"
" to directly log in to this website without a password. You are welcome to continue to follow this site." % {
"恭喜您,您已成功绑定邮箱地址。您可以使用%(oauthuser_type)s直接登录本站,无需密码。欢迎您继续关注本站。" % {
'oauthuser_type': oauthuser.type})
return render(request, 'oauth/bindsuccess.html', {
'title': title,
'content': content
})
})
Loading…
Cancel
Save