实现基于Redis和第三方短信服务商的短信验证码登录功能。用户可以通过手机号码获取验证码,并使用验证码进行登录。
基于aliyun的第三方短信服务商提供5次免费试用功能,开通后配置后台页面如下:

import urllib, urllib2, sys
import ssl
host = 'https://zwp.market.alicloudapi.com'
path = '/sms/sendv2'
method = 'GET'
appcode = '你自己的AppCode'
querys = 'mobile=1343994XXXX&content=%E3%80%90%E6%99%BA%E8%83%BD%E4%BA%91%E3%80%91%E6%82%A8%E7%9A%84%E9%AA%8C%E8%AF%81%E7%A0%81%E6%98%AF568126%E3%80%82%E5%A6%82%E9%9D%9E%E6%9C%AC%E4%BA%BA%E6%93%8D%E4%BD%9C%EF%BC%8C%E8%AF%B7%E5%BF%BD%E7%95%A5%E6%9C%AC%E7%9F%AD%E4%BF%A1'
bodys = {}
url = host + path + '?' + querys
request = urllib2.Request(url)
request.add_header('Authorization', 'APPCODE ' + appcode)
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
response = urllib2.urlopen(request, context=ctx)
content = response.read()
if (content):
print(content)
云市场API商品的认证方式主要以下两种方式
目前先采用简单身份认证,购买4元套餐启动认证,否则请求调用返回403鉴权错误。

Django 和 Vue.js 可以很好的集成在一起。Django 处理后端逻辑和 API,而 Vue.js 可以处理前端交互和视图。通过 Django 提供的 API 接口,与 Vue.js 前端进行数据交互。
npm install -g @vue/cli
vue create frontend
cd frontend
在 src/components/LoginWithSMS.vue 中:
配置setting如下:
import { createApp } from 'vue'
import App from './App.vue'
import LoginWithSMS from './components/LoginWithSMS.vue';
createApp(App)
.component('LoginWithSMS', LoginWithSMS)
.mount('#app');
在 Django 中设置 API 来处理手机号码和验证码的验证逻辑,并与 Redis 集成存储验证码。
在 myblog 应用中,创建 API 端点以处理验证码请求和登录验证。
from django.urls import path
from .views import request_verification_code, login_with_verification_code
urlpatterns = [
path('api/request_verification_code/', request_verification_code, name='request_verification_code'),
path('api/login_with_verification_code/', login_with_verification_code, name='login_with_verification_code'),
]
在 blog/views.py 中:
import random
import redis
from django.conf import settings
from django.http import JsonResponse
from django.contrib.auth.models import User
from django.contrib.auth import login
from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator
import json
# 连接Redis
redis_client = redis.StrictRedis(host=settings.REDIS_HOST, port=settings.REDIS_PORT, db=0)
@require_POST
def request_verification_code(request):
data = json.loads(request.body)
phone_number = data.get('phone_number')
if not phone_number:
return JsonResponse({'success': False, 'message': '手机号不能为空'}, status=400)
code = str(random.randint(100000, 999999))
redis_key = f"verification_code:{phone_number}"
redis_client.set(redis_key, code, ex=300) # 5分钟有效期
# 这里调用第三方短信服务商API发送验证码
# send_verification_code(phone_number, code)
return JsonResponse({'success': True, 'message': '验证码已发送'})
@require_POST
def login_with_verification_code(request):
data = json.loads(request.body)
phone_number = data.get('phone_number')
verification_code = data.get('verification_code')
if not phone_number or not verification_code:
return JsonResponse({'success': False, 'message': '手机号和验证码不能为空'}, status=400)
redis_key = f"verification_code:{phone_number}"
stored_code = redis_client.get(redis_key)
if stored_code and stored_code.decode('utf-8') == verification_code:
redis_client.delete(redis_key)
user, created = User.objects.get_or_create(username=phone_number)
if created:
user.set_unusable_password()
user.save()
login(request, user)
return JsonResponse({'success': True, 'message': '登录成功'})
return JsonResponse({'success': False, 'message': '验证码错误'}, status=400)
在 Django 的模板文件中login.html,引入 Vue.js 组件:
{% load static %}
DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Logintitle>
<link rel="stylesheet" href="{% static 'css/login.css' %}">
<script src="{% static 'js/app.85a93ec8.js' %}" defer>script>
<script src="{% static 'js/chunk-vendors.6b7a5a13.js' %}" defer>script>
<link rel="stylesheet" type="text/css" href="{% static 'css/app.438959e3.css' %}">
<script src="https://code.jquery.com/jquery-3.6.0.min.js">script>
<script>
// 当点击验证码图片时,刷新验证码
$('.captcha').click(function () {
$.getJSON('/captcha/refresh/',function (result) {
$('.captcha').attr('src',result['image_url']);
$('#id_captcha_0').val(result['key']);
});
});
script>
<style>
.focusable {
padding: 10px;
margin: 10px;
border: 1px solid #ccc;
outline: none;
}
.focusable:focus {
border-color: #007BFF;
background-color: #E9F7FF;
}
style>
head>
<body>
<div id="main-container">
<div class="main">
<div class="auth-content">
<div class="auth-form">
<div class="tabs">
<input type="radio" id="tab1" name="tab-group" checked>
<label for="tab1">邮箱登录label>
<div class="tab-content">
{% if error_message %}
<p>{{ error_message }}p>
{% endif %}
<form method="post">
{% csrf_token %}
{{ form.as_p }}
form>
div>
<input type="radio" id="tab2" name="tab-group">
<label for="tab2">手机登录label>
<div class="tab-content" id="app">
<login-with-sms>login-with-sms>
div>
<input type="radio" id="tab3" name="tab-group">
<label for="tab3">扫码登录label>
<div class="tab-content">
<h2>Content 3h2>
<p>This is the content of tab 3.p>
div>
<div class="clearfix shortcut-action">
<span class="login"><button type="submit">登录button>span>
<span class="forgot"><a href="{% url 'password_reset_request' %}">忘记密码a>span>
div>
div>
div>
div>
div>
div>
body>
html>
效果如下:

