随着对小程序开发的学习,我选择了使用uniapp作为前端框架,并发现了一个名为django-ninja的新型后端包。在本文中,我将详细记录我实现的功能块以及相关的过程
整体流程分析
1. 前端发送获取验证码的请求(GET)。
2. 后端生成验证码图片和唯一标识符(UUID)。
3. 将UUID和验证码文本进行存储。
4. 返回包含验证码图片和UUID的响应给前端。
5. 前端接收到响应后,展示验证码图片,并将UUID存储在本地。
6. 当用户输入验证码时,前端将UUID和验证码文本发送到后端进行比对。
7. 后端根据UUID检索存储的验证码文本,并与用户输入进行比对。
8. 如果比对成功,后端删除存储的验证码信息。
后端验证码实现
为了实现验证码功能,我选择使用了django-simple-captcha这个包,它提供了验证码生成和存储的功能。
django-simple-captcha
1. 首先,我们需要下载并安装该包:
pip install django-simple-captcha
2. 接下来,在项目的设置文件中注册该应用:
# settings.py
INSTALLED_APPS = [
...
captcha,
]
3. 在设置文件中,我们还需要配置一些基本的验证码参数,例如验证码图片大小、字符个数、超时时间、字体大小、前景色、背景色等。你可以根据自己的需求进行配置。
# ================================================= #
# **************** 验证码配置 ******************* #
# ================================================= #
CAPTCHA_IMAGE_SIZE = (160, 60) # 设置 captcha 图片大小
CAPTCHA_LENGTH = 4 # 字符个数
CAPTCHA_TIMEOUT = 1 # 超时(minutes)
CAPTCHA_OUTPUT_FORMAT = "%(image)s %(text_field)s %(hidden_field)s "
CAPTCHA_FONT_SIZE = 40 # 字体大小
CAPTCHA_FOREGROUND_COLOR = "#64DAAA" # 前景色
CAPTCHA_BACKGROUND_COLOR = "#F5F7F4" # 背景色
CAPTCHA_NOISE_FUNCTIONS = (
"captcha.helpers.noise_arcs", # 线
# "captcha.helpers.noise_dots", # 点
)
# CAPTCHA_CHALLENGE_FUNCT = 'captcha.helpers.random_char_challenge' #字母验证码
CAPTCHA_CHALLENGE_FUNCT = "captcha.helpers.math_challenge" # 加减乘除验证码
4. 然后,我们需要生成相应的数据库表:
python manage.py migrate
去看一下生成的表,就五个字段,还是很好理解的,`expiration`就是过期时间了
现在,我们已经可以生成验证码了。接下来,让我们来看一下如何在前端获取验证码。
首先,让我们查阅一下官方文档。嗯…官方文档可能不是很友好,不如我们直接看一下源代码。
在将该应用配置到Django项目中时,我们需要设置一个路由:
path('captcha/', include('captcha.urls'))
从路由captcha.urls
开始看
urlpatterns = [
re_path(
r"image/(?P<key>\w+)/$",
views.captcha_image,
name="captcha-image",
kwargs={"scale": 1},
),
re_path(
r"image/(?P<key>\w+)@2/$",
views.captcha_image,
name="captcha-image-2x",
kwargs={"scale": 2},
),
re_path(r"audio/(?P<key>\w+).wav$", views.captcha_audio, name="captcha-audio"),
re_path(r"refresh/$", views.captcha_refresh, name="captcha-refresh"),
]
通过查看captcha.urls的源代码,可以找到获取验证码图片、音频和刷新验证码的路由。
找到后,postman试下接口,直接返回404,6,那只能继续看下源码
def captcha_refresh(request):
"""Return json with new captcha for ajax refresh request"""
if not request.headers.get("x-requested-with") == "XMLHttpRequest":
raise Http404
new_key = CaptchaStore.pick()
to_json_response = {
"key": new_key,
"image_url": captcha_image_url(new_key),
"audio_url": captcha_audio_url(new_key)
if settings.CAPTCHA_FLITE_PATH
else None,
}
return HttpResponse(json.dumps(to_json_response), content_type="application/json")
然后看到了这个
if not request.headers.get("x-requested-with") == "XMLHttpRequest":
raise Http404
??? 牛逼
那没办法了,重新封装一个吧
既然如此,那我们就重新封装一个吧
下面是我的封装
获取新验证码、验证用户输入以及处理超时和错误情况的功能。
# -*- coding: utf-8 -*-
# @File : captcah_util.py
# @Time : 2023/7/17 12:02
# Author : 摸鱼呀阿凡
# Contact : f2095522823@gmail.com
# License : MIT LICENSE
import base64
from captcha.conf import settings
from captcha.models import CaptchaStore
from captcha.helpers import captcha_audio_url, captcha_image_url
from captcha.views import captcha_image
from django.utils import timezone
class CaptchaItem_url:
def __init__(self, key, image_url, audio_url):
self.key = key
self.image_url = image_url
self.audio_url = audio_url
class CaptchaItem_count:
def __init__(self, key, image_conte):
self.key = key
self.image_conte = image_conte
def refresh(request, choose: int = 1) -> CaptchaItem_url or CaptchaItem_count:
"""
获取新的验证码
:param request
:param choose 1 or 2
:return CaptchaItem_url or CaptchaItem_count
"""
key = CaptchaStore.pick()
if choose == 1:
return CaptchaItem_url(
key,
captcha_image_url(key),
captcha_audio_url(key) if settings.CAPTCHA_FLITE_PATH else None,
)
elif choose == 2:
return CaptchaItem_count(
key,
base64.b64encode(captcha_image(request, key).content).decode("utf-8")
)
def verify(key: str, code: str) -> str:
"""
检查输入的验证码是否正确并处理超时和错误情况
"""
current_time = timezone.now()
try:
captcha = CaptchaStore.objects.get(response=code, hashkey=key)
# 检查验证码是否超时
if captcha.expiration < current_time:
captcha.delete()
return '超时' # 返回超时提示
# 验证码正确,删除记录并返回成功
captcha.delete()
return '成功' # 返回成功提示
except CaptchaStore.DoesNotExist:
# 清理过期的验证码记录
CaptchaStore.remove_expired()
return '错误' # 返回错误提示
在api.py下实现验证码获取
# -*- coding: utf-8 -*-
# @File : login.py
# @Time : 2023/7/17 9:42
# Author : 摸鱼呀阿凡
# Contact : f2095522823@gmail.com
# License : MIT LICENSE
import json
from django.http import HttpResponse
from ninja import Router
from wechatapp.utlis.captcah_util import refresh
router = Router()
@router.get('/captchaImage', auth=None)
def get_captcha(request):
data = {}
new_captcha = refresh(request, 2)
# 将图片转换为base64
data = {
"msg": "操作成功",
"img": new_captcha.image_conte,
"code": 200,
"captchaEnabled": True,
"uuid": new_captcha.key,
}
return HttpResponse(json.dumps(data), content_type="application/json")
后端登录鉴权
JWT
这里我使用的是 simple jwt
pip install simple jwt
这个包就三个用法(应该):编码,解码,制作
编码:
from simplejwt import make
token = make('secret', {'my_payload': 'some_data'}, 'HS256', issuer='acme', valid_to=1234567)
# eyJ0eXBlIjogIkpXVCIsICJhbGciOiAiSFMyNTYifQ.eyJteV9wYXlsb2FkIjogInNvbWVfZGF0YSIsICJpc3MiOiAiYWNtZSIsICJleHAiOiAxMjM0NTY3fQ.Nr5IADzsOhlzjxnghquBrRwewg10srDHu__-HN7GGGA
参数名 | 类型 | 默认值 | 描述 |
---|---|---|---|
secret | str | _ | 用于创建token的一串你自己写的字符串 |
payload | dict | _ | token中想要包含的数据 |
alg | int | HS256 | 创建token的算法 |
自制:
from simplejwt import make
token = make('secret', {'my_payload': 'some_data'}, 'HS256', issuer='acme', valid_to=1234567)
# eyJ0eXBlIjogIkpXVCIsICJhbGciOiAiSFMyNTYifQ.eyJteV9wYXlsb2FkIjogInNvbWVfZGF0YSIsICJpc3MiOiAiYWNtZSIsICJleHAiOiAxMjM0NTY3fQ.Nr5IADzsOhlzjxnghquBrRwewg10srDHu__-HN7GGGA
参数名 | 类型 | 默认值 | 描述 |
---|---|---|---|
secret | str | _ | 用于创建 token 的一串你自己写的字符串 |
payload | dict | _ | token 中想要包含的数据 |
alg | int | HS256 | 创建token 的算法 |
issuer | str | None | token 的发行者 |
subject | str | None | token 的主题 |
audience | str | None | token 的受众 |
valid_to | int | None | token 的到期日期(时间戳 |
valid_from | int | None | token 有效的日期(时间戳 |
issued_at | int | None | token 的发行日期(时间戳) |
id | str | None | token 的 ID |
解码
from simplejwt import encode, decode
token = encode('secret', {'my_payload': 'some_data'}, 'HS256')
payload = decode('secret', token, 'HS256')
# {'my_payload': 'some_data'}
参数名 | 类型 | 默认值 | 描述 |
---|---|---|---|
secret | str | _ | 用于解码 token 的字符串 |
payload | dict | _ | 要解码的 token |
alg | int | HS256 | 用于创建 token 的算法 |
以上就是关于前后端分离架构下验证码实现和后端登录鉴权的一些简要介绍和代码示例。希望对你有所帮助!如果你还有其他问题或需要进一步的解释,请随时提问。