用Twilio在你的Django应用程序中启用多种OTP方法

447 阅读10分钟

在本教程中,你将学习如何在Django中使用Twilio制作一个一次性密码(OTP)验证系统。这个应用将允许用户选择他们喜欢的方式来接收OTP。

我们将使用Twilio短信服务和Twilio WhatsApp沙盒,通过短信和WhatsApp渠道向用户传递OTP。

让我们来谈谈本教程的主要重点。

我们的主要重点是建立一个传递OTP和验证的管道。这就是我们今天要做的全部。

我们不打算做什么呢?

我们不会使用默认的Django登录设置来制作整个Django用户管理和认证系统--我们将简单地使用cookies来查看用户是否经过OTP验证。

记住这些事情,让我们开始吧。

前提条件

  • 安装了Python3.6或以上版本
  • Django的基本知识(你将有机会在后面的步骤中安装它。)
  • 一个Twilio账户 (如果你没有,可以在这里注册一个免费账户。)
  • 一个用于测试的电话号码和WhatsApp号码

设置

首先,导航到你想建立项目的地方,并创建一个新的目录,该项目中的所有文件都将在此存放。一旦你完成了这项工作,请切换到新的目录。

mkdir twiliotutorial
cd twiliotutorial

接下来,你需要创建一个Python虚拟环境,在那里你可以安装这个项目的依赖项。如果你在 Mac 或 Unix 环境中工作,运行下面的命令。

python3 -m venv venv
source venv/bin/activate

如果你在 Windows 环境下工作,请运行下面的命令。

python -m venv venv
venv\Scripts\activate

在这个新的虚拟环境中,安装这个项目的依赖项。

pip3 install django twilio

现在你有了你需要的需求,是时候开始构建我们的项目了。

创建一个空白的Django项目

让我们开始创建一个带有单个应用程序的基本Django项目。在终端运行以下命令来创建新的Django项目。

django-admin startproject twiliotutorial

这将创建一个空白项目。现在,切换到项目目录,并运行以下命令,创建一个简单的Django应用程序,名为验证

python manage.py startapp verification

打开twiliotutorial/settings.py,将verification 加入到应用列表中。

.....
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'verification'
]
......

现在转到twiliotutorial/urls.py并包括该应用的 urls。

from django.contrib import admin
from django.urls import path,include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('',include('verification.urls'),name="verification")
]

验证文件夹内,创建一个名为urls.py的新文件并添加以下代码。

from django.urls import path

from . import views

urlpatterns = [
    path('', views.register, name='register'),
    path('home', views.home, name='home'),
    path('otp/<str:uid>/', views.otpVerify, name='otp')
]

我们将为我们的项目使用三个视图。一个是用于注册的,它将接受你的用户名和电话号码,然后它将向你的电话号码发送一个OTP。

OTP验证端点是你将能够输入你的OTP进行验证,最后,如果你在10分钟内输入正确的OTP,你将被重定向到主页端点。

现在去验证/views.py,做3个占位符函数。

from django.http import HttpResponse
from django.shortcuts import render,redirect
from django.contrib.auth.models import User
from .models import Profile
import random
from .helper import MessageHandler

def home(request):
        pass

def register(request):
        pass

def otpVerify(request,uid):   
        pass    

现在,对于设置Django项目的最后一步,让我们创建一个模型,用于保存用户资料和在资料中保存OTP。转到验证/models.py,添加这段代码。

from django.db import models
from django.contrib.auth.models import User
import uuid
# Create your models here.
class Profile(models.Model):
    user=models.OneToOneField(User,   on_delete=models.CASCADE,related_name="profile")
    phone_number=models.CharField(max_length=15)
    otp=models.CharField(max_length=100,null=True,blank=True)
    uid=models.CharField(default=f'{uuid.uuid4}',max_length=200)

这里我们要创建一个简单的配置文件模型,它有一个与默认的Django用户模型的链接字段。还有一个字段,将包含用户的电话号码。

OTP字段将存储最近发送给与档案相关的用户的OTP,UID字段将用于获取某个档案对象。

现在你已经全部完成了Django项目的基本结构的设置,你可以通过在终端运行以下命令来运行必要的迁移并启动服务器。