确保你在虚拟环境中安装了 Django 和 Redis:
pip install django djangorestframework redis
python manage.py runserver
npm run serve
集成上面的API调用,采用AppCode方式简单鉴权。
def send_verification_code(phone_number, code): host = 'http://zwp.market.alicloudapi.com' path = '/sms/sendv2' method = 'GET' appcode = settings.SEND_TEXT_APP_CODE content = f"【智能云】您的验证码是{code}。如非本人操作,请忽略本短信" querys = f'mobile={phone_number}&content={content}' print(f'querys, {querys}') bodys = {} api_url = host + path + '?' + querys print(f'api_url, {api_url}') headers = { 'Authorization': 'APPCODE ' + appcode, 'Content-Type': 'application/json', } print(f'headers, {headers}') try: response = requests.get(api_url, headers=headers, verify=True) if response.status_code == 200: print('短信发送成功') return True else: print(f'短信发送失败,错误代码: {response.status_code}, {response.text}') return False except requests.RequestException as e: print(f'短信发送失败: {str(e)}') return False'运行
确保 Vue.js 应用编译和打包正确:
npm run build
这将生成一个 dist 目录,其中包含所有静态文件。
将编译后的文件放到 Django 的静态文件目录:
dist 目录中)放置在 Django 项目的静态文件目录中。你可以将这些文件复制到 static 目录中:cp -r frontend/dist/* path/to/django/static/
启动Django服务。效果如下:
输入手机号

点击获取验证码,启动1分钟倒计时禁止重复请求验证码功能。

同时,成功发送验证码到用户手机。

查询redis服务器能够看到对应的值。

输入验证码,点击登录。报错AxiosError: Network Error at u.onerror (http://127.0.0.1:8000/static/vue/js/chunk-vendors.6b7a5a13.js:18:56732) at nn.request (http://127.0.0.1:8000/static/vue/js/chunk-vendors.6b7a5a13.js:18:64167) at async Proxy.requestVerificationCode (http://127.0.0.1:8000/static/vue/js/app.8100d9be.js:1:2450)
AxiosError: Network Error 表示 Axios 在尝试进行网络请求时遇到了问题。以下是一些可能的原因和解决方法:
确保你的 Django 服务器正在运行,并且你可以通过浏览器访问 http://127.0.0.1:8000。
python manage.py runserver
确保在 Vue.js 中 Axios 请求的 URL 和端口是正确的。
const response = await axios.post('http://127.0.0.1:8000/api/request_verification_code/', {
// 请求数据
});
如果前端和后端在不同的端口上运行,请确保你已经正确配置了 CORS。
django-cors-headers如果还没有安装 django-cors-headers,请先安装它:
pip install django-cors-headers
django-cors-headers在你的 Django 项目的 settings.py 文件中进行以下配置:
INSTALLED_APPS = [
...
'corsheaders',
...
]
MIDDLEWARE = [
...
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
...
]
CORS_ALLOWED_ORIGINS = [
"http://localhost:8080",
"http://127.0.0.1:8080",
# 其他允许的源
]
确保你的网络连接正常,并且没有任何代理服务器阻止网络请求。
在你的 Django 模板中添加 CSRF token:
<input type="hidden" id="csrf_token" name="csrfmiddlewaretoken" value="{% csrf_token %}">
在 Vue.js 中读取并传递 CSRF token:
async requestVerificationCode() {
if (!this.phoneNumber) {
this.message = '请填写手机号';
return;
}
this.isSendingCode = true;
try {
const csrftoken = document.querySelector('[name=csrfmiddlewaretoken]').value;
const response = await axios.post('http://127.0.0.1:8000/api/request_verification_code/', {
country_code: this.countryCode,
phone_number: this.phoneNumber,
}, {
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrftoken
}
});
if (response.data.success) {
this.message = '验证码已发送';
this.startCountdown();
} else {
this.message = '发送验证码失败';
this.isSendingCode = false;
}
} catch (error) {
console.error(error);
this.message = '发送验证码失败';
this.isSendingCode = false;
}
}
使用浏览器的开发者工具(通常按 F12 打开),查看 Network 面板,检查网络请求的详细信息。
确保你的前端(Vue.js)和后端(Django)都在正确的端口上运行。
export default {
data() {
return {
countryCodes: countryCodes, // 使用导入的国家代码数据
countryCode: '+86',
phoneNumber: '',
verificationCode: '',
isSendingCode: false,
countdown: 0,
countdownSeconds: 60,
message: '',
};
},
computed: {
buttonText() {
return this.isSendingCode ? `${this.countdown} 秒后重新获取` : '获取验证码';
}
},
methods: {
async requestVerificationCode() {
if (!this.phoneNumber) {
this.message = '请填写手机号';
return;
}
this.isSendingCode = true;
try {
const csrftoken = document.querySelector('[name=csrfmiddlewaretoken]').value;
const response = await axios.post('http://127.0.0.1:8000/api/request_verification_code/', {
country_code: this.countryCode,
phone_number: this.phoneNumber,
}, {
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrftoken
}
});
if (response.data.success) {
this.message = '验证码已发送';
this.startCountdown();
} else {
this.message = '发送验证码失败';
this.isSendingCode = false;
}
} catch (error) {
console.error(error);
this.message = '发送验证码失败';
this.isSendingCode = false;
}
},
async submitLogin() {
if (!this.phoneNumber || !this.verificationCode) {
this.message = '请填写完整信息';
return;
}
try {
const response = await axios.post('http://127.0.0.1:8000/api/login_with_verification_code/', {
phone_number: this.countryCode + this.phoneNumber,
verification_code: this.verificationCode,
});
if (response.data.success) {
this.message = '登录成功';
// 可以根据需要进行重定向或其他登录成功操作
} else {
this.message = '验证码错误或登录失败';
}
} catch (error) {
console.error(error);
this.message = '登录失败';
}
},
startCountdown() {
const countdownInterval = setInterval(() => {
if (this.countdownSeconds > 0) {
this.countdownSeconds--;
} else {
clearInterval(countdownInterval);
this.countdownTimer = null;
this.isSendingCode = false;
this.countdownSeconds = 60; // 重置倒计时时间
}
}, 1000);
},
},
};
通过以上步骤,你应该能够解决 AxiosError: Network Error 问题。如果问题仍然存在,请提供更多详细信息以便进一步帮助。
再次点击登录,报错网络请求报错 302 found,分析原因在login_with_verification_code中调用了redirect('/post_list')。
在 Vue.js 中成功登录后,使用浏览器的原生 JavaScript 方法进行页面重定向。
在 submitLogin 方法中,当登录成功时,使用 window.location.href 或 window.location.replace() 方法来实现页面的重定向。这些方法直接操作浏览器的地址栏,可以导航到任何 URL,包括 Django 中定义的页面 URL。
示例:
在django中处理rediect逻辑如下:
if stored_code and stored_code.decode('utf-8') == verification_code:
redis_client.delete(redis_key)
user, created = CustomUser.objects.get_or_create(username=phone_number)
print(f'user, {user}')
if created:
user.set_password(phone_number)
user.email = f'{phone_number}@qq.com'
user.save()
login(request, user)
redirect_url = '/post_list' # 修改为实际的 post_list 页面 URL
return JsonResponse({'success': True, 'redirect_url': redirect_url})
return JsonResponse({'success': False, 'message': '验证码错误'}, status=400)
在urls.py中添加路由如下:
path('post_list', views.post_list, name='post_list'),
在vue里面添加重定向路径:
if (response.data.success) {
this.message = '登录成功';
if (response.data.redirect_url) {
window.location.href = response.data.redirect_url; // 页面重定向到 Django 中的 post_list 页面
} else {
this.message = '验证码错误或登录失败';
this.isSendingCode = false;
}
再次点击登录,成功登录跳转
