hyt_accounts APP注释

pull/27/head
hyt 3 months ago
parent 009e363737
commit e769baf8ca

@ -1,3 +1,5 @@
# hyt:
from django import forms
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.forms import UserChangeForm
@ -9,15 +11,27 @@ from .models import BlogUser
class BlogUserCreationForm(forms.ModelForm):
"""
博客用户创建表单
功能处理新用户注册时的数据验证和保存
扩展了Django标准用户创建流程添加密码确认和来源记录
"""
password1 = forms.CharField(label=_('password'), widget=forms.PasswordInput)
password2 = forms.CharField(label=_('Enter password again'), widget=forms.PasswordInput)
class Meta:
model = BlogUser
fields = ('email',)
fields = ('email',) # 创建用户时只需要邮箱字段
def clean_password2(self):
# Check that the two password entries match
"""
密码确认验证
功能验证两次输入的密码是否一致
返回验证通过的密码
异常当密码不匹配时抛出ValidationError
"""
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
@ -25,28 +39,50 @@ class BlogUserCreationForm(forms.ModelForm):
return password2
def save(self, commit=True):
# Save the provided password in hashed format
"""
保存用户信息
功能处理密码哈希化和用户来源记录
参数commit - 是否立即保存到数据库
返回保存后的用户对象
"""
user = super().save(commit=False)
user.set_password(self.cleaned_data["password1"])
user.set_password(self.cleaned_data["password1"]) # 密码哈希化
if commit:
user.source = 'adminsite'
user.source = 'adminsite' # 标记用户来源为管理后台
user.save()
return user
class BlogUserChangeForm(UserChangeForm):
"""
博客用户信息修改表单
功能处理用户信息的编辑和更新
继承自Django标准UserChangeForm支持所有字段编辑
"""
class Meta:
model = BlogUser
fields = '__all__'
field_classes = {'username': UsernameField}
fields = '__all__' # 包含所有字段
field_classes = {'username': UsernameField} # 用户名字段使用特定类型
def __init__(self, *args, **kwargs):
"""初始化表单,设置字段属性"""
super().__init__(*args, **kwargs)
class BlogUserAdmin(UserAdmin):
form = BlogUserChangeForm
add_form = BlogUserCreationForm
"""
博客用户管理后台配置
功能自定义Django管理后台的用户管理界面
扩展了默认的用户管理功能优化显示字段和排序
"""
form = BlogUserChangeForm # 使用自定义修改表单
add_form = BlogUserCreationForm # 使用自定义创建表单
# 列表页显示字段
list_display = (
'id',
'nickname',
@ -54,6 +90,8 @@ class BlogUserAdmin(UserAdmin):
'email',
'last_login',
'date_joined',
'source')
list_display_links = ('id', 'username')
ordering = ('-id',)
'source'
)
list_display_links = ('id', 'username') # 可点击链接的字段
ordering = ('-id',) # 按ID降序排列

@ -1,5 +1,13 @@
# hyt:
from django.apps import AppConfig
class AccountsConfig(AppConfig):
name = 'accounts'
"""
账户应用配置类
功能定义Django账户应用的配置信息
继承自AppConfig用于应用初始化和元数据配置
"""
name = 'accounts' # 应用名称对应INSTALLED_APPS中的配置

