实现Django框架邮箱验证功能

Django框架中,实现用户中心页面的设置邮箱功能,并根据用户设置的邮箱,发送验证邮箱,用户通过点击验证地址,完成邮箱验证。

用户表添加字段

在用户的user表中添加字段,代表该用户使用的邮箱是否通过验证,true为通过验证,相反为false。

1
email_active = models.BooleanField(default=False, verbose_name='邮箱验证状态')

然后进行数据库迁移,完成字段创建。

保存用户邮箱,并发送验证邮箱

用户根据页面输入框输入邮箱后,点击保存,将用户输入的邮箱验证,并保存到数据库user表中,然后根据这个地址,发送验证邮件。

定义序列化器

继承自modelserializer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class EmailSerializer(serializers.ModelSerializer):
"""
邮箱序列化器
"""
# 指定模型类,属性
class Meta:
model = User
fields = ('id', 'email')
extra_kwargs = {
'email': {
# 验证邮箱为必填字段
'required': True
}
}
# 重写update方法,数据库修改该用户的邮箱
def update(self, instance, validated_data):
# 从请求体中接收数据,后面向这个邮箱发送邮件
email = validated_data['email']
# instance要序列化的对象,即user,给email属性赋值
instance.email = validated_data['email']
# 提交
instance.save()

定义保存的视图类

因为不是根据用户id来修改邮箱,而是根据当前登录的用户,所以不传pk,而是重写get_object方法,来获取当前登录的对象

1
2
3
4
5
6
7
8
9
class SaveEmail(UpdateAPIView):
# 验证用户是否登陆,Drf提供的验证功能
permission_classes = [IsAuthenticated]
# 指定序列化器
serializer_class = EmailSerializer
# 因为不获取pk,所以保存的时候获取当前登陆用户的对象
def get_object(self):
# 将对象存到request里
return self.request.user

然后配置url即可。

生成验证地址

保存用户输入的邮箱之后,向这个邮箱发送验证地址,首先需要生成验证地址。
在user的models里添加方法,实现生成地址的功能,在拼接地址时要经过加密,导入jwt,使用jwt加密

1
2
3
4
5
6
7
8
9
10
11
12
# 定义生成地址的方法
def generate_verify_email_url(self):
"""
生成验证邮箱的url
"""
# 设置itsdangerous加密
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://域名/success_verify_email.html?token=' + token
# 返回生成的地址
return verify_url

然后在序列化器 EmailSerializer下的update方法下添加代码:

1
2
# 生成验证链接,调用生成验证连接的函数
verify_url = instance.generate_verify_email_url()

接收生成后的url地址

向用户的邮箱发送验证地址

发送邮箱,属于等待耗时操作,所以使用Celery异步服务(详情看:点这里
在celery_tasks下新建包email,在包里新建文件tasks.py,然后在main.py里添加'celery_tasks.email',
在tasks文件里添加任务代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from celery_tasks.main import app
from django.core.mail import send_mail
from django.conf import settings

@app.task(name='send_verify_email')
def send_verify_email(to_email, verify_url):
"""
发送验证邮箱邮件
:param to_email: 收件人邮箱
:param verify_url: 验证链接
:return: None
"""
subject = "邮件标题(例:***邮箱验证)"
html_message = '<p>尊敬的用户您好!</p>' \
'<p>感谢您使用*****。</p>' \
'<p>您的邮箱为:%s 。请点击此链接激活您的邮箱:</p>' \
'<p><a href="%s">%s<a></p>' % (to_email, verify_url, verify_url)
# Django自带的发送邮件功能
send_mail(subject, "", settings.EMAIL_FROM, [to_email], html_message=html_message)

然后在序列化器 EmailSerializer下的update方法下添加代码:

1
2
3
4
5
# 发送验证邮件,邮箱,与验证连接作为参数
# 使用celery异步服务发送验证,在tasks里面
send_verify_email.delay(email, verify_url)
# 最后,经过保存,接收验证地址,发送地址等功能后,返回该对象
return instance

到此发送验证邮件完成

技术补充点:Django发送邮件

首先注册一个专门用来发送邮件的邮箱163,126,qq等,然后在这些邮箱配置SMTP服务器(设置中开启,经过手机验证后会收到授权码的短信,然后填写授权码,开启。详情可百度)

然后在配置中添加代码:

1
2
3
4
5
6
7
8
9
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.163.com' # (163为例)
EMAIL_PORT = 25
#发送邮件的邮箱
EMAIL_HOST_USER = '你的账号'
#在邮箱中设置的客户端授权密码
EMAIL_HOST_PASSWORD = '你的授权密码'
#收件人看到的发件人
EMAIL_FROM = '<你的邮箱>'

使用django发送邮件:

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

1
2
3
4
5
6
7
8
9
在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字符串可以呈现html页面

验证邮箱

用户在邮箱收到验证邮件后,点击地址进行验证,验证成功返回用户中心页面。

设置解密

因为生成的验证邮箱地址时带有itsdangerous加密的token的,所以要先解密,在user的models里添加解密方法,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 设置解密方法
@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:
# 解密之后,获取data里面的数据
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

然后新建constants.py常量文件,然后添加代码:

1
VERIFY_EMAIL_TOKEN_EXPIRES=60*60*24

设置过期时间。

定义视图函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class SendEmail(APIView):
def get(self, request):
# 获取token,由jwt签发的token,在查询参数里,获取查询参数
token = request.query_params.get('token')
if not token:
# 如果没有token,则返回信息,与状态码
return Response({'message': '缺少token'}, status=status.HTTP_400_BAD_REQUEST)
# 验证过程
# 取得token后验证token,解密,确定是否被更改
# 接收解密后返回的对象
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'})

由前端代码收到这个return后,跳转回用户页面即可。