python manage.py makemigrations
python manage.py migrate
python manage.py runserver

为短信设置Twilio

如果你是Twilio的新手,请到www.twilio.com/try-twilio,如果你还没有一个免费账户,请创建一个免费账户。

然后验证你自己的电话号码。

Form to verify your phone number

选择你喜欢的设置。

Twilio sign-up questionnaire

然后你会看到类似下面的内容。 点击 "获得一个Twilio电话号码"。

Diagram showing the steps to get a Twilio phone number

完成账户设置步骤后,你会在Twilio控制台中看到以下细节,你将在Django项目中使用。

Twilio account info, with account SID, auth token, and Twilio phone number

将它们添加到twiliotutorial/settings.py的底部。

ACCOUNT_SID='YOUR ACCOUNT SID'
AUTH_TOKEN='YOUR AUTH TOKEN'
COUNTRY_CODE='+country code of your choice'
TWILIO_WHATSAPP_NUMBER='whatsapp:+14155238886'
TWILIO_PHONE_NUMBER='number you get from Twilio'

在本教程中,设置中加入了一个国家代码,这样短信或WhatsApp信息就只能发送到你所选国家内的号码。

如果你对发送国际信息感兴趣,你可以自定义代码,删除对国家代码的引用,并在后面的测试步骤中,让用户以国际E.164格式输入他们的电话号码。

在这里了解更多关于国际电话号码的格式

设置Twilio用于WhatsApp信息传递

对于WhatsApp消息,我们将需要设置一个Twilio WhatsApp沙盒。它的工作原理是这样的:我们将从我们的程序发送消息到Twilio服务器,然后Twilio将发送消息到我们注册的WhatsApp号码。

在左边的侧边栏,在信息菜单下,进入 "试用",然后 "发送WhatsApp信息"。

Twilio Sandbox setup page

在Twilio沙盒页面,点击蓝色链接,上面写着 "点击这里"。然后发送一条包含粗体字突出显示的文本的WhatsApp信息(你的信息可能有所不同),以注册你的电话号码,以便在开发环境中接收WhatsApp信息。

WhatsApp message confirming phone number is registered

如果您看到这条消息,这意味着您的号码已经准备好接收WhatsApp信息。请记住,只有当你想在沙盒环境中开发应用程序时,你才需要验证号码。

创建Django和Twilio之间的连接

为了创建连接,我们将使用一个帮助文件来发送WhatsApp和SMS消息。

验证文件夹中创建一个名为helper.py的新文件。

我们将在该文件中创建一个MessageHandler ,该类将负责发送WhatsApp和短信。将代码添加到该文件中,如下所示。

from django.conf import settings
from twilio.rest import Client


