Django学习9-itsdangerous加密

使用加密验证

注册后在发送给用户的邮件中添加验证链接,最简单链接就是http://www.xxxx.com/users/comfirm/<id>这种形式的URL,其中id为数据库给注册的用户分配的id。用户点击访问这个链接后,对应的视图函数会确认收到用户的id,然后用户的状态更新为已验证。但这种方法并不安全,只要得知链接格式,就能对任意id的用户进行验证。解决的办法时将URL中id换成与之对应的令牌
这里使用itsdangerous包来生成身份验证令牌。Flask就使用了这个包来生成加密签名来保护用户会话cookie。

itsdangerous加密令牌

confirm.py

from itsdangerous import URLSafeTimedSerializer as usts
import base64
from django_ulysses import settings as django_settings

class Token:

    def __init__(self, security_key):
        self.security_key = security_key
        self.salt = base64.encodebytes(security_key.encode())

    def generate_validate_token(self, username):
        serializer = usts(self.security_key)
        return serializer.dumps(username, self.salt)

    def confirm_validate_token(self, token, expiration=3600):
        serializer = usts(self.security_key)
        return serializer.loads(token, salt=self.salt, max_age=expiration)

    def remove_validate_token(self, token):
        """验证不通过删除用户"""
        serializer = usts(self.security_key)
        print(serializer.loads(token, salt=self.salt))
        return serializer.loads(token, salt=self.salt)

token_confirm =Token(django_settings.SECRET_KEY)

使用了itsdangerous包下的URLSafeTimedSerializer类产生和验证令牌,它会产生一个具有过期时间的签名,而且能安全地在URL中使用。指定django项目的SECRET_KEY作为秘钥,并将其经过编码为字节码bytes后base64加密转换后作为salt。generate_validate_token函数会在注册时,根据用户名生成一段令牌,令牌生成后会将带有token的验证链接发送到用户注册用的邮箱。confirm_validate_token函数中,只有没有到令牌过期时间max_age,它会返回token解密后的username。而在remove_validate_token,不考虑过期时间,只是将获取用户名,用于删除对应的用户。

验证用的视图函数和URL

验证用户视图函数confirm

def confirm(request, token):
    try:
        username = token_confirm.confirm_validate_token(token)
    except:
        username = token_confirm.remove_validate_token(token)
        users = User.objects.filter(username=username).all()
        for user in users:
            user.delete()
        messages.add_message(request, DANGER, '验证码错误或已过期,请重新注册')
        return HttpResponseRedirect(reverse('learning_logs:index'))

    try:
        user =User.objects.get(username=username)
    except User.DoesNotExist:
        messages.add_message(request, DANGER, '没有此用户,请重新注册')
        return HttpResponseRedirect(reverse('learning_logs:index'))

    user.is_active = True
    user.save()
    login(request, user)
    messages.add_message(request, messages.SUCCESS, "验证成功,已登录")

    return HttpResponseRedirect(reverse('learning_logs:index'))

验证页面不管是否通过都会重定向到主页。这里,验证不通过的用户会直接被删除,通过的用户会将is_active属性设置为True,然后直接登录此用户。

>>> from users.confirm import token_confirm
>>>token=token_confirm.generate_validate_token('ulysses')
>>> token
'InVseXNzZXMi.DrmMjA.BqhabEN9p_tkOcOIHjZMUgaGcF0'
>>> token_1=token_confirm.generate_validate_token('Hanabi')
>>> token_1
'IkhhbmFiaSI.DrmNOw.Wj_-I3drUh6yWoT-13NksPapfqg'


可见,生成的token由3部分组成,每部分有可能由字母、数字或者下划线连接符_-组成。因此URL格式:

re_path(r"^confirm/(?P<token>\w+.[-_\w]*\w+.[-_\w]*\w+)/$", confirm, name='confirm')

验证邮件

修改注册函数,在注册时使用用户名生成信息令牌,在邮件正文中添加这段验证地址
register:

        if form.is_valid():
            new_user = form.save(commit=False)
            # 默认创建时is_active 是True
            new_user.is_active = False
            new_user.save()

            token = token_confirm.generate_validate_token(new_user.username)
            send_register_email.delay(new_user, token)
            messages.add_message(request, messages.INFO, '验证邮件已发送,请查收')

获取完整的URL地址

要在邮件正文中添加验证地址链接,需要获取其完整的绝对地址,可以使用Django的site框架。Django带有一个可选的“站点”框架。 它是将对象和功能与特定网站相关联的钩子。
Django的sites框架基于django.contrib.sites.models.Site类,它会保存网址的2个属性:

  • domain域名:它是网站的完整域,如:www.example.com
  • name:网站的详细名称,用于人为区分使用。

为了使用site框架,需要:

  1. 添加django.contrib.sites应用到INSTALLED_APPS
  2. setting设置一个SITE_ID,如SITE_ID = 1;
  3. 运行数据迁移;

在数据库中可以看到django-site这张表,
在这里插入图片描述
而上文所说要指定的SITE_ID就是指使用的数据库中那一条数据。更多用法见site framework

使用site对象的get_current()方法获取当前的网站,再使用拼接的方式形成完整的URL地址。

from django.core.mail import send_mail,send_mass_mail, EmailMultiAlternatives
from django.template import loader, Template
from celery import task
from django.conf import settings
from django.contrib.sites.models import Site
from django.urls import reverse
import os


@task
def send_register_email(user, token):

    subject, from_email, to = "[Django]验证用户", os.environ.get('EMAIL_HOST_USER', "2276777056@qq.com"), \
                              user.email

    site = Site.objects.get_current()
    url = '{protocol}://{domain}{path}'.format(
        protocol=getattr(settings, 'ABSOLUTEURI_PROTOCOL', 'http'),
        domain=site.domain,
        path= reverse('users:confirm', args=[token])
    )

    text_content = f"{user.username},你好\
                    欢迎来到 Ulysses\
                    为了验证您的账户,请点击以下链接进行验证\
                    链接:{url}\
                    Ulysses\
                    请勿回复此邮件"

    html_template = loader.get_template('email/confirm.html')
    context = {'user':user, 'url':url}
    html_context = html_template.render(context)
    email = EmailMultiAlternatives(subject=subject, body=text_content, from_email=from_email, to=[to])
    email.attach_alternative(html_context, 'text/html')
    # email.attach_alternative()
    # 添加附件
    # email.attach_file('users/templates/email/confirm.html', 'text/plain')
    # email.attach_file('users/templates/email/confirm.txt', 'text/html')
    email.send()

可以使用已经定义好格式的html模板,导入模板,使用context = {'user':user, 'url':url}来填充、渲染,并将其作为邮件的html正文。
confirm.html:

<p>{{ user.username }}, 你好</p>
<p>欢迎来到 <b>Django Ulysses</b>!</p>
<p>为了验证您的账户,请<a href="{{ url }}">点击</a>进行验证</p>
<p>或者,您可以在浏览器被输入以下内容:</p>
<p>链接:{{ url }}</p>
<p>Ulysses</p>
<p><small>请勿回复此邮件</small></p>

收到的验证邮件
在这里插入图片描述
点击进行验证:
在这里插入图片描述

参考:https://my.oschina.net/keyven/blog/726123

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值