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作为链接的参数。
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)
例如:
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)
API: PUT /email/
参数:
{
"email":"邮箱"
}
响应:
{
"id":"用户id",
"email":"用户邮箱"
}
业务逻辑:
1.获取参数并进校校验(email必传,邮箱格式)。
2.设置登录用户的邮箱并给邮箱发送验证邮件。
3.返回应答,邮箱设置成功。
在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()), # 设置邮箱
在保存邮箱的时候,需要向用户发送验证邮件,我们将发送邮件的工作放到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)
在发送邮件的异步任务中,需要用到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'])
邮箱的激活链接是用户点击时会访问的网址,我们让用户点击时进入到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
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
API: PUT /emails/verification/?token=xxx
参数:
{
"token":"用于验证邮箱的token"
}
响应:
{
"message":"验证处理结果"
}
业务逻辑:
1.获取token(加密用户信息)并进行校验(token必传,token是否有效)。
2.设置用户的邮箱验证标记True。
3.返回应答,邮箱验证成功。
在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
优质文章推荐: