Django后端-短信验证码登录

news2024/11/17 9:38:27

前端我使用的是vben-admin(悄悄说一下,好难用。。),对原生的登录页进行了修改。

本文主要讲一下后端实现。

参考文档:

django+celery使用阿里云短信服务异步发送注册验证码_小泽十一章的博客-CSDN博客

django-实现登录短信验证_子钦加油的博客-CSDN博客

在完成测试前,可以先不搭建celery,celery主要是一个异步任务机制。

1. 准备条件

  • Redis,需要本地搭建一个Redis服务,Redis主要是一个键值对数据库。
  • 开通阿里的短信服务,并安装对应的sdk

2. 短信登录逻辑及实现

1)前端form表单中输入电话号码后,点击发送,触发后端的发送验证码功能。2)后端先解析电话号码是否合理,以及数据库中是否存在该电话号码所对应的用户。

如果存在,则随机生成一个验证码,并使用阿里云的短信发送接口将验证码发送到该手机。

并将手机号:验证码键值对存入Redis。

3)前端用户收到短信后,在表单中填入验证码,点击登录按钮后触发后端的登录api。

这时后端将收到的验证码,与Redis库中存的验证码进行比对,如果一致则合理,返回token及用户信息到前端。

2.1 代码:

settings.py

CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://redis-ip:6379/0",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        }
    }
}

# 将session缓存在Redis中
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
SESSION_CACHE_ALIAS = "default"
SESSION_COOKIE_AGE = 60 * 60 * 12 # 12小时
SESSION_SAVE_EVERY_REQUEST = True
SESSION_EXPIRE_AT_BROWSER_CLOSE = True # 关闭浏览器,则COOKIE失效

 url.py

from django.urls import include, path
from rest_framework import routers

from . import views


router = routers.DefaultRouter()
router.register(r'auth', views.AuthViewSet, basename='auth')

urlpatterns = [
    path('', include(router.urls)),
]

views.py 

import time
import traceback
import random
from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group
from django.forms.models import model_to_dict
from django.http import JsonResponse
from django.shortcuts import get_object_or_404
from rest_framework import permissions, status
from rest_framework.authtoken.models import Token
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.viewsets import ViewSet, ModelViewSet, ReadOnlyModelViewSet
from phonenumbers import is_valid_number, parse as parse_number
from django.core.cache import cache

from utils.aliyun import Aliyun

from .serializers import (
    SendSMSCodeSerializer,
	MobileLoginSerializer,
)

class AuthViewSet(ViewSet):

    @action(detail=False, methods=['post'])
    def send_sms_code(self, request, *args, **kwargs):
        serializer = SendSMSCodeSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        mobile = serializer.validated_data['mobile']

        # 生成随机验证码,发送验证码并将其保存到数据库中
        stored_sms = random.randrange(1000, 9999)
        Aliyun.ali_send_sms(mobile, stored_sms)
        cache.set(mobile, stored_sms)   # 设置mobile:sms键值对,有效期为60s
        return Response({'detail': 'Verification code sent successfully'}, status=status.HTTP_200_OK)
		
    @action(detail=False, methods=['post'])
    def mobile_login(self, request, *args, **kwargs):
        serializer = MobileLoginSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        user = serializer.validated_data['user']
        token, created = Token.objects.get_or_create(user=user)
        if time.time() - token.created.timestamp() > settings.TOKEN_EXPIRE_SECONDS:
            Token.objects.filter(user_id=user.id).delete()
            token, created = Token.objects.get_or_create(user=user)
        return Response({'token': token.key,
                         'userId': user.username,})

serializers.py 中要创建序列化类并定义验证函数:

import re

from django.contrib.auth import authenticate, get_user_model
from django.contrib.auth.models import Group
from django.db.models.fields.files import FieldFile
from django.forms.models import model_to_dict
from rest_framework import serializers
from django.core.cache import cache
import phonenumbers

User = get_user_model()

class MobileLoginSerializer(serializers.Serializer):
    mobile = serializers.CharField(write_only=True)
    sms = serializers.CharField(write_only=True)

    def validate(self, attrs):
        mobile = attrs.get('mobile')
        sms = attrs.get('sms')
        # 从数据库中获取存储的验证码, 检查存储的验证码与输入的验证码是否匹配
        if cache.has_key(mobile):
            stored_sms = cache.get(mobile)
        else:
            serializers.ValidationError("未找到验证码")
        if mobile and sms:
            if str(stored_sms) != str(sms):
                raise serializers.ValidationError("验证码错误, sms:{}, stored_sms: {}".format(sms, stored_sms))
            user = User._default_manager.get(mobile=mobile)

            if not user:
                msg = f"Access denied: wrong mobile: {mobile} or sms: {sms}."
                raise serializers.ValidationError(msg)
        else:
            msg = 'Both "mobile" and "sms" are required.'
            raise serializers.ValidationError(msg)
        attrs['user'] = user
        return attrs


