Python后端部署-Part2
Python后端部署-Part2——Django登录验证
[TOC]
登录验证的实现思路
参考:
- 采用 token 认证方式,使用 rest_framework_simplejwt 库配置权限认证。
- Simple JWT 为 Django REST Framework 框架提供了一个 JSON Web 令牌认证后端。
- 注意:使用 rest_framework_simplejwt 进行身份认证时并不需要去对数据库进行查询校验,所以并不会将 token 保存在数据库中。
安装使用 rest_framework
安装
1
pip install djangorestframework
添加
rest_framework
到INSTALLED_APPS
设置中1
2
3
4
5
6# -> backend/WebService/settings.py
INSTALLED_APPS = [
...
'rest_framework', # DRF
]若使用 DRF 的可浏览 API,则修改路由
urls.py
(在生产环境中是不需要的)1
2
3
4
5
6# -> backend/WebService/urls.py
urlpatterns = [
...
path('api-auth/', include('rest_framework.urls')),
]安装使用 rest_framework_simplejwt
安装
1
pip install djangorestframework_simplejwt
添加
rest_framework_simplejwt
到INSTALLED_APPS
设置中1
2
3
4
5
6
7# -> backend/WebService/settings.py
INSTALLED_APPS = [
...
'rest_framework.authtoken',
'rest_framework_simplejwt', # jwt
]将 Simple JWT 的 JSON Web 令牌认证添加到身份验证类列表中
1
2
3
4
5
6
7
8
9
10
11
12# -> backend/WebService/settings.py
REST_FRAMEWORK = {
# 全局的权限认证,只有通过认证后才赋予用户权限
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
# 身份验证类列表,可以设定多个身份验证
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_simplejwt.authentication.JWTAuthentication',
)
}配置 JWT 相关参数
1
2
3
4
5
6
7
8
9# -> backend/WebService/settings.py
from datetime import timedelta
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=1), # token 的过期时间
'REFRESH_TOKEN_LIFETIME': timedelta(days=15), # 刷新 token 的过期时间
# 'AUTH_HEADER_TYPES': ('Bearer', 'JWT'), # token 的请求头类型
}在任意的路由配置中添加 Simple JWT 提供的视图(这里将登录验证模块集成到 login 应用下)
1
2
3
4
5
6
7
8
9
10
11# -> backend/login/urls.py
from django.urls import path
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView, TokenVerifyView
urlpatterns = [
path('token/', TokenObtainPairView.as_view(), name='token_obtain_pair'), # POST 登录接口
path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'), # POST 刷新token接口
path('token/verify/', TokenVerifyView.as_view(), name='token_verify'), # POST 验证token接口 用于vue前端写路由守卫
...
]如果自定义用户表
1
2
3
4# -> backend/WebService/settings.py
# AUTH_USER_MODEL 配置默认的校验用户表
AUTH_USER_MODEL = 'login.UserInfo'安装使用 django-simple-captcha
安装
1
pip install django-simple-captcha
添加
captcha
到INSTALLED_APPS
中1
2
3
4
5
6# -> backend/WebService/settings.py
INSTALLED_APPS = [
...
'captcha', # 生成验证码
]需要更新数据库
1
2python manage.py makemigrations
python manage.py migrate无验证码的登录验证
其实配置完 simplejwt 时,即可直接使用登录验证。
创建超级用户,命令行输入:
python3 manage.py createsuperuser
使用 post 请求获取 token 令牌
1
2headers:Content-Type:application/json
body:{"username": "admin", "password": "admin"}携带 token 令牌请求访问后端
1
body: {"token": "token"}
之后编写的其他视图函数,都要继承
from rest_framework.views import APIView
,否则将无法进行身份验证。1
2
3
4
5
6
7
8
9
10
11
12
13# -> backend/login/views.py
from rest_framework.views import APIView
class test(APIView): # 使用 token 之后,应当继承 APIView 类
def get(self, request):
return JsonResponse({"msg": "ok"}, json_dumps_params={"ensure_ascii": False})
class test_no_login(APIView): # 不需要进行登录验证的逻辑则添加 permission_classes = []
permission_classes = []
def get(self, request):
return JsonResponse({"msg": "ok"}, json_dumps_params={"ensure_ascii": False})带验证码的登录验证
构造返回验证码的视图
返回验证码的视图接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24# -> backend/login/views.py
import base64
import json
from django.http import HttpResponse
from captcha.views import CaptchaStore, captcha_image
from django.views import View
class CaptchaAPIView(View):
def get(self, request):
hash_key = CaptchaStore.generate_key()
try:
# 获取图片id
id_ = CaptchaStore.objects.filter(hashkey=hash_key).first().id
image = captcha_image(request, hash_key)
# 将图片转换为base64
image_base = 'data:image/png;base64,%s' % base64.b64encode(image.content).decode('utf-8')
json_data = json.dumps({"id": id_, "image_base": image_base})
# 批量删除过期验证码
CaptchaStore.remove_expired()
except:
json_data = None
return HttpResponse(json_data, content_type="application/json")添加验证码视图的接口路由
1
2
3
4
5
6
7
8
9
10# -> backend/login/urls.py
from django.urls import path
from login.views import CaptchaAPIView
urlpatterns = [
...
path('captcha/', CaptchaAPIView.as_view(), name='captcha_api'), # GET 返回验证码接口
]重写带验证码的登录视图
构造一个手动返回令牌的方法
1
2
3
4
5
6
7
8
9
10
11
12# -> backend/login/utils/get_token.py
from rest_framework_simplejwt.tokens import RefreshToken
def get_tokens_for_user(user):
# 手动返回令牌
refresh = RefreshToken.for_user(user)
return {
'refresh': str(refresh),
'access': str(refresh.access_token),
}在扩展
TokenObtainPairView
视图前先扩展该序列化类TokenObtainPairSerializer
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61# -> backend/login/serializer.py
from django.utils import timezone
from rest_framework import serializers
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from django.contrib.auth import authenticate
from captcha.fields import CaptchaStore
from login.utils.get_token import get_tokens_for_user
class DmallTokenObtainPairSerializer(TokenObtainPairSerializer):
captcha = serializers.CharField(max_length=4, required=True,
trim_whitespace=True, min_length=4,
error_messages={
"max_length": "图片验证码仅允许4位",
"min_length": "图片验证码仅允许4位",
"required": "请输入图片验证码"
}, help_text="图片验证码")
imgcode_id = serializers.CharField(required=True, write_only=True,
help_text="图片验证码id")
def get_token(cls, user):
token = super().get_token(user)
token['captcha'] = user.captcha
token['imgcode_id'] = user.imgcode_id
return token
def validate_captcha(self, captcha):
# 验证码验证
try:
captcha = captcha.lower()
except:
raise serializers.ValidationError("验证码错误")
img_code = CaptchaStore.objects.filter(
id=int(self.initial_data['imgcode_id'])
).first()
if img_code and timezone.now() > img_code.expiration:
raise serializers.ValidationError("图片验证码过期")
else:
if img_code and img_code.response == captcha:
pass
else:
raise serializers.ValidationError("验证码错误")
def validate(self, attrs):
# 删除验证码
del attrs['captcha']
del attrs['imgcode_id']
authenticate_kwargs = {
'username': attrs['username'],
'password': attrs['password'],
}
# 验证当前登录用户
self.user = authenticate(**authenticate_kwargs)
if self.user is None:
raise serializers.ValidationError('账号或密码不正确')
# 登录成功返回token信息
token = get_tokens_for_user(self.user)
return token重写
TokenObtainPairView
视图函数1
2
3
4
5
6
7
8# -> backend/login/views.py
from rest_framework_simplejwt.views import TokenObtainPairView
from login.serializer import DmallTokenObtainPairSerializer
class DmallTokenObtainPairView(TokenObtainPairView):
# 登录成功返回token
serializer_class = DmallTokenObtainPairSerializer添加登录视图的接口路由
1
2
3
4
5
6
7
8
9
10# -> backend/login/urls.py
from django.urls import path
from login.views import DmallTokenObtainPairView
urlpatterns = [
...
path('captcha/token/', DmallTokenObtainPairView.as_view(), name='mytoken'),
]测试
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 浅幽丶奈芙莲的个人博客!