@ -1,3 +1,5 @@
# hyt:
from django import forms
from django.contrib.auth import get_user_model, password_validation
from django.contrib.auth.forms import AuthenticationForm, UserCreationForm
@ -9,18 +11,37 @@ from .models import BlogUser
class LoginForm(AuthenticationForm):
"""
用户登录表单
功能处理用户登录认证自定义表单控件样式
继承自Django标准AuthenticationForm添加Bootstrap样式支持
"""
def __init__(self, *args, **kwargs):
"""初始化表单,设置用户名和密码输入框的样式和占位符"""
super(LoginForm, self).__init__(*args, **kwargs)
# 设置用户名输入框样式
self.fields['username'].widget = widgets.TextInput(
attrs={'placeholder': "username", "class": "form-control"})
# 设置密码输入框样式
self.fields['password'].widget = widgets.PasswordInput(
attrs={'placeholder': "password", "class": "form-control"})
class RegisterForm(UserCreationForm):
"""
用户注册表单
功能处理新用户注册包含用户名邮箱和密码验证
扩展Django标准UserCreationForm添加邮箱验证和表单样式
"""
def __init__(self, *args, **kwargs):
"""初始化表单设置所有字段的Bootstrap样式和占位符"""
super(RegisterForm, self).__init__(*args, **kwargs)
# 设置各字段的表单控件样式
self.fields['username'].widget = widgets.TextInput(
attrs={'placeholder': "username", "class": "form-control"})
self.fields['email'].widget = widgets.EmailInput(
@ -31,17 +52,33 @@ class RegisterForm(UserCreationForm):
attrs={'placeholder': "repeat password", "class": "form-control"})
def clean_email(self):
"""
邮箱唯一性验证
功能验证邮箱是否已被注册
返回验证通过的邮箱
异常邮箱已存在时抛出ValidationError
"""
email = self.cleaned_data['email']
if get_user_model().objects.filter(email=email).exists():
raise ValidationError(_("email already exists"))
return email
class Meta:
model = get_user_model()
fields = ("username", "email")
"""表单元数据配置"""
model = get_user_model() # 使用当前激活的用户模型
fields = ("username", "email") # 表单包含的字段
class ForgetPasswordForm(forms.Form):
"""
忘记密码重置表单
功能处理密码重置流程包含邮箱验证验证码校验和新密码设置
用于用户通过邮箱和验证码找回密码的场景
"""
# 新密码字段
new_password1 = forms.CharField(
label=_("New password"),
widget=forms.PasswordInput(
@ -52,6 +89,7 @@ class ForgetPasswordForm(forms.Form):
),
)
# 确认密码字段
new_password2 = forms.CharField(
label="确认密码",
widget=forms.PasswordInput(
@ -62,6 +100,7 @@ class ForgetPasswordForm(forms.Form):
),
)
# 邮箱字段
email = forms.EmailField(
label='邮箱',
widget=forms.TextInput(
@ -72,6 +111,7 @@ class ForgetPasswordForm(forms.Form):
),
)
# 验证码字段
code = forms.CharField(
label=_('Code'),
widget=forms.TextInput(
@ -83,24 +123,43 @@ class ForgetPasswordForm(forms.Form):
)
def clean_new_password2(self):
"""
新密码确认验证
功能验证两次输入的新密码是否一致并符合密码策略
返回验证通过的密码
异常密码不匹配或不符合策略时抛出ValidationError
"""
password1 = self.data.get("new_password1")
password2 = self.data.get("new_password2")
if password1 and password2 and password1 != password2:
raise ValidationError(_("passwords do not match"))
password_validation.validate_password(password2)
password_validation.validate_password(password2) # Django密码策略验证
return password2
def clean_email(self):
"""
邮箱存在性验证
功能验证邮箱是否在系统中注册
返回验证通过的邮箱
异常邮箱未注册时抛出ValidationError
"""
user_email = self.cleaned_data.get("email")
if not BlogUser.objects.filter(
email=user_email
).exists():
# todo 这里的报错提示可以判断一个邮箱是不是注册过,如果不想暴露可以修改
if not BlogUser.objects.filter(email=user_email).exists():
# 安全提示:这里的报错会暴露邮箱是否注册,可根据安全需求调整
raise ValidationError(_("email does not exist"))
return user_email
def clean_code(self):
"""
验证码校验
功能验证邮箱验证码的有效性
返回验证通过的验证码
异常验证码无效时抛出ValidationError
"""
code = self.cleaned_data.get("code")
error = utils.verify(
email=self.cleaned_data.get("email"),
@ -112,6 +171,13 @@ class ForgetPasswordForm(forms.Form):
class ForgetPasswordCodeForm(forms.Form):
"""
忘记密码验证码请求表单
功能用于请求发送密码重置验证码仅包含邮箱字段
简化表单专门用于验证码发送流程
"""
email = forms.EmailField(
label=_('Email'),
label=_('Email'), # 邮箱标签
)

@ -1,104 +0,0 @@
#hyt:
# Generated by Django 4.1.7 on 2023-03-02 07:14
import django.contrib.auth.models
import django.contrib.auth.validators
from django.db import migrations, models
import django.utils.timezone
class Migration(migrations.Migration):
"""
BlogUser 模型的初始迁移文件
创建自定义用户模型 BlogUser扩展 Django 内置 User 模型
添加了昵称时间戳和来源字段支持中文显示和自定义排序
"""
initial = True
dependencies = [
('auth', '0012_alter_user_first_name_max_length'),
]
operations = [
migrations.CreateModel(
name='BlogUser',
fields=[
# 主键字段 - 使用 BigAutoField 作为自增主键
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
# 认证相关字段 - Django 内置用户认证系统必需字段
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
# 权限相关字段 - 用户权限和状态管理
('is_superuser', models.BooleanField(
default=False,
help_text='Designates that this user has all permissions without explicitly assigning them.',
verbose_name='superuser status'
)),
# 用户基本信息字段 - 用户名、姓名、邮箱等
('username', models.CharField(
error_messages={'unique': 'A user with that username already exists.'},
help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.',
max_length=150,
unique=True,
validators=[django.contrib.auth.validators.UnicodeUsernameValidator()],
verbose_name='username'
)),
('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
# 状态字段 - 用户状态标识
('is_staff', models.BooleanField(
default=False,
help_text='Designates whether the user can log into this admin site.',
verbose_name='staff status'
)),
('is_active', models.BooleanField(
default=True,
help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.',
verbose_name='active'
)),
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
# 自定义扩展字段 - 博客用户特有字段
('nickname', models.CharField(blank=True, max_length=100, verbose_name='昵称')),
('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')),
('last_mod_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='修改时间')),
('source', models.CharField(blank=True, max_length=100, verbose_name='创建来源')),
# 权限关联字段 - 用户组和权限的多对多关系
('groups', models.ManyToManyField(
blank=True,
help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.',
related_name='user_set',
related_query_name='user',
to='auth.group',
verbose_name='groups'
)),
('user_permissions', models.ManyToManyField(
blank=True,
help_text='Specific permissions for this user.',
related_name='user_set',
related_query_name='user',
to='auth.permission',
verbose_name='user permissions'
)),
],
options={
# 模型元选项 - 定义模型在 admin 中的显示和排序
'verbose_name': '用户', # 单数显示名称
'verbose_name_plural': '用户', # 复数显示名称
'ordering': ['-id'], # 按 ID 降序排列
'get_latest_by': 'id', # 获取最新记录的依据字段
},
managers=[
# 模型管理器 - 使用 Django 默认的用户管理器
('objects', django.contrib.auth.models.UserManager()),
],
),
]

@ -1,81 +0,0 @@
#hyt:
# Generated by Django 4.2.5 on 2023-09-06 13:13
from django.db import migrations, models
import django.utils.timezone
class Migration(migrations.Migration):
"""
BlogUser 模型结构调整迁移文件
accounts 应用的 BlogUser 模型进行字段优化和国际化改进
1. 调整时间字段命名统一使用英文命名规范
2. 更新模型选项改进 Admin 后台显示
3. 字段标签国际化为多语言支持做准备
"""
dependencies = [
('accounts', '0001_initial'), # 依赖于初始迁移文件
]
operations = [
# 模型选项调整 - 更新 Admin 后台显示配置
migrations.AlterModelOptions(
name='bloguser',
options={
'get_latest_by': 'id', # 按 ID 获取最新记录
'ordering': ['-id'], # 按 ID 降序排列
'verbose_name': 'user', # 单数显示名称(英文)
'verbose_name_plural': 'user', # 复数显示名称(英文)
},
),
# 字段清理 - 移除旧的时间字段
migrations.RemoveField(
model_name='bloguser',
name='created_time', # 移除旧的创建时间字段
),
migrations.RemoveField(
model_name='bloguser',
name='last_mod_time', # 移除旧的修改时间字段
),
# 字段添加 - 新增标准化时间字段
migrations.AddField(
model_name='bloguser',
name='creation_time',
field=models.DateTimeField(
default=django.utils.timezone.now, # 默认值为当前时间
verbose_name='creation time' # 字段显示名称(英文)
),
),
migrations.AddField(
model_name='bloguser',
name='last_modify_time',
field=models.DateTimeField(
default=django.utils.timezone.now, # 默认值为当前时间
verbose_name='last modify time' # 字段显示名称(英文)
),
),
# 字段调整 - 更新字段标签为英文
migrations.AlterField(
model_name='bloguser',
name='nickname',
field=models.CharField(
blank=True, # 允许为空
max_length=100, # 最大长度100字符
verbose_name='nick name' # 字段显示名称(英文)
),
),
migrations.AlterField(
model_name='bloguser',
name='source',
field=models.CharField(
blank=True, # 允许为空
max_length=100, # 最大长度100字符
verbose_name='create source' # 字段显示名称(英文)
),
),
]

@ -1,3 +1,5 @@
# hyt:
from django.contrib.auth.models import AbstractUser
from django.db import models
from django.urls import reverse
@ -9,27 +11,64 @@ from djangoblog.utils import get_current_site
# Create your models here.
class BlogUser(AbstractUser):
"""
博客用户模型
功能扩展Django标准用户模型添加博客系统特有字段
继承自AbstractUser包含认证系统基础字段和自定义业务字段
"""
# 用户昵称 - 可选的显示名称
nickname = models.CharField(_('nick name'), max_length=100, blank=True)
# 创建时间 - 记录用户注册时间
creation_time = models.DateTimeField(_('creation time'), default=now)
# 最后修改时间 - 记录用户信息最后更新时间
last_modify_time = models.DateTimeField(_('last modify time'), default=now)
# 创建来源 - 记录用户注册渠道(如网站、移动端等)
source = models.CharField(_('create source'), max_length=100, blank=True)
def get_absolute_url(self):
"""
获取用户详情页的绝对URL
返回用户作者详情页的URL路径
用于Django admin和模板中的链接生成
"""
return reverse(
'blog:author_detail', kwargs={
'author_name': self.username})
def __str__(self):
"""
对象字符串表示
返回用户的邮箱地址
用于Django admin和其他显示场景
"""
return self.email
def get_full_url(self):
"""
获取用户的完整URL包含域名
返回包含协议和域名的完整用户URL
用于生成外部可访问的用户主页链接
"""
site = get_current_site().domain
url = "https://{site}{path}".format(site=site,
path=self.get_absolute_url())
return url
class Meta:
ordering = ['-id']
verbose_name = _('user')
verbose_name_plural = verbose_name
get_latest_by = 'id'
"""
模型元数据配置
定义模型在数据库和Django admin中的行为
"""
ordering = ['-id'] # 默认按ID降序排列
verbose_name = _('user') # 单数显示名称
verbose_name_plural = verbose_name # 复数显示名称(与单数相同)
get_latest_by = 'id' # 获取最新记录的依据字段

@ -1,28 +1,42 @@
from django.urls import path
# hyt:
# from django.urls import path
from django.urls import re_path
from . import views
from .forms import LoginForm
app_name = "accounts"
urlpatterns = [re_path(r'^login/$',
views.LoginView.as_view(success_url='/'),
name='login',
kwargs={'authentication_form': LoginForm}),
re_path(r'^register/$',
views.RegisterView.as_view(success_url="/"),
name='register'),
re_path(r'^logout/$',
views.LogoutView.as_view(),
name='logout'),
path(r'account/result.html',
views.account_result,
name='result'),
re_path(r'^forget_password/$',
views.ForgetPasswordView.as_view(),
name='forget_password'),
re_path(r'^forget_password_code/$',
views.ForgetPasswordEmailCode.as_view(),
name='forget_password_code'),
]
app_name = "accounts" # 应用命名空间用于URL反向解析
urlpatterns = [
# 用户登录URL
re_path(r'^login/$',
views.LoginView.as_view(success_url='/'), # 登录成功后跳转到首页
name='login', # URL名称用于反向解析
kwargs={'authentication_form': LoginForm}), # 使用自定义登录表单
# 用户注册URL
re_path(r'^register/$',
views.RegisterView.as_view(success_url="/"), # 注册成功后跳转到首页
name='register'), # URL名称用于反向解析
# 用户注销URL
re_path(r'^logout/$',
views.LogoutView.as_view(), # 注销视图使用Django内置LogoutView
name='logout'), # URL名称用于反向解析
# 账户操作结果页面URL
path(r'account/result.html',
views.account_result, # 函数视图,处理账户操作结果展示
name='result'), # URL名称用于反向解析
# 忘记密码页面URL
re_path(r'^forget_password/$',
views.ForgetPasswordView.as_view(), # 密码重置视图
name='forget_password'), # URL名称用于反向解析
# 忘记密码验证码请求URL
re_path(r'^forget_password_code/$',
views.ForgetPasswordEmailCode.as_view(), # 验证码发送视图
name='forget_password_code'), # URL名称用于反向解析
]

@ -1,26 +1,70 @@
# hyt:
from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend
class EmailOrUsernameModelBackend(ModelBackend):
"""
允许使用用户名或邮箱登录
多字段认证后端
功能扩展Django认证系统支持使用用户名或邮箱登录
继承自ModelBackend提供灵活的用户身份验证方式
应用场景方便用户使用用户名或邮箱任意一种方式登录系统
"""
def authenticate(self, request, username=None, password=None, **kwargs):
"""
用户认证方法
功能根据用户名或邮箱验证用户身份
参数
request: HTTP请求对象
username: 用户名或邮箱地址
password: 用户密码
**kwargs: 其他关键字参数
返回
User对象: 认证成功返回用户实例
None: 认证失败返回None
逻辑
- 判断输入是否包含'@'符号来区分用户名和邮箱
- 查询对应用户并验证密码
- 支持Django认证系统的标准接口
"""
# 根据输入内容判断是用户名还是邮箱
if '@' in username:
kwargs = {'email': username}
kwargs = {'email': username} # 包含@符号,按邮箱处理
else:
kwargs = {'username': username}
kwargs = {'username': username} # 不包含@符号,按用户名处理
try:
# 根据用户名或邮箱查询用户
user = get_user_model().objects.get(**kwargs)
# 验证密码是否正确
if user.check_password(password):
return user
return user # 认证成功,返回用户对象
except get_user_model().DoesNotExist:
# 用户不存在,认证失败
return None
def get_user(self, username):
"""
根据用户ID获取用户对象
功能通过用户主键获取用户实例
参数
username: 用户ID主键
返回
User对象: 用户存在时返回用户实例
None: 用户不存在时返回None
注意此处的username参数实际上是用户主键ID
用于session认证中从用户ID获取用户对象
"""
try:
return get_user_model().objects.get(pk=username)
except get_user_model().DoesNotExist:
return None
return None

@ -1,3 +1,5 @@
# hyt:
import typing
from datetime import timedelta
@ -7,15 +9,21 @@ from django.utils.translation import gettext_lazy as _
from djangoblog.utils import send_email
# 验证码有效期配置5分钟
_code_ttl = timedelta(minutes=5)
def send_verify_email(to_mail: str, code: str, subject: str = _("Verify Email")):
"""发送重设密码验证码
Args:
to_mail: 接受邮箱
subject: 邮件主题
code: 验证码
"""
发送验证邮件
功能向指定邮箱发送包含验证码的邮件用于密码重置等验证场景
参数
to_mail: 接收邮箱地址
code: 验证码内容
subject: 邮件主题默认为"Verify Email"
注意邮件内容包含验证码和5分钟有效期的提示
"""
html_content = _(
"You are resetting the password, the verification code is%(code)s, valid within 5 minutes, please keep it "
@ -24,15 +32,20 @@ def send_verify_email(to_mail: str, code: str, subject: str = _("Verify Email"))
def verify(email: str, code: str) -> typing.Optional[str]:
"""验证code是否有效
Args:
email: 请求邮箱
code: 验证码
Return:
如果有错误就返回错误str
Node:
这里的错误处理不太合理应该采用raise抛出
否测调用方也需要对error进行处理
"""
验证验证码有效性
功能验证用户输入的验证码与缓存中的验证码是否匹配
参数
email: 用户邮箱地址作为缓存键
code: 用户输入的验证码
返回
str: 验证失败时返回错误信息
None: 验证成功时返回None
注意当前错误处理方式不够合理建议改为异常抛出机制
这样调用方可以通过try-except处理错误避免条件判断
"""
cache_code = get_code(email)
if cache_code != code:
@ -40,10 +53,29 @@ def verify(email: str, code: str) -> typing.Optional[str]:
def set_code(email: str, code: str):
"""设置code"""
"""
设置验证码到缓存
功能将验证码存储到缓存系统使用邮箱作为键
参数
email: 邮箱地址作为缓存键
code: 验证码内容作为缓存值
缓存配置验证码有效期为5分钟_code_ttl
"""
cache.set(email, code, _code_ttl.seconds)
def get_code(email: str) -> typing.Optional[str]:
"""获取code"""
return cache.get(email)
"""
从缓存获取验证码
功能根据邮箱地址从缓存中获取对应的验证码
参数
email: 邮箱地址作为缓存键
返回
str: 存在验证码时返回验证码内容
None: 验证码不存在或已过期时返回None
"""
return cache.get(email)

@ -1,3 +1,5 @@
# hyt:
import logging
from django.utils.translation import gettext_lazy as _
from django.conf import settings
@ -32,28 +34,46 @@ logger = logging.getLogger(__name__)
# Create your views here.
class RegisterView(FormView):
form_class = RegisterForm
template_name = 'account/registration_form.html'
"""
用户注册视图
功能处理新用户注册流程包括表单验证用户创建和邮箱验证
继承自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)
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'
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>
@ -64,6 +84,7 @@ class RegisterView(FormView):
如果上面链接无法打开请将此链接复制至浏览器
{url}
""".format(url=url)
# 发送验证邮件
send_email(
emailto=[
user.email,
@ -71,102 +92,152 @@ 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/'
"""
用户注销视图
功能处理用户注销流程清理会话和缓存
继承自RedirectView注销后重定向到登录页面
"""
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):
logout(request)
delete_sidebar_cache()
"""
处理GET请求注销
功能执行用户注销操作清理侧边栏缓存
"""
logout(request) # Django内置注销函数
delete_sidebar_cache() # 清理侧边栏缓存
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 # 一个月的时间
@method_decorator(sensitive_post_parameters('password'))
@method_decorator(csrf_protect)
@method_decorator(never_cache)
"""
用户登录视图
功能处理用户登录认证支持记住登录状态
继承自FormView使用自定义登录表单
"""
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) # 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):
"""
获取模板上下文数据
功能添加重定向地址到上下文用于登录后跳转
"""
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()
delete_sidebar_cache() # 清理侧边栏缓存
logger.info(self.redirect_field_name)
auth.login(self.request, form.get_user())
auth.login(self.request, form.get_user()) # Django内置登录函数
# 处理"记住我"选项
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):
"""
获取登录成功后的跳转地址
功能验证重定向地址的安全性防止开放重定向攻击
"""
redirect_to = self.request.POST.get(self.redirect_field_name)
# 验证重定向地址是否安全
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 # 不安全则使用默认地址
return redirect_to
def account_result(request):
type = request.GET.get('type')
id = request.GET.get('id')
"""
账户操作结果页面视图
功能显示注册或邮箱验证的结果信息
处理注册成功提示和邮箱验证激活
"""
type = request.GET.get('type') # 操作类型register或validation
id = request.GET.get('id') # 用户ID
user = get_object_or_404(get_user_model(), id=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.is_active = True # 激活用户
user.save()
content = '''
恭喜您已经成功的完成邮箱验证您现在可以使用您的账号来登录本站
'''
title = '验证成功'
# 渲染结果页面
return render(request, 'account/result.html', {
'title': title,
'content': content
@ -176,29 +247,56 @@ def account_result(request):
class ForgetPasswordView(FormView):
form_class = ForgetPasswordForm
template_name = 'account/forget_password.html'
"""
忘记密码重置视图
功能处理用户密码重置流程
继承自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/')
return HttpResponseRedirect('/login/') # 重定向到登录页面
else:
# 表单验证失败,重新渲染表单
return self.render_to_response({'form': form})
class ForgetPasswordEmailCode(View):
"""
忘记密码验证码发送视图
功能处理密码重置验证码的发送请求
继承自View处理POST请求发送验证码邮件
"""
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)
utils.send_verify_email(to_email, code) # 发送验证邮件
utils.set_code(to_email, code) # 存储验证码到缓存
return HttpResponse("ok")
return HttpResponse("ok") # 返回成功响应

Loading…
Cancel
Save