diff --git a/src/DjangoBlog-master(1)/DjangoBlog-master/oauth/oauthmanager.py b/src/DjangoBlog-master(1)/DjangoBlog-master/oauth/oauthmanager.py index 2e7ceef..23f4315 100644 --- a/src/DjangoBlog-master(1)/DjangoBlog-master/oauth/oauthmanager.py +++ b/src/DjangoBlog-master(1)/DjangoBlog-master/oauth/oauthmanager.py @@ -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 \ No newline at end of file diff --git a/src/DjangoBlog-master(1)/DjangoBlog-master/oauth/urls.py b/src/DjangoBlog-master(1)/DjangoBlog-master/oauth/urls.py index c4a12a0..180fe81 100644 --- a/src/DjangoBlog-master(1)/DjangoBlog-master/oauth/urls.py +++ b/src/DjangoBlog-master(1)/DjangoBlog-master/oauth/urls.py @@ -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/.html', - views.RequireEmailView.as_view(), - name='require_email'), + r'oauth/requireemail/.html', # 带oauthid参数的URL + views.RequireEmailView.as_view(), # 类视图,处理邮箱输入 + name='require_email'), # URL名称,用于反向解析 + + # 邮箱确认 - 验证用户输入的邮箱地址 path( - r'oauth/emailconfirm//.html', - views.emailconfirm, - name='email_confirm'), + r'oauth/emailconfirm//.html', # 带id和签名参数的URL + views.emailconfirm, # 视图函数,处理邮箱确认 + name='email_confirm'), # URL名称,用于反向解析 + + # 绑定成功页面 - 显示第三方账号绑定成功信息 path( - r'oauth/bindsuccess/.html', - views.bindsuccess, - name='bindsuccess'), + r'oauth/bindsuccess/.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名称,用于反向解析 +] diff --git a/src/DjangoBlog-master(1)/DjangoBlog-master/oauth/views.py b/src/DjangoBlog-master(1)/DjangoBlog-master/oauth/views.py index 12e3a6e..e08c39b 100644 --- a/src/DjangoBlog-master(1)/DjangoBlog-master/oauth/views.py +++ b/src/DjangoBlog-master(1)/DjangoBlog-master/oauth/views.py @@ -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 = _(''' -

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, the address is - %(site)s - Thank you again! +

恭喜您,您已成功绑定邮箱。您可以使用%(oauthuser_type)s直接登录本站,无需密码。

+ 欢迎您继续关注本站,地址是%(site)s + 再次感谢!
- 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 = _(""" -

Please click the link below to bind your email

- +

请点击下面的链接完成邮箱绑定

%(url)s - - Thank you again! + 再次感谢! +
+ 如果上面的链接无法打开,请将此链接复制到浏览器。
- If the link above cannot be opened, please copy this link to your browser. -
%(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 - }) + }) \ No newline at end of file