一、学习DRF的认证类; 设计:LoginView不登录就可以访问,UserView和OrderView需要通过认证后才能访问;
1、urls.py
urlpatterns = [
path('login/', views.LoginView.as_view()),
path('user/', views.UserView.as_view()),
path('order/', views.OrderView.as_view()),
]
2、views.py
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed # 认证失败抛出异常使用
class MyAuthentication(BaseAuthentication):
def authenticate(self, request):
'''
重写这个方法,去做认证,
1、读取用户请求传递的token;
2、校验合法性;
3、返回值:
- 返回一个元组(),认证成功:必须有2个元素,request.user request.auth
- 认证失败,抛出异常,返回错误信息;
- 返回None,在有多个认证类的时候,会依次认证; 都没有认证成功的话,默认为匿名用户;
'''
token = request.query_params.get("token")
if token:
return ("wupeiqi","123456")
else:
raise AuthenticationFailed("认证失败")
class LoginView(APIView):
def get(self,request):
return Response("LoginView")
class UserView(APIView):
authentication_classes = [MyAuthentication,]
def get(self,request):
return Response("UserView")
class OrderView(APIView):
authentication_classes = [MyAuthentication,]
def get(self,request):
return Response("OrderView")
二、简单的实例
上面快速应用,需要在每个视图中都加 authentication_classes 的设置,如果有100个都需要加,按照上面的方法就太繁琐了,所以DRF支持全局配置 authentication_classes ;
还有一个需要注意的地方:全局设置 authentication_classes 的时候,自定义认证类不能在views.py中,要单独放到一个文件中,不然会引起反复调用,报错的问题。
1、urls.py
urlpatterns = [
path('login/', views.LoginView.as_view()),
path('user/', views.UserView.as_view()),
path('order/', views.OrderView.as_view()),
]
2、views.py
class LoginView(APIView):
# 排除页面设置为空就可以了;
# 如果全局和局部都设置了,以局部为准;
authentication_classes = []
def get(self,request):
return Response("LoginView")
class UserView(APIView):
def get(self,request):
return Response("UserView")
class OrderView(APIView):
def get(self,request):
return Response("OrderView")
4、auth.py,在项目下新建一个文件夹,名字为ext,在该文件夹下建立auth.py文件,作为自定义认证类的存放地址
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed # 认证失败抛出异常使用
class MyAuthentication(BaseAuthentication):
def authenticate(self, request):
'''
重写这个方法,去做认证,
1、读取用户请求传递的token;
2、校验合法性;
3、返回值:
- 返回一个元组(),认证成功:必须有2个元素,request.user request.auth
- 认证失败,抛出异常,返回错误信息;
- 返回None,在有多个认证类的时候,会依次认证; 都没有认证成功的话,默认为匿名用户;
'''
token = request.query_params.get("token")
if token:
return ("wupeiqi","123456")
else:
raise AuthenticationFailed("认证失败")
# 解决抛出异常,状态码一致的问题;
def authenticate_header(self, request):
return "API"
3、settings.py
REST_FRAMEWORK = {
# 自定义认证组件的全局配置
"DEFAULT_AUTHENTICATION_CLASSES":['ext.auth.MyAuthentication',]
}
三、多个认证类的执行流程
如果有多个认证类,执行流程是依次执行,直到最后。如果通过,后面不再执行。如果执行到最后都没有通过会返回None,匿名访问。
四、实例应用
1、urls.py
urlpatterns = [
path('login/', views.LoginView.as_view()),
path('user/', views.UserView.as_view()),
path('order/', views.OrderView.as_view()),
]
2、auth.py
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed # 认证失败抛出异常使用
from api import models
# 在url中寻找token
class QueryParamsAuthentication(BaseAuthentication):
def authenticate(self, request):
'''
重写这个方法,去做认证,
1、读取用户请求传递的token;
2、校验合法性;
3、返回值:
- 返回一个元组(),认证成功:必须有2个元素,request.user request.auth
- 认证失败,抛出异常,返回错误信息;
- 返回None,在有多个认证类的时候,会依次认证; 都没有认证成功的话,默认为匿名用户;
'''
token = request.query_params.get("token")
# 如果没有token 返回None
if not token:
return
user_object = models.UserInfo.objects.filter(token=token).first()
if user_object:
return user_object,token # 这样 request.user= 用户对象,request.auth= token ;
# 没有认证成功,返回none,继续去别的认证类中认证
return
# 解决抛出异常,状态码一致的问题;
def authenticate_header(self, request):
return "API"
# 在请求头中寻找token
class HeaderParamsAuthentication(BaseAuthentication):
def authenticate(self, request):
token = request.META.get("HTTP_AUTHORIZATION")
# 如果没有token 返回None
if not token:
return
user_object = models.UserInfo.objects.filter(token=token).first()
if user_object:
return user_object,token # 这样 request.user= 用户对象,request.auth= token ;
# 没有认证成功,返回none,继续去别的认证类中认证
return
# 解决抛出异常,状态码一致的问题;
def authenticate_header(self, request):
return "API"
# 都没找到token,抛出失败
class NoAuthentication(BaseAuthentication):
def authenticate(self, request):
raise AuthenticationFailed({"code":1001,"msg":"认证失败"})
# 解决抛出异常,状态码一致的问题;
def authenticate_header(self, request):
return "API"
3、settings.py
REST_FRAMEWORK = {
"UNAUTHENTICATED_USER": None,
"UNAUTHENTICATED_TOKEN": None,
# 自定义认证组件的全局配置
"DEFAULT_AUTHENTICATION_CLASSES":[
'ext.auth.QueryParamsAuthentication',
'ext.auth.HeaderParamsAuthentication',
'ext.auth.NoAuthentication',
]
}
4、models.py
class UserInfo(models.Model):
'''用户表'''
username = models.CharField(verbose_name="用户名",max_length=32)
password = models.CharField(verbose_name="密码",max_length=64)
# 临时测试方法,token可以存放到很多地方,例如radis jwt等
token = models.CharField(verbose_name="TOKEN",max_length=64,null=True,blank=True)
5、views.py
from rest_framework.response import Response
from rest_framework.views import APIView
from api import models
import uuid # 用于生成token
class LoginView(APIView):
# login页面不需要认证就可以登录,所以单独设置为空;
authentication_classes = []
def post(self,request):
# 1、接收用户提交的用户名和密码;
user = request.data.get("username")
pwd = request.data.get("password")
# 2、数据库校验;
user_object = models.UserInfo.objects.filter(username=user,password=pwd).first()
if not user_object:
return Response({"status":False,"msg":"用户名或者密码错误"})
# 用户名密码正确为用户生产token
token = str(uuid.uuid4())
user_object.token = token
user_object.save()
return Response({"status":True,"msg":"登录成功!","token":token})
class UserView(APIView):
def get(self,request):
print(request.user,request.auth)
return Response("UserView")
class OrderView(APIView):
def get(self,request):
return Response("OrderView")