补充
编写规范:
- 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