diff --git a/src/DjangoBlog-master(1)/DjangoBlog-master/accounts/views.py b/src/DjangoBlog-master(1)/DjangoBlog-master/accounts/views.py index ae67aec..f10cb3f 100644 --- a/src/DjangoBlog-master(1)/DjangoBlog-master/accounts/views.py +++ b/src/DjangoBlog-master(1)/DjangoBlog-master/accounts/views.py @@ -1,3 +1,14 @@ +# -*- coding: utf-8 -*- +""" +# 模块级注释(app: accounts) +作者:djq +功能:用户账户相关视图逻辑,包含用户注册、登录、登出、密码找回及邮箱验证等功能 +关联: +- 表单:RegisterForm(注册表单)、LoginForm(登录表单)等 +- 模型:BlogUser(自定义用户模型) +- 模板:account/registration_form.html(注册页)、account/login.html(登录页)等 +- 工具函数:djangoblog.utils中的邮件发送、加密等工具 +""" import logging from django.utils.translation import gettext_lazy as _ from django.conf import settings @@ -32,28 +43,55 @@ logger = logging.getLogger(__name__) # Create your views here. class RegisterView(FormView): - form_class = RegisterForm - template_name = 'account/registration_form.html' + """ + 功能:处理用户注册逻辑,包括表单验证、创建未激活用户、发送邮箱验证链接 + 继承:FormView(Django表单处理基类) + 核心流程: + 1. 验证注册表单数据 + 2. 创建用户并设置为未激活状态 + 3. 生成邮箱验证链接(包含用户ID和加密签名) + 4. 发送验证邮件到用户邮箱 + 5. 重定向到注册结果页 + """ + form_class = RegisterForm # 关联注册表单类 + template_name = 'account/registration_form.html' # 注册页面模板 @method_decorator(csrf_protect) def dispatch(self, *args, **kwargs): + """ + 功能:重写dispatch方法,添加CSRF保护装饰器(防止跨站请求伪造) + 参数:*args, **kwargs:视图函数的位置参数和关键字参数 + 返回:父类dispatch方法的处理结果 + """ return super(RegisterView, self).dispatch(*args, **kwargs) def form_valid(self, form): + """ + 功能:当表单验证通过后执行的逻辑(核心注册流程) + 参数:form:验证通过的注册表单实例 + 返回:重定向到注册结果页的响应 + """ if form.is_valid(): + # djq: 创建用户但不立即保存(先设置额外属性) user = form.save(False) - user.is_active = False - user.source = 'Register' - user.save(True) + user.is_active = False # djq: 新用户默认未激活(需邮箱验证) + user.source = 'Register' # djq: 标记用户来源为自主注册 + user.save(True) # djq: 保存用户到数据库 + + # djq: 获取当前站点域名(用于构建验证链接) site = get_current_site().domain + # djq: 生成加密签名(结合SECRET_KEY和用户ID,防止链接被篡改) sign = get_sha256(get_sha256(settings.SECRET_KEY + str(user.id))) + # djq: 开发环境下使用本地域名 if settings.DEBUG: site = '127.0.0.1:8000' - path = reverse('account:result') + path = reverse('account:result') # djq: 验证结果页的路由 + # djq: 拼接邮箱验证链接(包含用户ID和签名) url = "http://{site}{path}?type=validation&id={id}&sign={sign}".format( site=site, path=path, id=user.id, sign=sign) + # djq: 验证邮件内容(包含验证链接) content = """
请点击下面链接验证您的邮箱
@@ -64,6 +102,7 @@ class RegisterView(FormView): 如果上面链接无法打开,请将此链接复制至浏览器。 {url} """.format(url=url) + # djq: 发送验证邮件到用户注册邮箱 send_email( emailto=[ user.email, @@ -71,134 +110,234 @@ class RegisterView(FormView): title='验证您的电子邮箱', content=content) + # djq: 重定向到注册结果页(提示用户查收验证邮件) url = reverse('accounts:result') + \ '?type=register&id=' + str(user.id) return HttpResponseRedirect(url) else: + # djq: 表单验证失败时,返回原页面并显示错误 return self.render_to_response({ 'form': form }) class LogoutView(RedirectView): - url = '/login/' + """ + 功能:处理用户登出逻辑,清除会话并跳转至登录页 + 继承:RedirectView(Django重定向基类) + """ + url = '/login/' # 登出后跳转的目标URL(登录页) @method_decorator(never_cache) def dispatch(self, request, *args, **kwargs): + """ + 功能:重写dispatch方法,添加禁止缓存装饰器(确保每次登出请求都是最新的) + 参数:*args, **kwargs:视图函数的参数 + 返回:父类dispatch方法的处理结果 + """ return super(LogoutView, self).dispatch(request, *args, **kwargs) def get(self, request, *args, **kwargs): - logout(request) - delete_sidebar_cache() + """ + 功能:处理GET请求,执行登出操作 + 参数:request:HTTP请求对象 + 返回:重定向到登录页的响应 + """ + logout(request) # djq: 清除用户会话,完成登出 + delete_sidebar_cache() # djq: 清除侧边栏缓存(可能包含用户相关信息) return super(LogoutView, self).get(request, *args, **kwargs) class LoginView(FormView): - form_class = LoginForm - template_name = 'account/login.html' - success_url = '/' - redirect_field_name = REDIRECT_FIELD_NAME - login_ttl = 2626560 # 一个月的时间 + """ + 功能:处理用户登录逻辑,包括表单验证、用户认证、会话管理 + 继承:FormView(Django表单处理基类) + 核心流程: + 1. 验证登录表单(用户名/邮箱+密码) + 2. 认证用户身份 + 3. 根据"记住我"选项设置会话过期时间 + 4. 重定向到登录前的页面或首页 + """ + form_class = LoginForm # 关联登录表单类 + template_name = 'account/login.html' # 登录页面模板 + success_url = '/' # 登录成功默认跳转页(首页) + redirect_field_name = REDIRECT_FIELD_NAME # 存储登录前URL的参数名 + login_ttl = 2626560 # djq: 会话过期时间(单位:秒),此处为一个月 @method_decorator(sensitive_post_parameters('password')) @method_decorator(csrf_protect) @method_decorator(never_cache) def dispatch(self, request, *args, **kwargs): - + """ + 功能:重写dispatch方法,添加多重保护装饰器 + - sensitive_post_parameters:标记密码字段为敏感信息(日志中隐藏) + - csrf_protect:CSRF保护 + - never_cache:禁止缓存 + 参数:*args, **kwargs:视图函数的参数 + 返回:父类dispatch方法的处理结果 + """ return super(LoginView, self).dispatch(request, *args, **kwargs) - def get_context_data(self, **kwargs): + def get_context_data(self,** kwargs): + """ + 功能:向模板传递额外上下文数据(登录前的跳转URL) + 参数:**kwargs:上下文关键字参数 + 返回:包含跳转URL的上下文字典 + """ + # djq: 获取登录前的页面URL(从请求参数中提取) redirect_to = self.request.GET.get(self.redirect_field_name) if redirect_to is None: - redirect_to = '/' - kwargs['redirect_to'] = redirect_to + redirect_to = '/' # djq: 默认跳转至首页 + kwargs['redirect_to'] = redirect_to # djq: 将跳转URL添加到上下文 - return super(LoginView, self).get_context_data(**kwargs) + return super(LoginView, self).get_context_data(** kwargs) def form_valid(self, form): + """ + 功能:表单验证通过后执行的登录逻辑 + 参数:form:验证通过的登录表单实例 + 返回:重定向到目标页面的响应 + """ + # djq: 使用Django内置认证表单再次验证(兼容用户名/邮箱登录) form = AuthenticationForm(data=self.request.POST, request=self.request) if form.is_valid(): - delete_sidebar_cache() - logger.info(self.redirect_field_name) + delete_sidebar_cache() # djq: 清除侧边栏缓存(更新用户登录状态) + logger.info(self.redirect_field_name) # djq: 记录跳转参数名到日志 + # djq: 执行登录(将用户信息存入会话) auth.login(self.request, form.get_user()) + # djq: 如果勾选"记住我",设置会话过期时间为一个月 if self.request.POST.get("remember"): self.request.session.set_expiry(self.login_ttl) return super(LoginView, self).form_valid(form) - # return HttpResponseRedirect('/') else: + # djq: 表单验证失败(如密码错误),返回原页面显示错误 return self.render_to_response({ 'form': form }) def get_success_url(self): - + """ + 功能:确定登录成功后的跳转URL(优先跳转到登录前的页面) + 返回:安全的跳转URL + """ + # djq: 从POST参数中获取登录前的URL redirect_to = self.request.POST.get(self.redirect_field_name) + # djq: 验证跳转URL是否安全(防止跳转到外部恶意网站) if not url_has_allowed_host_and_scheme( url=redirect_to, allowed_hosts=[ self.request.get_host()]): - redirect_to = self.success_url + redirect_to = self.success_url # djq: 不安全则使用默认首页 return redirect_to def account_result(request): - type = request.GET.get('type') - id = request.GET.get('id') - + """ + 功能:处理注册结果和邮箱验证结果的展示 + 参数:request:HTTP请求对象 + 返回:渲染结果页面的响应 + 逻辑: + 1. 区分"注册成功"和"邮箱验证成功"两种场景 + 2. 验证场景合法性(如验证链接的签名是否有效) + 3. 展示对应结果信息 + """ + type = request.GET.get('type') # djq: 获取场景类型(register/validation) + id = request.GET.get('id') # djq: 获取用户ID + + # djq: 获取对应的用户(不存在则返回404) user = get_object_or_404(get_user_model(), id=id) - logger.info(type) + logger.info(type) # djq: 记录场景类型到日志 + + # djq: 如果用户已激活,直接跳转至首页(避免重复验证) if user.is_active: return HttpResponseRedirect('/') + if type and type in ['register', 'validation']: if type == 'register': + # djq: 注册成功场景:提示用户查收验证邮件 content = ''' 恭喜您注册成功,一封验证邮件已经发送到您的邮箱,请验证您的邮箱后登录本站。 ''' title = '注册成功' else: - c_sign = get_sha256(get_sha256(settings.SECRET_KEY + str(user.id))) - sign = request.GET.get('sign') + # djq: 邮箱验证场景:验证签名合法性 + c_sign = get_sha256(get_sha256(settings.SECRET_KEY + str(user.id))) # 计算正确签名 + sign = request.GET.get('sign') # 获取请求中的签名 if sign != c_sign: - return HttpResponseForbidden() + return HttpResponseForbidden() # djq: 签名不匹配,返回403禁止访问 + + # djq: 签名验证通过,激活用户 user.is_active = True user.save() + # djq: 提示用户验证成功 content = ''' 恭喜您已经成功的完成邮箱验证,您现在可以使用您的账号来登录本站。 ''' title = '验证成功' + # djq: 渲染结果页面 return render(request, 'account/result.html', { 'title': title, 'content': content }) else: + # djq: 场景类型不合法,跳转至首页 return HttpResponseRedirect('/') class ForgetPasswordView(FormView): - form_class = ForgetPasswordForm - template_name = 'account/forget_password.html' + """ + 功能:处理密码找回逻辑(通过邮箱验证后重置密码) + 继承:FormView(Django表单处理基类) + """ + form_class = ForgetPasswordForm # 关联密码找回表单 + template_name = 'account/forget_password.html' # 密码找回页面模板 def form_valid(self, form): + """ + 功能:表单验证通过后执行的密码重置逻辑 + 参数:form:验证通过的密码找回表单实例 + 返回:重定向到登录页的响应 + """ if form.is_valid(): + # djq: 根据邮箱获取用户(假设表单已验证邮箱存在) blog_user = BlogUser.objects.filter(email=form.cleaned_data.get("email")).get() + # djq: 加密新密码并保存(make_password自动处理哈希) blog_user.password = make_password(form.cleaned_data["new_password2"]) blog_user.save() + # djq: 密码重置成功,跳转至登录页 return HttpResponseRedirect('/login/') else: + # djq: 表单验证失败(如密码不一致),返回原页面 return self.render_to_response({'form': form}) class ForgetPasswordEmailCode(View): + """ + 功能:处理密码找回时的邮箱验证码发送逻辑 + 继承:View(Django基础视图类) + 核心流程: + 1. 验证邮箱格式 + 2. 生成随机验证码 + 3. 发送验证码到目标邮箱 + 4. 存储验证码(用于后续验证) + """ def post(self, request: HttpRequest): + """ + 功能:处理POST请求,发送密码找回验证码 + 参数:request:HTTP请求对象(包含邮箱参数) + 返回:"ok"字符串(成功)或错误提示 + """ + # djq: 验证请求中的邮箱格式 form = ForgetPasswordCodeForm(request.POST) if not form.is_valid(): - return HttpResponse("错误的邮箱") - to_email = form.cleaned_data["email"] + return HttpResponse("错误的邮箱") # djq: 邮箱格式错误,返回提示 + + to_email = form.cleaned_data["email"] # djq: 获取验证通过的邮箱 - code = generate_code() - utils.send_verify_email(to_email, code) - utils.set_code(to_email, code) + code = generate_code() # djq: 生成随机验证码 + utils.send_verify_email(to_email, code) # djq: 发送验证码到邮箱 + utils.set_code(to_email, code) # djq: 存储验证码(如存入缓存,用于后续校验) - return HttpResponse("ok") + return HttpResponse("ok") # djq: 发送成功,返回标识 \ No newline at end of file