|
|
|
|
@ -1,5 +1,3 @@
|
|
|
|
|
#hyt:
|
|
|
|
|
|
|
|
|
|
import logging
|
|
|
|
|
from django.utils.translation import gettext_lazy as _
|
|
|
|
|
from django.conf import settings
|
|
|
|
|
@ -28,57 +26,34 @@ 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):
|
|
|
|
|
"""
|
|
|
|
|
用户注册视图
|
|
|
|
|
|
|
|
|
|
处理新用户注册流程,包括表单验证、用户创建和邮箱验证邮件发送
|
|
|
|
|
注册后用户处于未激活状态,需要邮箱验证后才能登录
|
|
|
|
|
"""
|
|
|
|
|
form_class = RegisterForm # 注册表单类
|
|
|
|
|
template_name = 'account/registration_form.html' # 注册页面模板
|
|
|
|
|
form_class = RegisterForm
|
|
|
|
|
template_name = 'account/registration_form.html'
|
|
|
|
|
|
|
|
|
|
@method_decorator(csrf_protect)
|
|
|
|
|
def dispatch(self, *args, **kwargs):
|
|
|
|
|
"""
|
|
|
|
|
请求分发方法
|
|
|
|
|
|
|
|
|
|
添加CSRF保护装饰器,防止跨站请求伪造攻击
|
|
|
|
|
"""
|
|
|
|
|
return super(RegisterView, self).dispatch(*args, **kwargs)
|
|
|
|
|
|
|
|
|
|
def form_valid(self, form):
|
|
|
|
|
"""
|
|
|
|
|
表单验证通过处理
|
|
|
|
|
|
|
|
|
|
保存用户信息,发送邮箱验证邮件,跳转到结果页面
|
|
|
|
|
"""
|
|
|
|
|
if form.is_valid():
|
|
|
|
|
# 保存用户信息但不立即提交到数据库
|
|
|
|
|
user = form.save(False)
|
|
|
|
|
user.is_active = False # 设置用户为未激活状态
|
|
|
|
|
user.source = 'Register' # 标记用户来源为注册
|
|
|
|
|
user.save(True) # 保存到数据库
|
|
|
|
|
|
|
|
|
|
# 生成邮箱验证链接
|
|
|
|
|
user.is_active = False
|
|
|
|
|
user.source = 'Register'
|
|
|
|
|
user.save(True)
|
|
|
|
|
site = get_current_site().domain
|
|
|
|
|
sign = get_sha256(get_sha256(settings.SECRET_KEY + str(user.id)))
|
|
|
|
|
|
|
|
|
|
# 开发环境下使用本地地址
|
|
|
|
|
if settings.DEBUG:
|
|
|
|
|
site = '127.0.0.1:8000'
|
|
|
|
|
|
|
|
|
|
path = reverse('account:result')
|
|
|
|
|
url = "http://{site}{path}?type=validation&id={id}&sign={sign}".format(
|
|
|
|
|
site=site, path=path, id=user.id, sign=sign)
|
|
|
|
|
|
|
|
|
|
# 构建邮件内容
|
|
|
|
|
content = """
|
|
|
|
|
<p>请点击下面链接验证您的邮箱</p>
|
|
|
|
|
|
|
|
|
|
@ -89,7 +64,6 @@ class RegisterView(FormView):
|
|
|
|
|
如果上面链接无法打开,请将此链接复制至浏览器。
|
|
|
|
|
{url}
|
|
|
|
|
""".format(url=url)
|
|
|
|
|
# 发送验证邮件
|
|
|
|
|
send_email(
|
|
|
|
|
emailto=[
|
|
|
|
|
user.email,
|
|
|
|
|
@ -97,220 +71,134 @@ class RegisterView(FormView):
|
|
|
|
|
title='验证您的电子邮箱',
|
|
|
|
|
content=content)
|
|
|
|
|
|
|
|
|
|
# 跳转到注册结果页面
|
|
|
|
|
url = reverse('accounts:result') + \
|
|
|
|
|
'?type=register&id=' + str(user.id)
|
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
|
else:
|
|
|
|
|
# 表单验证失败,重新渲染表单页面
|
|
|
|
|
return self.render_to_response({
|
|
|
|
|
'form': form
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class LogoutView(RedirectView):
|
|
|
|
|
"""
|
|
|
|
|
用户退出登录视图
|
|
|
|
|
|
|
|
|
|
处理用户退出登录流程,清理会话和缓存,重定向到登录页面
|
|
|
|
|
"""
|
|
|
|
|
url = '/login/' # 退出后重定向的URL
|
|
|
|
|
url = '/login/'
|
|
|
|
|
|
|
|
|
|
@method_decorator(never_cache)
|
|
|
|
|
def dispatch(self, request, *args, **kwargs):
|
|
|
|
|
"""
|
|
|
|
|
请求分发方法
|
|
|
|
|
|
|
|
|
|
添加不缓存装饰器,确保退出后页面不被缓存
|
|
|
|
|
"""
|
|
|
|
|
return super(LogoutView, self).dispatch(request, *args, **kwargs)
|
|
|
|
|
|
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
|
|
|
"""
|
|
|
|
|
GET请求处理
|
|
|
|
|
|
|
|
|
|
执行退出登录操作,清理用户会话和侧边栏缓存
|
|
|
|
|
"""
|
|
|
|
|
logout(request) # Django内置退出登录方法
|
|
|
|
|
delete_sidebar_cache() # 清理侧边栏缓存
|
|
|
|
|
logout(request)
|
|
|
|
|
delete_sidebar_cache()
|
|
|
|
|
return super(LogoutView, self).get(request, *args, **kwargs)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class LoginView(FormView):
|
|
|
|
|
"""
|
|
|
|
|
用户登录视图
|
|
|
|
|
|
|
|
|
|
处理用户登录认证,支持记住登录状态和重定向功能
|
|
|
|
|
"""
|
|
|
|
|
form_class = LoginForm # 登录表单类
|
|
|
|
|
template_name = 'account/login.html' # 登录页面模板
|
|
|
|
|
success_url = '/' # 登录成功默认跳转URL
|
|
|
|
|
redirect_field_name = REDIRECT_FIELD_NAME # 重定向字段名
|
|
|
|
|
login_ttl = 2626560 # 记住登录状态的会话有效期(一个月)
|
|
|
|
|
|
|
|
|
|
@method_decorator(sensitive_post_parameters('password')) # 保护密码参数
|
|
|
|
|
@method_decorator(csrf_protect) # CSRF保护
|
|
|
|
|
@method_decorator(never_cache) # 禁止缓存
|
|
|
|
|
form_class = LoginForm
|
|
|
|
|
template_name = 'account/login.html'
|
|
|
|
|
success_url = '/'
|
|
|
|
|
redirect_field_name = REDIRECT_FIELD_NAME
|
|
|
|
|
login_ttl = 2626560 # 一个月的时间
|
|
|
|
|
|
|
|
|
|
@method_decorator(sensitive_post_parameters('password'))
|
|
|
|
|
@method_decorator(csrf_protect)
|
|
|
|
|
@method_decorator(never_cache)
|
|
|
|
|
def dispatch(self, request, *args, **kwargs):
|
|
|
|
|
"""
|
|
|
|
|
请求分发方法
|
|
|
|
|
|
|
|
|
|
添加安全相关的装饰器,保护登录过程的安全性
|
|
|
|
|
"""
|
|
|
|
|
return super(LoginView, self).dispatch(request, *args, **kwargs)
|
|
|
|
|
|
|
|
|
|
def get_context_data(self, **kwargs):
|
|
|
|
|
"""
|
|
|
|
|
获取模板上下文数据
|
|
|
|
|
|
|
|
|
|
处理重定向参数,确保登录后能正确跳转
|
|
|
|
|
"""
|
|
|
|
|
redirect_to = self.request.GET.get(self.redirect_field_name)
|
|
|
|
|
if redirect_to is None:
|
|
|
|
|
redirect_to = '/' # 默认重定向到首页
|
|
|
|
|
redirect_to = '/'
|
|
|
|
|
kwargs['redirect_to'] = redirect_to
|
|
|
|
|
|
|
|
|
|
return super(LoginView, self).get_context_data(**kwargs)
|
|
|
|
|
|
|
|
|
|
def form_valid(self, form):
|
|
|
|
|
"""
|
|
|
|
|
表单验证通过处理
|
|
|
|
|
|
|
|
|
|
执行用户登录认证,处理记住登录状态选项
|
|
|
|
|
"""
|
|
|
|
|
form = AuthenticationForm(data=self.request.POST, request=self.request)
|
|
|
|
|
|
|
|
|
|
if form.is_valid():
|
|
|
|
|
# 登录成功,清理缓存并记录日志
|
|
|
|
|
delete_sidebar_cache()
|
|
|
|
|
logger.info(self.redirect_field_name)
|
|
|
|
|
|
|
|
|
|
# 执行登录操作
|
|
|
|
|
auth.login(self.request, form.get_user())
|
|
|
|
|
|
|
|
|
|
# 处理"记住我"选项
|
|
|
|
|
if self.request.POST.get("remember"):
|
|
|
|
|
self.request.session.set_expiry(self.login_ttl) # 设置会话有效期
|
|
|
|
|
self.request.session.set_expiry(self.login_ttl)
|
|
|
|
|
return super(LoginView, self).form_valid(form)
|
|
|
|
|
# return HttpResponseRedirect('/')
|
|
|
|
|
else:
|
|
|
|
|
# 登录失败,重新显示表单
|
|
|
|
|
return self.render_to_response({
|
|
|
|
|
'form': form
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
def get_success_url(self):
|
|
|
|
|
"""
|
|
|
|
|
获取登录成功后的跳转URL
|
|
|
|
|
|
|
|
|
|
验证重定向URL的安全性,防止开放重定向攻击
|
|
|
|
|
"""
|
|
|
|
|
redirect_to = self.request.POST.get(self.redirect_field_name)
|
|
|
|
|
# 验证重定向URL是否安全
|
|
|
|
|
if not url_has_allowed_host_and_scheme(
|
|
|
|
|
url=redirect_to, allowed_hosts=[
|
|
|
|
|
self.request.get_host()]):
|
|
|
|
|
redirect_to = self.success_url # 不安全则使用默认URL
|
|
|
|
|
redirect_to = self.success_url
|
|
|
|
|
return redirect_to
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def account_result(request):
|
|
|
|
|
"""
|
|
|
|
|
账户操作结果页面视图
|
|
|
|
|
|
|
|
|
|
显示注册成功或邮箱验证成功的结果信息
|
|
|
|
|
处理邮箱验证链接的验证逻辑
|
|
|
|
|
"""
|
|
|
|
|
type = request.GET.get('type') # 操作类型:register或validation
|
|
|
|
|
id = request.GET.get('id') # 用户ID
|
|
|
|
|
type = request.GET.get('type')
|
|
|
|
|
id = request.GET.get('id')
|
|
|
|
|
|
|
|
|
|
user = get_object_or_404(get_user_model(), id=id)
|
|
|
|
|
logger.info(type)
|
|
|
|
|
|
|
|
|
|
# 如果用户已激活,直接跳转到首页
|
|
|
|
|
if user.is_active:
|
|
|
|
|
return HttpResponseRedirect('/')
|
|
|
|
|
|
|
|
|
|
# 处理注册结果或邮箱验证
|
|
|
|
|
if type and type in ['register', 'validation']:
|
|
|
|
|
if type == 'register':
|
|
|
|
|
# 注册成功页面
|
|
|
|
|
content = '''
|
|
|
|
|
恭喜您注册成功,一封验证邮件已经发送到您的邮箱,请验证您的邮箱后登录本站。
|
|
|
|
|
'''
|
|
|
|
|
title = '注册成功'
|
|
|
|
|
else:
|
|
|
|
|
# 邮箱验证处理
|
|
|
|
|
c_sign = get_sha256(get_sha256(settings.SECRET_KEY + str(user.id)))
|
|
|
|
|
sign = request.GET.get('sign')
|
|
|
|
|
# 验证签名防止篡改
|
|
|
|
|
if sign != c_sign:
|
|
|
|
|
return HttpResponseForbidden()
|
|
|
|
|
# 激活用户账户
|
|
|
|
|
user.is_active = True
|
|
|
|
|
user.save()
|
|
|
|
|
content = '''
|
|
|
|
|
恭喜您已经成功的完成邮箱验证,您现在可以使用您的账号来登录本站。
|
|
|
|
|
'''
|
|
|
|
|
title = '验证成功'
|
|
|
|
|
# 渲染结果页面
|
|
|
|
|
return render(request, 'account/result.html', {
|
|
|
|
|
'title': title,
|
|
|
|
|
'content': content
|
|
|
|
|
})
|
|
|
|
|
else:
|
|
|
|
|
# 无效的操作类型,跳转到首页
|
|
|
|
|
return HttpResponseRedirect('/')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ForgetPasswordView(FormView):
|
|
|
|
|
"""
|
|
|
|
|
忘记密码重置视图
|
|
|
|
|
|
|
|
|
|
处理用户忘记密码后的密码重置流程
|
|
|
|
|
验证验证码后更新用户密码
|
|
|
|
|
"""
|
|
|
|
|
form_class = ForgetPasswordForm # 忘记密码表单
|
|
|
|
|
template_name = 'account/forget_password.html' # 模板路径
|
|
|
|
|
form_class = ForgetPasswordForm
|
|
|
|
|
template_name = 'account/forget_password.html'
|
|
|
|
|
|
|
|
|
|
def form_valid(self, form):
|
|
|
|
|
"""
|
|
|
|
|
表单验证通过处理
|
|
|
|
|
|
|
|
|
|
重置用户密码并重定向到登录页面
|
|
|
|
|
"""
|
|
|
|
|
if form.is_valid():
|
|
|
|
|
# 获取用户并更新密码
|
|
|
|
|
blog_user = BlogUser.objects.filter(email=form.cleaned_data.get("email")).get()
|
|
|
|
|
blog_user.password = make_password(form.cleaned_data["new_password2"]) # 哈希密码
|
|
|
|
|
blog_user.password = make_password(form.cleaned_data["new_password2"])
|
|
|
|
|
blog_user.save()
|
|
|
|
|
return HttpResponseRedirect('/login/') # 重定向到登录页面
|
|
|
|
|
return HttpResponseRedirect('/login/')
|
|
|
|
|
else:
|
|
|
|
|
# 表单验证失败,重新显示表单
|
|
|
|
|
return self.render_to_response({'form': form})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ForgetPasswordEmailCode(View):
|
|
|
|
|
"""
|
|
|
|
|
忘记密码验证码发送视图
|
|
|
|
|
|
|
|
|
|
处理忘记密码流程中的验证码发送请求
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def post(self, request: HttpRequest):
|
|
|
|
|
"""
|
|
|
|
|
POST请求处理
|
|
|
|
|
|
|
|
|
|
验证邮箱地址并发送密码重置验证码
|
|
|
|
|
"""
|
|
|
|
|
form = ForgetPasswordCodeForm(request.POST)
|
|
|
|
|
if not form.is_valid():
|
|
|
|
|
return HttpResponse("错误的邮箱") # 邮箱格式错误
|
|
|
|
|
|
|
|
|
|
return HttpResponse("错误的邮箱")
|
|
|
|
|
to_email = form.cleaned_data["email"]
|
|
|
|
|
|
|
|
|
|
# 生成并发送验证码
|
|
|
|
|
code = generate_code()
|
|
|
|
|
utils.send_verify_email(to_email, code)
|
|
|
|
|
utils.set_code(to_email, code)
|
|
|
|
|
|
|
|
|
|
return HttpResponse("ok") # 发送成功
|
|
|
|
|
return HttpResponse("ok")
|
|
|
|
|
|