class SendSMSCodeSerializer(serializers.Serializer):
    mobile = serializers.CharField(write_only=True)

    def validate(self, attrs):
        mobile = attrs.get('mobile')
        mobile_string = phonenumbers.parse('+86{}'.format(mobile), None)
        if not phonenumbers.is_valid_number(mobile_string):
            raise serializers.ValidationError('号码不存在: {}'.format(mobile))

        if mobile:
            user = User._default_manager.get(mobile=mobile)
            if not user:
                msg = f"未找到该号码相关的注册用户"
                raise serializers.ValidationError(msg)
        else:
            msg = '手机号码错误, 请重新输入'
            raise serializers.ValidationError(msg)

        return attrs

3. 阿里云短信发送接口

先登录阿里云,支付宝扫码登录即可,直接搜索栏搜索短信服务,点击进入:

搜索短信服务->概览->右下角点击AccessKey, 刚开始可申请一段时间的免费试用。

 为了防止token过大导致的权限问题,阿里推荐使用RAM子账号。

点击后,会生成对应的'accessKeyId', 'accessKeySecret',调用的时候传入即可,我是写在了Django的settings里面。

短信签名,模板都是需要申请的,目前暂时使用的是阿里测试签名和短信模板。

        send_sms_request = dysmsapi_20170525_models.SendSmsRequest(

            sign_name='阿里云短信测试',

            template_code='SMS_154950909',

            phone_numbers='159*****888',

            template_param='{"code":"1234"}'

        )

短信服务_SDK中心-阿里云OpenAPI开发者门户 (aliyun.com)

有很多版本,我们Django应该选择python版本,安装的python package为:

pip install alibabacloud_dysmsapi20170525==2.0.23

3.1  代码:

settings.py

ACCESS_KEY_ID = 'xxx'
ACCESS_KEY_SECRET = 'xxx'

utils/aliyun.py

# -*- coding: utf-8 -*-
# This file is auto-generated, don't edit it. Thanks.
import sys
import json
from django.conf import settings

from alibabacloud_dysmsapi20170525.client import Client as Dysmsapi20170525Client
from alibabacloud_tea_openapi import models as open_api_models
from alibabacloud_dysmsapi20170525 import models as dysmsapi_20170525_models
from alibabacloud_tea_util import models as util_models
from alibabacloud_tea_util.client import Client as UtilClient


class Aliyun:
    def __init__(self):
        pass

    @staticmethod
    def create_client(
        access_key_id: str,
        access_key_secret: str,
    ) -> Dysmsapi20170525Client:
        """
        使用AK&SK初始化账号Client
        @param access_key_id:
        @param access_key_secret:
        @return: Client
        @throws Exception
        """
        config = open_api_models.Config(
            # 必填,您的 AccessKey ID,
            access_key_id=access_key_id,
            # 必填,您的 AccessKey Secret,
            access_key_secret=access_key_secret
        )
        # 访问的域名
        config.endpoint = f'dysmsapi.aliyuncs.com'
        return Dysmsapi20170525Client(config)

    @staticmethod
    def ali_send_sms(mobile, sms) -> None:
        # 工程代码泄露可能会导致AccessKey泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考,建议使用更安全的 STS 方式,更多鉴权访问方式请参见:https://help.aliyun.com/document_detail/378659.html
        # client = Aliyun.create_client(settings.ACCESS_KEY_ID, settings.ACCESS_KEY_SECRET)
        client = Aliyun.create_client(settings.ACCESS_KEY_ID, settings.ACCESS_KEY_SECRET)
        send_sms_request = dysmsapi_20170525_models.SendSmsRequest(
            sign_name='阿里云短信测试',
            template_code='SMS_154950909',
            phone_numbers=mobile,
            template_param=json.dumps({"code":sms})
        )
        try:
            # 复制代码运行请自行打印 API 的返回值
            client.send_sms_with_options(send_sms_request, util_models.RuntimeOptions())
        except Exception as error:
            # 如有需要,请打印 error
            UtilClient.assert_as_string(error.message)

    @staticmethod
    async def ali_send_sms_async(mobile, sms) -> None:
        # 工程代码泄露可能会导致AccessKey泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考,建议使用更安全的 STS 方式,更多鉴权访问方式请参见:https://help.aliyun.com/document_detail/378659.html
        client = Aliyun.create_client(settings.ACCESS_KEY_ID, settings.ACCESS_KEY_SECRET)
        send_sms_request = dysmsapi_20170525_models.SendSmsRequest(
            sign_name='阿里云短信测试',
            template_code='SMS_154950909',
            phone_numbers=mobile,
            template_param=json.dumps({"code":sms})
        )
        try:
            # 复制代码运行请自行打印 API 的返回值
            await client.send_sms_with_options_async(send_sms_request, util_models.RuntimeOptions())
        except Exception as error:
            # 如有需要,请打印 error
            UtilClient.assert_as_string(error.message)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/496829.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

