在许多网站的用户中心中,都有绑定邮箱的功能,那这到底是怎么实现的呢?
本文主要讲述使用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 = '13528145XXX@163.com'
#在邮箱中设置的客户端授权密码
EMAIL_HOST_PASSWORD = 'xxxxx'
#收件人看到的发件人
EMAIL_FROM = 'XXXX<13528145xxx@163.com>'
发送邮件的邮箱就是之前设置授权码的邮箱
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, ['XXXX@163.com'], html_message=msg)
二.具体实现
需求:
1.我们在浏览器中输入邮箱地址,点击保存将email保存到数据库
2.数据模型中需要定义一个默认email_active=False的字段来记录邮箱是否被激活(数据库迁移)
这个比较简单,是之前配置好的,就不写了
3.在email保存到数据库时对email发送激活邮件
4.用户点击激活邮箱的url,服务器接收请求改变email_active=True
5.前端页面刷新显示
1.我们在浏览器中输入邮箱地址,点击保存将email保存到数据库
后端接口设计:
请求方式:PUT/users/emails/
请求参数:
参数 | 类型 | 是否必须 | 说明 |
---|---|---|---|
str | 是 | Email邮箱 |
返回数据:
返回值 | 类型 | 是否必须 | 说明 |
---|---|---|---|
id | int | 是 | 用户id |
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">| 邮箱验证成功</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>
最后显示如下: