You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
django/src%2FDjangoBlog-master%2Fa.../views.py

283 lines
10 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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__)
class RegisterView(FormView):
"""
用户注册视图
处理用户注册流程,包括表单验证、用户创建和发送验证邮件
"""
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) # 保存用户到数据库
# 获取当前站点信息
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'
# 构建验证URL
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>
<a href="{url}" rel="bookmark">{url}</a>
再次感谢您!
<br />
如果上面链接无法打开,请将此链接复制至浏览器。
{url}
""".format(url=url)
# 发送验证邮件
send_email(
emailto=[
user.email,
],
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
@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) # 执行登出操作
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) # 禁止缓存
def dispatch(self, request, *args, **kwargs):
"""应用装饰器到视图分发方法"""
return super(LoginView, self).dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
"""向模板上下文添加重定向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):
"""处理有效的登录表单"""
# 使用Django的AuthenticationForm进行认证
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)
return super(LoginView, self).form_valid(form)
else:
# 认证失败,重新显示表单
return self.render_to_response({
'form': form
})
def get_success_url(self):
"""获取登录成功后重定向的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使用默认URL
return redirect_to
def account_result(request):
"""
账户操作结果页面
处理注册结果和邮箱验证
"""
type = request.GET.get('type') # 操作类型register或validation
id = request.GET.get('id') # 用户ID
# 获取用户对象如果不存在返回404
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'
def form_valid(self, form):
"""处理有效的密码重置表单"""
if form.is_valid():
# 根据邮箱查找用户
blog_user = BlogUser.objects.filter(email=form.cleaned_data.get("email")).get()
# 使用Django的密码哈希器设置新密码
blog_user.password = make_password(form.cleaned_data["new_password2"])
blog_user.save() # 保存新密码
# 重定向到登录页面
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("错误的邮箱")
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") # 返回成功响应