邮箱验证

每日推荐

There is nothing so useless as doing efficiently  
that which should not be done at all.

今天分享的这句话,是管理学教授彼得·德鲁克说过的一句名言,大意是『没有什么比有效地做那些根本不需要做的事更无效的了』让我们不要浪费时间在那些无用的事上。

邮箱验证

需求

1.在用户中心页面中,我们允许用户设置邮箱。

2.当用户点击保存后,我们会向用户发送邮件以验证邮箱的有效性。

3.为了避免用户未收到验证邮件,我们提供『重新发送验证邮件』按钮,允许用户选择重新发送邮件。

4.当用户点击邮件中的连接之后,显示验证成功,然后再用户中心页面显示『已验证』按钮。

技术要点说明

在邮件中提供的激活链接地址,为了能区分是哪个用户在进行邮箱验证,需要在链接中包含用户和邮箱的识别信息,如userid和email数据,但是基于安全性的考虑,不能将这两个数据直接暴露在邮件链接中,而是需要进行隐藏和签名处理(能够检测出是否修改过链接数据)。可以使用前面学过的 itsdangerous对userid和email数据进行处理,生成token作为链接的参数。

1.使用Django发送邮件

Django中内置了邮件发送功能,被定义在django.core.mail模块中。发送邮件需要使用SMTP服务器,常用的免费服务器有:163、126、QQ,下面以163邮件为例。

a.注册163邮箱,登录后在顶部菜单栏『设置』下拉菜单中选择POP3/SMTP/IMAP选项。

b.在新页面中点击左侧『客户端授权密码』,在右侧页面勾选『开启』选项,弹出新窗口按提示进行操作,填写手机验证码。

c.填写授权码后提示开启成功。

d.在Django配置文件中,设置邮箱的配置信息

EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.163.com'
EMAIL_PORT = 25
#发送邮件的邮箱
EMAIL_HOST_USER = 'xxxxx@163.com'
#在邮箱中设置的客户端授权密码
EMAIL_HOST_PASSWORD = 'xxxx'
#收件人看到的发件人
EMAIL_FROM = 'python<xxxxxx@163.com>'

e.使用Django提供的模块发送邮件

django.core.mail模块提供了 send_mail来发送邮件。

send_mail(subject, message, from_email, recipient_list,html_message=None)

  • subject 邮件标题
  • message 普通邮件正文, 普通字符串
  • from_email 发件人
  • recipient_list 收件人列表
  • html_message 多媒体邮件正文,可以是html字符串

例如:

msg='<a href="http://www.xxxx.cn/subject/pythonzly/index.shtml" target="_blank">点击激活</a>'
send_mail('注册激活','',settings.EMAIL_FROM, ['xxx@163.com'], html_message=msg)

2.保存邮箱并发送验证邮件

API: PUT /email/
参数:
    {
        "email":"邮箱"
    }
响应:
    {
        "id":"用户id",
        "email":"用户邮箱"
    }

业务逻辑

1.获取参数并进校校验(email必传,邮箱格式)。

2.设置登录用户的邮箱并给邮箱发送验证邮件。

3.返回应答,邮箱设置成功。

2.1详细步骤

在users/serializers.py中新建序列化器,用户验证用户提交的邮箱信息。

class EmailSerializer(serializers.ModelSerializer):
    """
    邮箱序列化器
    """
    class Meta:
        model = User
        fields = ('id', 'email')
        extra_kwargs = {
            'email': {
                'required': True
            }
        }

    def update(self, instance, validated_data):
        instance.email = validated_data['email']
        instance.save()
        return instance

在users/views.py中创建新视图,用于保存用户的邮箱信息,注意需要用户登录通过认证后。

from rest_framework.permissions import IsAuthenticated

class EmailView(UpdateAPIView):
    """
    保存用户邮箱
    """
    permission_classes = [IsAuthenticated]
    serializer_class = serializers.EmailSerializer

    def get_object(self, *args, **kwargs):
        return self.request.user

设置路由信息

url(r'^emails/$', views.EmailView.as_view()),  # 设置邮箱

2.2补充发送验证邮件

在保存邮箱的时候,需要向用户发送验证邮件,我们将发送邮件的工作放到celery中异步执行。

在celery_tasks目录中新建email包和并在包里面新建email/tasks.py文件

email/tasks.py文件中是实现发送邮件的异步任务

from celery_tasks.main import celery_app
from django.core.mail import send_mail
from django.conf import settings

