补充
编写规范:-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 modelsfrom 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 renderfrom rest_framework.views import APIView
from .models import User
from rest_framework.response import Responsefrom rest_framework_jwt.settings import api_settings
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLERclass 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+passwordemail+passwordphone+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 userelse:raise ValidationError('用户名或密码错误')def _get_token(self, user):payload = jwt_payload_handler(user)token = jwt_encode_handler(payload)return tokendef validate(self, attrs):user = self._get_user(attrs) token = self._get_token(user)self.context['token'] = tokenself.context['username'] = user.usernamereturn attrs
2.3 视图类
'''用户名+密码 邮箱+密码 手机号+密码 都可以登录username+passwordemail+passwordphone+password无论是username,email,phone都以 username形式提交到后端于是:从username字段中取出来的,可能是用户名,可能是邮箱,可能是密码---》都能登录成功
'''
from rest_framework.viewsets import ViewSet, GenericViewSet
from rest_framework.decorators import action
import refrom .serializer import LoginSerializer
from .models import AuthUserclass 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 SimpleRouterrouter = SimpleRouter()
router.register('user', UserView, 'user')
urlpatterns = [path('admin/', admin.site.urls),
]
urlpatterns += router.urls