class MessageHandler:
    phone_number=None
    otp=None
    def __init__(self,phone_number,otp) -> None:
        self.phone_number=phone_number
        self.otp=otp
    def send_otp_via_message(self):     
        client= Client(settings.ACCOUNT_SID,settings.AUTH_TOKEN)
        message=client.messages.create(body=f'your otp is:{self.otp}',from_=f'{settings.TWILIO_PHONE_NUMBER}",to=f'{settings.COUNTRY_CODE}{self.phone_number}')
    def send_otp_via_whatsapp(self):     
        client= Client(settings.ACCOUNT_SID,settings.AUTH_TOKEN)
        message=client.messages.create(body=f'your otp is:{self.otp}',from_=f'{settings.TWILIO_WHATSAPP_NUMBER}',to=f'whatsapp:{settings.COUNTRY_CODE}{self.phone_number}')

你可以按原样使用上述代码,但为了你的好奇心,让我解释一下这段代码。我们创建一个MessageHandler 类,它需要两个参数:OTPphone_numberphone_number 是我们要发送OTP的电话号码。

我们在这个类中有两个函数:一个负责发送短信,另一个负责发送WhatsApp信息。

我们先在这两个函数中初始化Client ,然后使用Twilio函数发送消息。注意,在WhatsApp函数中,我们将使用一个沙盒电话号码,而不是Twilio给我们的号码。

为你的项目创建前端

在本节中,我们将创建registerotpVerify 视图,以及它们的HTML文件。

创建register 视图

让我们为我们的HTML文件编写代码。在验证应用程序的文件夹中,创建一个名为templates的新文件夹,并在里面创建一个名为register**.html的文件。

添加以下代码。

<!DOCTYPE html>
<html>
<head>
    <title>Register</title>
    <style>
        body{
            background-color: rgb(193, 156, 228);
        }
       form {
  position: fixed; /* Stay in place */
  left: 30%;
  top: 10%;
  width: 300px; /* Full width */
  height: 350px; /* Full height */
  overflow: auto; /* Enable scroll if needed */
  background-color: rgb(55, 56, 95); /* Fallback color */
  padding: 60px;
  color: aliceblue;
}
input[type=text], input[type=password] {
  width: 100%;
  padding: 12px 20px;
  margin: 8px 0;
  display: inline-block;
  border: 1px solid #ccc;
  box-sizing: border-box;
}
input[type=submit]{
    margin-top:50px;
    width:100px;
    height:50px;
    font-size: 20px;
    border-radius: 20px;
    background-color: rgb(33, 70, 70);
    color: wheat;
}
.radio-box{
    margin-top:-30px;
}
h2{
    margin-top: -30px;
    color: rgb(213, 217, 221);
}
    </style>
</head>
<body>
   
    <form action="/" method="post">
        <h2>REGISTRATION FORM</h2>
        {% csrf_token %}
        <label for="user_name">User name:</label>
        <input id="user_name" type="text" name="user_name">
        <br>
        <label for="phone_number">Phone Number:</label>
        <input id="phone_number" type="text" name="phone_number">
        <br>
        <h3>OTP verification method:</h3>
        <br>
        <div class="radio-box">
        <label for="methodOtpSms">SMS:</label>
        <input type="radio" name="methodOtp" id="methodOtpSms" value="methodOtpSms">

        <label for="methodOtpWhatsapp">WhatsApp:</label>
        <input type="radio" name="methodOtp" id="methodOtpWhatsapp" value="methodOtpWhatsapp">
    </div>
        <input type="submit" value="login">
        </form>
</body>
</html>

在这里,用户可以简单地填写表格,然后提交,发送一个POST请求。这个表单允许他们在输入框中输入他们的用户名和电话号码,并使用单选按钮来选择他们是否要从短信或WhatsApp接收OTP。

现在在verification/views.py中*,*让我们做一个注册视图。

def register(request):
    if request.method=="POST":
        if User.objects.filter(username__iexact=request.POST['user_name']).exists():
            return HttpResponse("User already exists")

        user=User.objects.create(username=request.POST['user_name'])
        otp=random.randint(1000,9999)
        profile=Profile.objects.create(user=user,phone_number=request.POST['phone_number'],otp=f'{otp}')
        if request.POST['methodOtp']=="methodOtpWhatsapp":
            messagehandler=MessageHandler(request.POST['phone_number'],otp).send_otp_via_whatsapp()
        else:
            messagehandler=MessageHandler(request.POST['phone_number'],otp).send_otp_via_message()
        red=redirect(f'otp/{profile.uid}/')
        red.set_cookie("can_otp_enter",True,max_age=600)
        return red  
    return render(request, 'register.html')

在这里,我们在GET请求中渲染register.html,在POST请求中,我们首先用用户名创建一个用户对象(如果它不存在的话),然后我们用randint 函数生成一个OTP。

然后我们用之前创建的user 实例、电话号码和OTP创建一个Profile

之后,我们检查用户选择什么方式来接收他们的OTP,并使用我们之前创建的函数发送OTP。

最后,我们设置一个有效期为10分钟的cookie,我们将使用这个cookie来定义输入OTP的时间限制。

这里是前台的样子。

Registration form, with username, phone number, and OTP verification fields

在你的服务器仍在运行的情况下,你可以通过在浏览器中打开http://localhost:8000/,看到这个表单。

验证OTP并重定向到主页

在这一部分,我们将创建主要功能--本教程的重点:我们将使用直接的逻辑来验证OTP。

模板文件夹中创建一个文件,命名为otp.html

转到otp.html并添加这段代码。

<!DOCTYPE html>

<html>

<head>
    <title>Register</title>
    <style>
        body{
            background-color: rgb(193, 156, 228);
        }
       form {
  position: fixed; /* Stay in place */
  left: 30%;
  top: 10%;
  width: 300px; /* Full width */
  height: 200px; /* Full height */
  overflow: auto; /* Enable scroll if needed */
  background-color: rgb(55, 56, 95); /* Fallback color */
  padding: 60px;
  color: aliceblue;
}
input[type=text], input[type=password] {
  width: 100%;
  padding: 12px 20px;
  margin: 8px 0;
  display: inline-block;
  border: 1px solid #ccc;
  box-sizing: border-box;
}
input[type=submit]{
    margin-top:50px;
    width:100px;
    height:50px;
    font-size: 20px;
    border-radius: 20px;
    background-color: rgb(33, 70, 70);
    color: wheat;
}
.radio-box{
    margin-top:-30px;
}
h2{
    margin-top: -30px;
    margin-left: 30px;
    color: rgb(213, 217, 221);
}
    </style>
</head>
<body>
    {% block includes %}
    <form action="/otp/{{id}}/" method="post">
        <h2>OTP VERIFICATION:</h2>
        {% csrf_token %}
        <label for="otp">OTP:         </label>
        <input id="otp" type="text" name="otp">
        <input type="submit" value="login">
        </form>
        {% endblock includes %}

</body>
</html>

在这里,我们只是从用户那里获取OTP,并将其送回otp/<uid> 端点。这里,{{id}} 将从我们将创建的视图中发送。

下面是它的外观。

OTP verification form

转到verification/views.py并创建otpVerify 函数。

def otpVerify(request,uid):
    if request.method=="POST":
        profile=Profile.objects.get(uid=uid)     
        if request.COOKIES.get('can_otp_enter')!=None:
            if(profile.otp==request.POST['otp']):
                red=redirect("home")
                red.set_cookie('verified',True)
                return red
            return HttpResponse("wrong otp")
        return HttpResponse("10 minutes passed")        
    return render(request,"otp.html",{'id':uid})

在这里,我们将OTP.htmluid 一起呈现给GET请求。对于POST请求,我们首先检查我们创建的cookie是否仍然有效。如果不是,那么我们将得到一个信息,即10分钟已经过去了。

然后,我们使用uid ,以获得用户的个人资料,并检查保存在个人资料上的OTP是否与用户输入的OTP相符。如果匹配,那么我们就将cookies设置为verified=True ,并将用户重定向到主页。否则,verified=False ,然后应用程序将重定向到主页。

最后,让我们来创建主页视图。


def home(request):
    if request.COOKIES.get('verified') and request.COOKIES.get('verified')!=None:
        return HttpResponse(" verified.")
    else:
        return HttpResponse(" Not verified.")

这个视图将检查验证cookie是否被设置为真,它将在此基础上返回一个响应。

测试你的工作

确保你已经验证了你要用来测试的号码。如果你还没有这样做,在这里验证你的电话号码

我们将使用我们用来验证Twilio账户的电话号码来接收OTP。

以下是一切的工作方式。

输入一个用户名和电话号码,然后选择OTP验证方法。Filled-in registration form

一旦你在手机上收到了OTP,就把这个号码输入到验证表格中。

OTP verification form with filled-in OTP field

验证OTP后,你会看到一个页面,上面有HTTPResponse "已验证"。

Response saying "verified"

这里是我们通过短信收到的OTP。
Phone notification showing the OTP code in an SMS

如果你选择WhatsApp,OTP将看起来像下面这样。

WhatsApp message "your otp is 1158"

总结

我们今天学到了什么?

  • 如何为Django网站创建你自己的验证系统
  • 如何让用户选择短信和WhatsApp来接收OTP
  • 如何在一个应用程序中使用短信或WhatsApp进行认证
  • Django中OTP认证的管道

我是Ath Tripathi,一个来自印度的16岁的程序员,在过去的3年里,我一直在做编程,作为兼职和自由职业者为许多客户和公司工作。网络、应用程序和游戏开发是我的主要兴趣。我做博客是一种业余爱好,也是一个自由职业者。