绑定邮箱(邮箱激活)功能的实现

在许多网站的用户中心中,都有绑定邮箱的功能,那这到底是怎么实现的呢?

本文主要讲述使用Django发送邮件的方法(以163邮箱为例)

常用的免费服务器还有126,QQ

注意:项目未上线,只能在虚拟机中登录的163邮箱点击验证链接才能够成功跳转

一.163邮箱配置

1.注册163邮箱

已注册成功

2.登录后设置

点击设置,点击POP3/SMTP/IMAP

3.

点击客户端授权密码,点击开启,验证手机号,输入授权码(这个需要记住,Django中设置要用) 

4.提示成功开启

5.在Django中配置文件,设置文件的配置信息 

# 邮箱配置信息
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.163.com'
EMAIL_PORT = 25
#发送邮件的邮箱
EMAIL_HOST_USER = '[email protected]'
#在邮箱中设置的客户端授权密码
EMAIL_HOST_PASSWORD = 'xxxxx'
#收件人看到的发件人
EMAIL_FROM = 'XXXX<[email protected]>'

发送邮件的邮箱就是之前设置授权码的邮箱

6.使用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.itcast.cn/subject/pythonzly/index.shtml" target="_blank">点击激活</a>'
send_mail('注册激活','',settings.EMAIL_FROM, ['[email protected]'], html_message=msg)

二.具体实现 

需求:

1.我们在浏览器中输入邮箱地址,点击保存将email保存到数据库

2.数据模型中需要定义一个默认email_active=False的字段来记录邮箱是否被激活(数据库迁移)

    这个比较简单,是之前配置好的,就不写了

3.在email保存到数据库时对email发送激活邮件

4.用户点击激活邮箱的url,服务器接收请求改变email_active=True

5.前端页面刷新显示

1.我们在浏览器中输入邮箱地址,点击保存将email保存到数据库

后端接口设计:

请求方式:PUT/users/emails/

请求参数:

参数 类型 是否必须 说明
email str Email邮箱

返回数据:

返回值 类型 是否必须 说明
id int 用户id
email str Email邮箱

1.首先我们要确定只有登录的用户才能绑定邮箱,所以需要授权

2.创建一个视图并确定继承什么类视图

          只是更新一个字段的值(UpdateAPIView)

3.路径中没有传入PK值,所以需要重写get_object来获取指定用户

4.创建序列化器处理字段信息

视图:

from rest_framework.permissions import IsAuthenticated
from rest_framework.generics import UpdateAPIView
from .serializers import EmailSerializer
class EmailView(UpdateAPIView):
    """
    保存邮箱
    PUT /users/emails/
    """

    permission_classes = [IsAuthenticated]

    serializer_class = EmailSerializer

    def get_object(self):
        return self.request.user

序列化器:

class EmailSerializer(serializers.ModelSerializer):
    """
    邮箱序列化器
    """

    class Meta:
        model = User
        fields = ('id','email')
        extra_kwargs = {
            'email':{
                'required':True
            }
        }


    def update(self, instance, validated_data):

        email = validated_data['email']
        instance.email = validated_data['email']
        instance.save()
        return instance

2.对保存的用户email发送验证邮件

这一步需要在保存email后执行,由于发送验证邮件需要时间,我们可以将这一步放在celery异步任务中实现

from celery_tasks.main import app
from django.core.mail import send_mail
from mall import settings

@app.task(name='send_verify_mail')
def send_verify_mail(email, url):
    # subject, message, from_email, recipient_list,html_message=None
    subject = 'XXXX激活邮件'
    message = ''
    from_email = settings.EMAIL_FROM  # 发件人
    recipient_list = [email]  # 收件人列表
    # 可以传递 html代码
    # 我们需要生成一个url,这个url中的token需要包含用户的id信息(id需要被处理)
    html_message = '<p>尊敬的用户您好!</p>' \
                   '<p>感谢您使用XXXX。</p>' \
                   '<p>您的邮箱为:%s 。请点击此链接激活您的邮箱:</p>' \
                   '<p><a href="%s">%s<a></p>' % (email, url, url)
    send_mail(subject, message, from_email, recipient_list, html_message=html_message)
def update(self, instance, validated_data):
        email = validated_data.get('email')
        instance.email = email
        instance.save()
        # 在这里发送激活邮件
        url = instance.generic_verify_url()
        from celery_tasks.email.tasks import send_verify_mail
        send_verify_mail.delay(email,url)
        return instance

我们发送验证邮件给用户,用户点击验证后我们希望知道这个用户是谁,

所以可以将用户id和email信息拼接在URL发送给用户邮箱当做链接

但是直接发送敏感信息不安全,所以需要用itsdangerous加密发送

def generic_verify_url(self):
        serializer = Serializer(settings.SECRET_KEY, 3600)
        token = serializer.dumps({'id': self.id, 'email': self.email})
        return 'http://www.meiduo.site:8080/success_verify_email.html?token=' + token.decode()

这里需要注意itsdangerous生成的token拼接字符串时需要解码

前端实现:

// 保存email
        save_email: function(){
            var re = /^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$/;
            if(re.test(this.email)) {
                this.email_error = false;
            } else {
                this.email_error = true;
                return;
            }
            axios.put(this.host + '/users/emails/',
                { email: this.email },
                {
                    headers: {
                        'Authorization': 'JWT ' + this.token
                    },
                    responseType: 'json'
                })
                .then(response => {
                    this.set_email = false;
                    this.send_email_btn_disabled = true;
                    this.send_email_tip = '已发送验证邮件'
                })
                .catch(error => {
                    alert(error.data);
                });
        }

用户邮箱可以收到该验证邮件,点击验证链接

3.验证邮箱链接:

后端接口设计:

请求方式:GET /users/emails/verification/

请求参数:

参数 类型 是否必须 说明
token str 用于验证邮箱的token

返回数据:

返回值 类型 是否必须 说明
message str 验证处理结果

1.创建一个视图,分析继承那个视图类(这里不需要序列化器,所以继承APIView) 

2.对token解密取出用户ID和email

3.根据用户ID找到用户

4.修改用户字段email_active=True

class VerificationEmailView(APIView):
    def get(self,request):
        token = request.query_params.get('token')
        if not token:
            return Response({'message':'缺少token'},status=status.HTTP_400_BAD_REQUEST)
        user = User.check_verify_email_token(token)
        if user is None:
            return Response({'message':'链接无效'},status=status.HTTP_400_BAD_REQUEST)
        else:
            user.email_active = True
            user.save()
            return Response({'message':'ok'})

解密方法:

    @staticmethod
    def check_verify_email_token(token):
        serializer = Serializer(settings.SECRET_KEY, 3600)
        try:
            result = serializer.loads(token)
        except BadData:
            return None
        else:
            user_id = result.get('id')
            email = result.get('email')
            try:
                user = User.objects.get(id=user_id, email=email)
            except User.DoesNotExist:
                user = None
            else:
                return user

前端修改显示信息:

<div class="find_header">
        <img src="images/logo.png">
        <span class="sub_page_name fl">|&nbsp;&nbsp;&nbsp;&nbsp;邮箱验证成功</span>
    </div>

    <div class="find_form" id="email_result">
        <div v-if="success" class="pass_change_finish">恭喜您,邮箱验证成功!<br/><a href="/index.html">返回主页</a></div>
        <div v-else class="pass_change_finish">链接已失效,验证失败,请重新验证!<br/><a href="/index.html">返回主页</a></div>
    </div>

最后显示如下:

猜你喜欢

转载自blog.csdn.net/Spencer_q/article/details/82108931