|
|
import logging
|
|
|
from django.utils.translation import gettext_lazy as _
|
|
|
from django.conf import settings
|
|
|
from django.contrib import auth
|
|
|
from django.contrib.auth import REDIRECT_FIELD_NAME
|
|
|
from django.contrib.auth import get_user_model
|
|
|
from django.contrib.auth import logout
|
|
|
from django.contrib.auth.forms import AuthenticationForm
|
|
|
from django.contrib.auth.hashers import make_password
|
|
|
from django.http import HttpResponseRedirect, HttpResponseForbidden
|
|
|
from django.http.request import HttpRequest
|
|
|
from django.http.response import HttpResponse
|
|
|
from django.shortcuts import get_object_or_404
|
|
|
from django.shortcuts import render
|
|
|
from django.urls import reverse
|
|
|
from django.utils.decorators import method_decorator
|
|
|
from django.utils.http import url_has_allowed_host_and_scheme
|
|
|
from django.views import View
|
|
|
from django.views.decorators.cache import never_cache
|
|
|
from django.views.decorators.csrf import csrf_protect
|
|
|
from django.views.decorators.debug import sensitive_post_parameters
|
|
|
from django.views.generic import FormView, RedirectView
|
|
|
|
|
|
from djangoblog.utils import send_email, get_sha256, get_current_site, generate_code, delete_sidebar_cache
|
|
|
from . import utils
|
|
|
from .forms import RegisterForm, LoginForm, ForgetPasswordForm, ForgetPasswordCodeForm
|
|
|
from .models import BlogUser
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
# Create your views here.
|
|
|
|
|
|
class RegisterView(FormView):
|
|
|
#sjt 用户注册视图:处理注册表单提交,发送邮箱验证链接
|
|
|
form_class = RegisterForm # sjt 注册表单类
|
|
|
template_name = 'account/registration_form.html' # sjt 注册页面模板
|
|
|
|
|
|
@method_decorator(csrf_protect)
|
|
|
def dispatch(self, *args, **kwargs):
|
|
|
#sjt 处理请求分发,添加CSRF保护
|
|
|
return super(RegisterView, self).dispatch(*args, **kwargs)
|
|
|
|
|
|
def form_valid(self, form):
|
|
|
#sjt 表单验证通过后:创建未激活用户,生成验证链接并发送邮件
|
|
|
if form.is_valid():
|
|
|
user = form.save(False) # sjt 不立即保存用户
|
|
|
user.is_active = False # sjt 初始为未激活状态(需邮箱验证)
|
|
|
user.source = 'Register' # sjt 标记来源为注册
|
|
|
user.save(True) # sjt 保存用户
|
|
|
site = get_current_site().domain # sjt 获取当前站点域名
|
|
|
# sjt 生成双重加密签名(防止链接篡改)
|
|
|
sign = get_sha256(get_sha256(settings.SECRET_KEY + str(user.id)))
|
|
|
|
|
|
if settings.DEBUG: # sjt 开发环境下使用本地域名
|
|
|
site = '127.0.0.1:8000'
|
|
|
path = reverse('account:result') # sjt 获取结果页路由
|
|
|
# sjt 构建验证链接
|
|
|
url = "http://{site}{path}?type=validation&id={id}&sign={sign}".format(
|
|
|
site=site, path=path, id=user.id, sign=sign)
|
|
|
|
|
|
# sjt 构建验证邮件内容
|
|
|
content = """
|
|
|
<p>请点击下面链接验证您的邮箱</p>
|
|
|
|
|
|
<a href="{url}" rel="bookmark">{url}</a>
|
|
|
|
|
|
再次感谢您!
|
|
|
<br />
|
|
|
如果上面链接无法打开,请将此链接复制至浏览器。
|
|
|
{url}
|
|
|
""".format(url=url)
|
|
|
# sjt 发送验证邮件
|
|
|
send_email(
|
|
|
emailto=[
|
|
|
user.email,
|
|
|
],
|
|
|
title='验证您的电子邮箱',
|
|
|
content=content)
|
|
|
|
|
|
# sjt 重定向到注册结果页
|
|
|
url = reverse('accounts:result') + \
|
|
|
'?type=register&id=' + str(user.id)
|
|
|
return HttpResponseRedirect(url)
|
|
|
else:
|
|
|
return self.render_to_response({
|
|
|
'form': form
|
|
|
})
|
|
|
|
|
|
|
|
|
class LogoutView(RedirectView):
|
|
|
#sjt 用户注销视图:处理用户注销并跳转登录页
|
|
|
url = '/login/' # sjt 注销后跳转的URL
|
|
|
|
|
|
@method_decorator(never_cache)
|
|
|
def dispatch(self, request, *args, **kwargs):
|
|
|
#sjt 处理请求分发,禁止缓存
|
|
|
return super(LogoutView, self).dispatch(request, *args, **kwargs)
|
|
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
|
#sjt 处理GET请求:执行注销操作并清除缓存
|
|
|
logout(request) # sjt 注销用户
|
|
|
delete_sidebar_cache() # sjt 删除侧边栏缓存
|
|
|
return super(LogoutView, self).get(request, *args, **kwargs)
|
|
|
|
|
|
|
|
|
class LoginView(FormView):
|
|
|
#sjt 用户登录视图:支持用户名/邮箱登录,处理登录状态
|
|
|
form_class = LoginForm # sjt 登录表单类
|
|
|
template_name = 'account/login.html' # sjt 登录页面模板
|
|
|
success_url = '/' # sjt 登录成功默认跳转页
|
|
|
redirect_field_name = REDIRECT_FIELD_NAME # sjt 重定向字段名
|
|
|
login_ttl = 2626560 # sjt 记住登录状态的有效期(一个月,单位秒)
|
|
|
|
|
|
@method_decorator(sensitive_post_parameters('password'))
|
|
|
@method_decorator(csrf_protect)
|
|
|
@method_decorator(never_cache)
|
|
|
def dispatch(self, request, *args, **kwargs):
|
|
|
#sjt 处理请求分发:保护敏感参数、CSRF保护、禁止缓存
|
|
|
return super(LoginView, self).dispatch(request, *args, **kwargs)
|
|
|
|
|
|
def get_context_data(self, **kwargs):
|
|
|
#sjt 构建上下文数据:获取重定向URL
|
|
|
redirect_to = self.request.GET.get(self.redirect_field_name)
|
|
|
if redirect_to is None:
|
|
|
redirect_to = '/'
|
|
|
kwargs['redirect_to'] = redirect_to
|
|
|
|
|
|
return super(LoginView, self).get_context_data(** kwargs)
|
|
|
|
|
|
def form_valid(self, form):
|
|
|
#sjt 表单验证通过后:验证用户凭据,处理登录状态(含"记住我"功能)
|
|
|
form = AuthenticationForm(data=self.request.POST, request=self.request)
|
|
|
|
|
|
if form.is_valid(): # sjt 验证用户凭据
|
|
|
delete_sidebar_cache() # sjt 删除侧边栏缓存
|
|
|
logger.info(self.redirect_field_name)
|
|
|
|
|
|
auth.login(self.request, form.get_user()) # sjt 登录用户
|
|
|
if self.request.POST.get("remember"): # sjt 如果勾选"记住我"
|
|
|
self.request.session.set_expiry(self.login_ttl) # sjt 设置会话有效期
|
|
|
return super(LoginView, self).form_valid(form)
|
|
|
else:
|
|
|
return self.render_to_response({
|
|
|
'form': form
|
|
|
})
|
|
|
|
|
|
def get_success_url(self):
|
|
|
#sjt 获取登录成功后的跳转URL(验证安全性)
|
|
|
redirect_to = self.request.POST.get(self.redirect_field_name)
|
|
|
# sjt 验证跳转URL是否安全
|
|
|
if not url_has_allowed_host_and_scheme(
|
|
|
url=redirect_to, allowed_hosts=[
|
|
|
self.request.get_host()]):
|
|
|
redirect_to = self.success_url
|
|
|
return redirect_to
|
|
|
|
|
|
|
|
|
def account_result(request):
|
|
|
#sjt 注册/验证结果页:处理邮箱验证逻辑,激活用户账号
|
|
|
type = request.GET.get('type') # sjt 获取操作类型(注册/验证)
|
|
|
id = request.GET.get('id') # sjt 获取用户ID
|
|
|
|
|
|
user = get_object_or_404(get_user_model(), id=id) # sjt 获取用户对象
|
|
|
logger.info(type)
|
|
|
if user.is_active: # sjt 如果用户已激活,直接跳转首页
|
|
|
return HttpResponseRedirect('/')
|
|
|
if type and type in ['register', 'validation']:
|
|
|
if type == 'register': # sjt 注册成功结果页
|
|
|
content = '''
|
|
|
恭喜您注册成功,一封验证邮件已经发送到您的邮箱,请验证您的邮箱后登录本站。
|
|
|
'''
|
|
|
title = '注册成功'
|
|
|
else: # sjt 邮箱验证结果页
|
|
|
# sjt 验证签名是否正确
|
|
|
c_sign = get_sha256(get_sha256(settings.SECRET_KEY + str(user.id)))
|
|
|
sign = request.GET.get('sign')
|
|
|
if sign != c_sign: # sjt 签名错误返回403
|
|
|
return HttpResponseForbidden()
|
|
|
user.is_active = True # sjt 激活用户
|
|
|
user.save()
|
|
|
content = '''
|
|
|
恭喜您已经成功的完成邮箱验证,您现在可以使用您的账号来登录本站。
|
|
|
'''
|
|
|
title = '验证成功'
|
|
|
return render(request, 'account/result.html', { # sjt 渲染结果页
|
|
|
'title': title,
|
|
|
'content': content
|
|
|
})
|
|
|
else:
|
|
|
return HttpResponseRedirect('/')
|
|
|
|
|
|
|
|
|
class ForgetPasswordView(FormView):
|
|
|
#sjt 密码找回视图:验证验证码后重置密码
|
|
|
form_class = ForgetPasswordForm # sjt 密码找回表单
|
|
|
template_name = 'account/forget_password.html' # sjt 密码找回页面模板
|
|
|
|
|
|
def form_valid(self, form):
|
|
|
#sjt 表单验证通过后:更新用户密码并跳转登录页
|
|
|
if form.is_valid():
|
|
|
blog_user = BlogUser.objects.filter(email=form.cleaned_data.get("email")).get()
|
|
|
# sjt 加密并更新密码
|
|
|
blog_user.password = make_password(form.cleaned_data["new_password2"])
|
|
|
blog_user.save()
|
|
|
return HttpResponseRedirect('/login/') # sjt 跳转登录页
|
|
|
else:
|
|
|
return self.render_to_response({'form': form})
|
|
|
|
|
|
|
|
|
class ForgetPasswordEmailCode(View):
|
|
|
#sjt 发送密码找回验证码视图:处理发送验证码请求
|
|
|
|
|
|
def post(self, request: HttpRequest):
|
|
|
#sjt 处理POST请求:验证邮箱并发送验证码
|
|
|
form = ForgetPasswordCodeForm(request.POST) # sjt 验证邮箱表单
|
|
|
if not form.is_valid(): # sjt 邮箱验证失败
|
|
|
return HttpResponse("错误的邮箱")
|
|
|
to_email = form.cleaned_data["email"] # sjt 获取目标邮箱
|
|
|
|
|
|
code = generate_code() # sjt 生成验证码
|
|
|
utils.send_verify_email(to_email, code) # sjt 发送验证码邮件
|
|
|
utils.set_code(to_email, code) # sjt 缓存验证码(有效期5分钟)
|
|
|
|
|
|
return HttpResponse("ok") # sjt 发送成功返回"ok" |