|
|
|
|
@ -7,43 +7,97 @@ from django.utils.translation import gettext_lazy as _
|
|
|
|
|
|
|
|
|
|
from djangoblog.utils import send_email
|
|
|
|
|
|
|
|
|
|
#ZXY: 验证码有效期配置:5分钟,使用timedelta便于时间计算
|
|
|
|
|
_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 (str): 接收邮件的邮箱地址
|
|
|
|
|
code (str): 6位数字验证码
|
|
|
|
|
subject (str): 邮件主题,默认为"验证邮件"(支持国际化)
|
|
|
|
|
|
|
|
|
|
Example:
|
|
|
|
|
>>> send_verify_email("user@example.com", "123456")
|
|
|
|
|
# 发送包含验证码123456的邮件到user@example.com
|
|
|
|
|
"""
|
|
|
|
|
#ZXY: 构建邮件正文,使用国际化翻译
|
|
|
|
|
#ZXY: %(code)s是字符串格式化占位符,会被实际的验证码替换
|
|
|
|
|
html_content = _(
|
|
|
|
|
"You are resetting the password, the verification code is:%(code)s, valid within 5 minutes, please keep it "
|
|
|
|
|
"properly") % {'code': code}
|
|
|
|
|
send_email([to_mail], subject, html_content)
|
|
|
|
|
|
|
|
|
|
#ZXY: 调用项目通用的邮件发送函数
|
|
|
|
|
send_email(
|
|
|
|
|
[to_mail], #ZXY: 收件人列表,包装成列表形式
|
|
|
|
|
subject, #ZXY: 邮件主题
|
|
|
|
|
html_content) #ZXY:邮件内容(HTML格式)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def verify(email: str, code: str) -> typing.Optional[str]:
|
|
|
|
|
"""验证code是否有效
|
|
|
|
|
"""
|
|
|
|
|
验证用户输入的验证码是否正确
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
email: 请求邮箱
|
|
|
|
|
code: 验证码
|
|
|
|
|
Return:
|
|
|
|
|
如果有错误就返回错误str
|
|
|
|
|
Node:
|
|
|
|
|
这里的错误处理不太合理,应该采用raise抛出
|
|
|
|
|
否测调用方也需要对error进行处理
|
|
|
|
|
email (str): 用户邮箱,作为缓存键
|
|
|
|
|
code (str): 用户输入的验证码
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
Optional[str]:
|
|
|
|
|
- 验证成功返回 None
|
|
|
|
|
- 验证失败返回错误信息字符串
|
|
|
|
|
|
|
|
|
|
Note:
|
|
|
|
|
当前设计问题:返回错误字符串的方式不够优雅
|
|
|
|
|
建议改进:使用异常抛出,让调用方处理异常
|
|
|
|
|
|
|
|
|
|
Example:
|
|
|
|
|
>>> result = verify("user@example.com", "123456")
|
|
|
|
|
>>> if result:
|
|
|
|
|
... print(f"验证失败: {result}")
|
|
|
|
|
"""
|
|
|
|
|
#ZXY: 从缓存中获取该邮箱对应的验证码
|
|
|
|
|
cache_code = get_code(email)
|
|
|
|
|
|
|
|
|
|
#ZXY: 比较缓存中的验证码和用户输入的验证码
|
|
|
|
|
if cache_code != code:
|
|
|
|
|
#ZXY: 验证失败,返回错误信息(支持国际化)
|
|
|
|
|
return gettext("Verification code error")
|
|
|
|
|
|
|
|
|
|
#ZXY: 验证成功,返回None
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
def set_code(email: str, code: str):
|
|
|
|
|
"""
|
|
|
|
|
将验证码存储到缓存中,设置5分钟有效期
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
email (str): 邮箱地址,作为缓存键
|
|
|
|
|
code (str): 要存储的验证码
|
|
|
|
|
|
|
|
|
|
Technical:
|
|
|
|
|
使用Django的缓存框架,支持多种缓存后端(数据库、Redis、内存等)
|
|
|
|
|
timedelta.seconds 将时间差转换为秒数
|
|
|
|
|
"""
|
|
|
|
|
"""设置code"""
|
|
|
|
|
cache.set(email, code, _code_ttl.seconds)
|
|
|
|
|
cache.set(
|
|
|
|
|
email, #ZXY: 缓存键:使用邮箱地址
|
|
|
|
|
code, #ZXY: 缓存值:验证码字符串
|
|
|
|
|
_code_ttl.seconds) #ZXY: 过期时间:5分钟(300秒)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_code(email: str) -> typing.Optional[str]:
|
|
|
|
|
"""
|
|
|
|
|
从缓存中获取指定邮箱的验证码
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
email (str): 邮箱地址,作为缓存键
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
Optional[str]:
|
|
|
|
|
- 存在验证码则返回验证码字符串
|
|
|
|
|
- 不存在或已过期则返回None
|
|
|
|
|
"""
|
|
|
|
|
"""获取code"""
|
|
|
|
|
return cache.get(email)
|
|
|
|
|
return cache.get(email) #ZXY: 从缓存中获取值,不存在返回None
|
|
|
|
|
|