@celery_app.task(name='send_verify_email')
def send_verify_email(to_email,verfy_url):
    subject = "闫氏商城邮箱验证"
    html_message = '<p>尊敬的用户您好!</p>' \
                   '<p>你咋这么有眼光呢?欢迎使用我们闫氏商城。</p>' \
                   '<p>您的邮箱为:%s。请点击此链接(就是这个有蓝色下划线的东西)激活您的邮箱:</p>' \
                   '<p><a href="%s">%s<a></p>' %(to_email,verfy_url,verfy_url)
    send_mail(subject,"",settings.EMAIL_FROM,[to_email],html_message=html_message)
2.2.1注意

在发送邮件的异步任务中,需要用到django的配置文件,所以我们需要修改celery的启动文件main.py,在其中指明celery可以读取的django配置文件,并且注册添加email的任务

from celery import Celery

# 为celery使用django配置文件进行设置
import os
if not os.getenv('DJANGO_SETTINGS_MODULE'):
    os.environ['DJANGO_SETTINGS_MODULE'] = 'meiduo_mall.settings.dev'

# 创建celery应用
celery_app = Celery('meiduo')

# 导入celery配置
celery_app.config_from_object('celery_tasks.config')

# 自动注册celery任务
celery_app.autodiscover_tasks(['celery_tasks.sms', 'celery_tasks.email'])
2.2.2在User模型类中定义生成验证邮箱链接的方法

邮箱的激活链接是用户点击时会访问的网址,我们让用户点击时进入到successverifyemail.html页面。

class User(AbstrackUser):
    ...
    def generate_verify_email_url(self):
        """
        生成验证邮箱的url
        """
        serializer = TJWSSerializer(settings.SECRET_KEY, expires_in=constants.VERIFY_EMAIL_TOKEN_EXPIRES)
        data = {'user_id': self.id, 'email': self.email}
        token = serializer.dumps(data).decode()
        verify_url = 'http://www.meiduo.site:8080/success_verify_email.html?token=' + token
        return verify_url
2.2.3修改EmailSerializer序列化器的update方法,增加发送邮件
def update(self, instance, validated_data):
        # 设置登录用户的邮箱
        email = validated_data['email']
        instance.email = email
        instance.save()

        # TODO:并给邮箱发送验证邮件
        # 验证链接地址:http://api.meiduo.site:8000/success_verify_email.html?token=<加密用户信息>
        verify_url = instance.generate_verify_email_url()

        # 发出发送邮件的任务消息
        # send_mail()
        from celery_tasks.email.tasks import send_verify_email
        send_verify_email.delay(email,verify_url)

        return instance

3.验证邮箱连接

API: PUT /emails/verification/?token=xxx
参数:
    {
        "token":"用于验证邮箱的token"
    }
响应:
    {
        "message":"验证处理结果"
    }

业务逻辑

1.获取token(加密用户信息)并进行校验(token必传,token是否有效)。

2.设置用户的邮箱验证标记True。

3.返回应答,邮箱验证成功。

3.1详细步骤

在users/views.py 中新建视图

# PUT /emails/verification/?token=<加密信息>
class EmailVerifyView(APIView):
    def put(self,request):
        """
        用户邮箱验证
        1.获取token(加密用户信息)并进行校验(token必传,token是否有效)
        2.设置用户的邮箱验证标记True
        3.返回应答,邮箱验证成功
        """
        # 1.获取token(加密用户信息)并进行校验(token必传,token是否有效)
        token = request.query_params.get('token')

        if token is None:
            return Response({'message':'缺少token参数'},status=status.HTTP_400_BAD_REQUEST)
        # token是否有效
        user = User.check_verify_email_token(token)
        if user is None:
            return Response({'message':'无效的token数据'},status=status.HTTP_400_BAD_REQUEST)

        # 2.设置用户的邮箱验证标记True
        user.email_active = True
        user.save()

        # 3.返回应答,邮箱验证成功
        return Response({'message':'OK'})

在User模型类中定义验证token的方法

class User(AbstrackUser):
    ...
    @staticmethod
    def check_verify_email_token(token):
        """
        检查验证邮件的token
        """
        serializer = TJWSSerializer(settings.SECRET_KEY, expires_in=constants.VERIFY_EMAIL_TOKEN_EXPIRES)
        try:
            data = serializer.loads(token)
        except BadData:
            return None
        else:
            email = data.get('email')
            user_id = data.get('user_id')
            try:
                user = User.objects.get(id=user_id, email=email)
            except User.DoesNotExist:
                return None
            else:
                return user

优质文章推荐:

公众号使用指南

redis操作命令总结

前端中那些让你头疼的英文单词

Flask框架重点知识总结回顾

项目重点知识点详解

难点理解&面试题问答

flask框架中的一些常见问题

团队开发注意事项

浅谈密码加密

Django框架中的英文单词

Django中数据库的相关操作

DRF框架中的英文单词

本文分享自微信公众号 - 全栈技术精选(Pythonnote)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-01-29

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券