单链表OJ题:LeetCode--203.移除链表元素

朋友们、伙计们,我们又见面了,今天给大家带来的是LeetCode中203题:移除链表元素 数据结构:数据结构专栏 作 者:stackY、 C 语 言 :C语言专栏 LeetCode--203.移除链表元素:https://leetco…

攻防世界-web-simple js

题目描述:小宁发现了一个网页,但却一直输不对密码。(Flag格式为 Cyberpeace{xxxxxxxxx} ) 打开链接: 然后我们会发现不管我们输入什么密码,发现是都是这样的报错 1. 先用bp抓包看看,可以抓到这样的一串js脚本 看不懂…

SVM(基于李航统计学习方法,包含SMO)

文章目录 线性可分SVM和硬间隔最大化函数间隔和几何间隔间隔最大化支持向量 学习的对偶算法 线性SVM和软间隔最大化支持向量 非线性SVM和核函数SMO算法求解二次规划选择变量第一个变量第二个变量 计算 b b b 和 E i E_i Ei​ 线性可分SVM和硬间隔最大化 函数间隔和几何间隔 …

C++11多线程:std::thread创建线程和std::async创建异步任务的区别,std::async创建异步任务后没有被推迟执行。

系列文章目录 文章目录 系列文章目录前言一、thread和async的区别1.1 新线程和异步任务1.2 std::async和std::thread最明显的不同,就是async有时候并不创建新线程。1.3 std::async和std::thread的区别1.4 std::async不确定性问题的解决 二、使用方法2.1 std::async创…

js基础内容

