补充
编写规范:
-1 描述
-2 请求地址
-3 请求方式
-4 请求参数
-headers
-请求体
-请求参数
-5 请求编码格式
-6 返回格式
-示例
-返回数据字段含义
-其他:
-错误状态码
-...
-接口文档编写位置
-写在文件中:word,md,跟前端共享
-公司有接口文档平台
-yapi
-自研
-第三方接口文档:coreapi,drf-yasg
-自动生成---》访问项目地址 的 /doc 路径就能看到接口文档
-json web token的缩写,web方向前后端认证的方式,传统的认证方案使用session,使用jwt后,服务端不需要再存数据了,数据都放在客户端
-本质原理
-jwt 分三段:
-头:公司信息,加密方式,声明这是jwt
-荷载:数据,用户信息,用户id,名字,邮箱,过期时间
-签名:前面量部分通过某个加密方式加密得到一个签名,base64编码后拼接到最后
-开发中:
签发:登录,登录成功后,按照上面方式生成token串
认证:客户端携带token到后端,使用原来的加密方式把第一段和第二段再做加密,加密后跟传入的第三代比较,如果一样,说明传入的token可靠,如果不一样,说明数据被篡改,伪造的,不能信任,程序不能继续往后走了
----------------------------------------------------------------------------------
-加密算法
-1 编码和解码,不加密:base64,url编码和解码
-2 摘要算法:md5,sha1 不能解开
-3 对称加密:des,AES 能加密,能解密;加密和解密使用同样的秘钥
-4 非对称加密 :RSA 能加密,能解密,加密秘钥和解密秘钥是不同的:公钥和私钥
-编码和解码使用内置模块
----------------------------------------------------------------------------------
-django-rest-framework-jwt:有点老
-djangorestframework-simplejwt:新的
-签发:path('login/', obtain_jwt_token),
-认证(局部使用,全局使用,局部禁用):
authentication_classes = [JSONWebTokenAuthentication,]
permission_classes = [IsAuthenticated]
1 jwt自定义表签发
1.1 models.py
from django.db import models
from django.contrib.auth.models import AbstractUser
class User(models.Model):
username = models.CharField(max_length=32)
password = models.CharField(max_length=32)
email = models.EmailField(max_length=32)
gender = models.IntegerField(choices=((1, '男'), (2, '女'), (0, '未知')))
1.2 视图
from django.shortcuts import render
from rest_framework.views import APIView
from .models import User
from rest_framework.response import Response
from rest_framework_jwt.settings import api_settings
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
class UserView(APIView):
def post(self, request):
username = request.data.get('username')
password = request.data.get('password')
user = User.objects.filter(username=username, password=password).first()
if user:
payload={'username':'asdfasdf','exp':1694401763}
token = jwt_encode_handler(payload)
return Response({'code': 101, 'msg': '登录成功', 'token': token, 'username': user.username})
else:
return Response({'code': 101, 'msg': '用户名或密码错误'})
1.3 路由
path('login/', UserView.as_view()),
2 jwt 多方式登录(auth的user表)
username+password
email+password
phone+password
无论是username,email,phone都以 username形式提交到后端
于是:从username字段中取出来的,可能是用户名,可能是邮箱,可能是密码---》都能登录成功
-解决方案:以后尽量不要这么做
-以后要扩写auth的user表,一开始就要扩写,不要等迁移完之后再扩写
-删库
-删迁移文件(不要删__init__.py和migrations文件夹)
-项目app的迁移文件
-django内置app的admin和auth的迁移文件
-重新迁移--两条命令
-扩写auth的user表,需要在配置文件配置
python manage.py createsuperuser
2.1 总结流程
会走字段自己的规则---》username过不了---》因为有unique---》所有需要重写
会走局部钩子---》这里没写
会走全局钩子---》全局钩子里校验
-分成了两个方法:好处是以后修改方法
-_get_user :多方式的 ,以后改成单方式登录,只要该这个方法即可
-_get_token:用的第三方签发,后期改成自己的签发,只需要改它即可
-把生成的token和用户名放到了,序列化类中,单是怕污染数据,放到了序列化类的对象的context中
token = ser.context.get('token')
username = ser.context.get('username')
2.2 序列化类
from .models import AuthUser
from rest_framework import serializers
import re
from rest_framework.exceptions import ValidationError
from rest_framework_jwt.settings import api_settings
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
class LoginSerializer(serializers.ModelSerializer):
username=serializers.CharField()
class Meta:
model = AuthUser
fields = ['username', 'password']
def _get_user(self, attrs):
username = attrs.get('username')
password = attrs.get('password')
if re.match(r'^1[3-9][0-9]{9}$', username):
user = AuthUser.objects.filter(phone=username).first()
elif re.match(r'^.+@.+$', username):
user = AuthUser.objects.filter(email=username).first()
else:
user = AuthUser.objects.filter(username=username).first()
if user and user.check_password(password):
return user
else:
raise ValidationError('用户名或密码错误')
def _get_token(self, user):
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
return token
def validate(self, attrs):
user = self._get_user(attrs)
token = self._get_token(user)
self.context['token'] = token
self.context['username'] = user.username
return attrs
2.3 视图类
'''
用户名+密码 邮箱+密码 手机号+密码 都可以登录
username+password
email+password
phone+password
无论是username,email,phone都以 username形式提交到后端
于是:从username字段中取出来的,可能是用户名,可能是邮箱,可能是密码---》都能登录成功
'''
from rest_framework.viewsets import ViewSet, GenericViewSet
from rest_framework.decorators import action
import re
from .serializer import LoginSerializer
from .models import AuthUser
class UserView(GenericViewSet):
serializer_class = LoginSerializer
@action(methods=['POST'], detail=False)
def login(self, request, *args, **kwargs):
username = request.data.get('username')
password = request.data.get('password')
if re.match(r'^1[3-9][0-9]{9}$', username):
user = AuthUser.objects.filter(phone=username).first()
elif re.match(r'^.+@.+$', username):
user = AuthUser.objects.filter(email=username).first()
else:
user = AuthUser.objects.filter(username=username).first()
if user and user.check_password(password):
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
return Response({'code': 100, 'msg': '登录成功', 'token': token, 'username': username})
return Response({'code': 101, 'msg': '用户名或密码错误'})
-------------------优化版----------------------------------------------------
from rest_framework_jwt.views import obtain_jwt_token
class UserView(GenericViewSet):
serializer_class = LoginSerializer
@action(methods=['POST'], detail=False)
def login(self, request, *args, **kwargs):
ser = self.get_serializer(data=request.data)
if ser.is_valid():
token = ser.context.get('token')
username = ser.context.get('username')
return Response({'code': 100, 'msg': '登录成功', 'token': token, 'username': username})
else:
return Response({'code': 101, 'msg':'用户名密码错误'})
2.4 路由
from django.contrib import admin
from django.urls import path
from app01.views import UserView
from rest_framework.routers import SimpleRouter
router = SimpleRouter()
router.register('user', UserView, 'user')
urlpatterns = [
path('admin/', admin.site.urls),
]
urlpatterns += router.urls