第一种,几乎完全不用 <a href"#" onclick"alert(百度一下)">百度一下</a>第二种,写在script内 <body><a class"baidu" href"#">百度一下</a><script>var baiduAEldocument.querySelector("…

ChatGPT-5即将发布,上千名人士却紧急叫停

ChatGPT4还没有好好体验&#xff0c;比GPT4强大1000倍的ChatGPT5又即将发布&#xff01;届时将彻底改变人工智能领域&#xff0c;并改变我们现有的世界 【ChatGPT 5简介】 OpenAI计划在2023年12月发布其最新且最强大的人工智能模型——ChatGPT 5。该模型具备人工通用智能的能力…

中文大模型安全性哪家强?清华团队新发布

当前大型语言模型的火爆程度我们不用再进行赘述了&#xff0c;伴随着百度文心一言打响国内商业大模型第一枪&#xff0c;华为盘古&#xff0c;阿里通义千问&#xff0c;智谱ChatGLM,科大讯飞星火等国内公司纷纷开始布局。 另一方面由于众所周知的政策原因&#xff0c;和如火如荼…

Threejs进阶之十三:CSS3DRenderer与Tween.js实现粒子小球按规律变化

今天我们使用CSS3DRendererTween.js实现Threejs官方示例中的粒子小球按规律变化的效果&#xff0c;先看下最终实现的效果 先来分析下&#xff0c;这个页面的动画效果是由512个小球组合起来的四种不同变化&#xff0c;分别是曲面、立方体、随机和圆球四种变化&#xff1b;下面我…

Linux——进程间通信(管道)

目录 进程通信的目的 管道 见见猪跑(举个例子) 文件描述符fd与管道的关系(深度理解管道) 什么是管道&#xff1f; 匿名管道 pipe函数概述 父子进程通信时与文件描述符的关系图(理解pipe函数的关键) pipe函数的使用 管道读写规则 管道的大小 自测 使用man 7 pipe查看 …

Unity Timeline使用

Unity Timeline使用 1.创建Timeline&#xff1a;打开面板Window->Sequencing->Timeline (1.1)选择一个要添加 Timeline 的物体&#xff0c;我创建一个物体就叫 Timeline(可以随意命名)&#xff0c;选择Timeline&#xff0c;然后在面板上显示 Create 按钮&#xff0c;如…

Arduino_STM32 之Arduino IDE开发配置

前言 由于选了物联网作为选修课&#xff0c;老师喜欢使用Arduino进行编程&#xff0c;但是也要教我们使用STM32。于是他就让我们使用Arduino IDE开发STM32&#xff08;用Keil 不好吗&#xff1f;&#xff1f;&#xff1f;&#xff09;。 第一章 软件下载 安装Arduino IDE&…

springboot请求响应

SpringBootWeb请求响应 前言 在上一次的课程中&#xff0c;我们开发了springbootweb的入门程序。 基于SpringBoot的方式开发一个web应用&#xff0c;浏览器发起请求 /hello 后 &#xff0c;给浏览器返回字符串 “Hello World ~”。 其实呢&#xff0c;是我们在浏览器发起请求…

环形链表 力扣

题目描述 题目要求 判断一个单链表是不是环形链表&#xff0c;是就返回true 不是就返回false 思路 要搞清楚环形链表长啥样环形链表有哪些特征 环形链表顾名思义就是在链表中有一个类似环形的结构&#xff0c; 它和普通单链表的区别就是 你用遍历普通单链表的法子遍历一个环…

k8s基础5——Pod常用命令、资源共享机制、重启策略和健康检查、环境变量、初始化容器、静态pod

文章目录 一、基本了解二、管理命令三、yaml文件参数大全四、创建pod的工作流程五、资源共享机制5.1 共享网络5.2 共享存储 六、生命周期重启策略健康检查七、环境变量八、Init Containe初始化容器九、静态Pod 一、基本了解 概念&#xff1a; Pod是一个逻辑抽象概念&#xff0c…

Vben Admin 自学记录 —— Table组件的基本使用及练习(持续更新中...)

Table 表格 对 antv 的 table 组件进行封装 table相关使用及概念 练习 —— 画一个简单的包含增删改查的表格静态页面&#xff08;不包含相关逻辑和处理&#xff09; 之前相关记录&#xff1a; Vben Admin 自学记录 —— 介绍及使用 1.在之前添加的新路由模块中添加一个表…

TCP/IP网络编程(三)

TCP/IP网络编程读书笔记 第14章 多播与广播14.1 多播14.1.1 多播的数据传输方式及流量方面的优点14.1.2 路由&#xff08;Routing&#xff09;和 TTL&#xff08;Time to Live&#xff0c;生存时间&#xff09;&#xff0c;以及加入组的办法14.1.3 实现多播 Sender 和 Receiver…

使用mybatisX逆向生成数据表实体类(pojo,dao),mapper,service

先看使用mybatisX后生成的文件。 1.先在idea安装mybatisX插件&#xff0c;在file->setting->plugins&#xff0c;搜索mybatisX插件&#xff0c;重新启动idea即可。 2.在idea编辑器右侧点击Database&#xff0c;点击“”链接你的数据库类型&#xff0c;这里我选mysql。 输…

Vue核心 列表渲染 数据监视

1.13.列表渲染 1.13.1.基本列表 v-for指令 用于展示列表数据语法&#xff1a;&#xff0c;这里key可以是index&#xff0c;更好的是遍历对象的唯一标识可遍历&#xff1a;数组、对象、字符串&#xff08;用的少&#xff09;、指定次数&#xff08;用的少&#xff09; <!…

尚硅谷大数据技术NiFi教程-笔记01【NiFi(基本概念、安装、使用)】

视频地址&#xff1a;尚硅谷大数据NiFi教程&#xff08;从部署到开发&#xff09;_哔哩哔哩_bilibili 尚硅谷大数据技术NiFi教程-笔记01【NiFi&#xff08;基本概念、安装、使用&#xff09;】尚硅谷大数据技术NiFi教程-笔记02【NiFi&#xff08;使用案例&#xff0c;同步文件、…

Kafka 权威指南

Kafka 权威指南 这本书于 2021 年看完&#xff0c;2022 年又看了一遍&#xff0c;感觉书读百遍&#xff0c;其义自现。 这本书侧重于 Kafka 的理论知识&#xff0c;虽然书有点老&#xff0c;但是其中关于 Kafka 的基础知识的章节讲得确实不错&#xff0c;适合学习 Kafka 的新手…