DRF - 认证
认证组件
在DRF中,提交请求的时候不需要携带csrf,因为有token认证了
认证:通过判断token,来判断用户是否登录
1.登录接口编写
- 需要和数据库交互,但是不需要序列化,所以使用ViewSetMixin + APIView
使用action装饰器装饰
-
发送post请求,取出前端传入的数据-->request.data中获取
-
判断是否成功 将session存在UserToken中存储登录状态
如果存在则更新,不存在就更新
-
返回给前端
(1)models.py - 模型层
建立User和UserToken表
class User(models.Model):
username = models.CharField(max_length=32)
password = models.CharField(max_length=32)
class UserToken(models.Model):
# 跟User是一对一,存放token信息
token = models.CharField(max_length=32)
user = models.OneToOneField(to='User', on_delete=models.CASCADE, null=True)
# user :反向,表名小写,所有有user字段
(2)urls.py - 路由
通过路由自动生成接口 - /api/v1/user/login/
from rest_framework.routers import SimpleRouter
# 生成路由
router = SimpleRouter()
router.register('user', views.UserView, 'user')
urlpatterns = [
path('admin/', admin.site.urls),
path('api/v1/', include(router.urls)),
]
(3)views.py - 视图层
登录接口,自动生成路由 + ViewSet
- 由于登录功能,不用序列化数据,所以可以继承ViewSet
class UserView(ViewSet):
# 路由接口装饰器
@action(methods=['POST'], detail=False)
def login(self, request):
username = request.data.get('username')
password = request.data.get('password')
user = User.objects.filter(username=username, password=password).first()
# 1.判断用户名是否存在
if user:
# 用户名存在
# 1.1 生成一个随机字符串:通过uuid4生成
token = str(uuid.uuid4())
# 1.2 生成之后将其存在user_token中,用于登录的时候校验
# 如果之前没有登录过,则没有token则新建一个。
# 如果有token则更新token ==> 使用 update_or_create(传对象,defaults={'token':token)方法
UserToken.objects.update_or_create(user=user,defaults={'token':token})
return Response({'code': 100, 'msg': '用户登录成功'})
else:
return Response({'code': 101, 'msg': '用户名或者密码错误'})
2.认证功能
自定认证的使用步骤
(1)编写一个认证类,继承【认证模块】中的BaseAuthentication
from rest_framework.authentication import BaseAuthentication
(2)重写authenticate
方法,来进行登录认证
- 判断用户是否提供了token
- 如果提供了token,则判断token是否一致
一般token都放在【请求头】中,而通过request.META来获取
(3)如果认证成功,返回【当前登录用户,token】
(4)如果认证不通过则通过AuthenticationFailed
抛出异常
from rest_framework.exceptions import AuthenticationFailed
(5)全局认证与局部认证、局部禁用
3.全局认证与局部认证、局部禁用
全局、局部、默认配置的优先级
1.局部(视图类中)
2.全局(
settings.py
中)3.默认(默认配置文件中)
认证类可以设置是全局使用或者是局部使用。
并且认证类可以配置多个,按照从前向后到顺序执行,如果前面有返回值,认证就不继续往下执行了
(1)局部认证:对于对应的视图类生效
在视图类中,通过authentication_classes
来进行限制认证
class BookDetailView(ViewSetMixin, RetrieveAPIView):
"""查询单个"""
queryset = Book.objects.all()
serializer_class = BookSerializer
"局部认证"
authentication_classes = [LoginAuth]
(2)全局认证:对于全局的视图类生效
settings.py在配置文件中配置
REST_FRAMEWORK = {
# 【认证】的全局配置
'DEFAULT_AUTHENTICATION_CLASSES':'app01.authenticate.LoginAuth',
}
(3)全局认证 + 局部禁用
settings.py 在配置文件中配置
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": ["mine.x_authenticate.LoginAuth", ]
}
views.py的视图类中
当全局都配置了需要认证后,只要在局部的视图类中authentication_classes
空即可完成局部禁用的功能
class BookView(APIView):
authentication_classes = []
...
不要在配置中乱导东西??
例如:在配置文件中导入
from app01.authenticate import LoginAuth
则会报错,这是因为app01还没在程序中加载成功
django.core.exceptions.AppRegistryNotReady: Apps aren't loaded yet.
4.代码演示
authenticate.py
- 自己编写认证类
# 导入认证类
from rest_framework.authentication import BaseAuthentication
# 导入认证失败时抛出的异常字段
from rest_framework.exceptions import AuthenticationFailed
# 导入UserToken表
from .models import UserToken
class LoginAuth(BaseAuthentication):
def authenticate(self, request):
"""
阅读源码我们可以知道,需要重写 authenticate 方法,来进行登录认证。
代码逻辑:如果请求中携带了正确的token则可以登录成功【token放在地址栏中】
"""
# 1.获取token
token = request.query_params.get('token', None)
# 2.判断是否存在token
if token:
# 3.获取UserToken表中的token
user_token = UserToken.objects.filter(token=token).first()
# 4.判断UserToken表中是否记录了token
if user_token:
# 5.如果存在token则表示用户已经登录,则需要【固定】返回两个数据:(1)当前登录用户 (2)token
return user_token.user, token
else:
# 6.如果没有登录则抛出异常
raise AuthenticationFailed('用户未登录,token认证失败')
else:
# 7.前端没有传token抛出异常
raise AuthenticationFailed('token未携带')
views.py
- 视图类
from .serializer import BookSerializer
# 导入认证类
from .authenticate import LoginAuth
"2 查询所有 和 查询单个 的接口"
# 导入查询所有和查询单个的视图子类
from rest_framework.generics import ListAPIView, RetrieveAPIView
# 自动生成路由的魔法类
from rest_framework.viewsets import ViewSetMixin
class BookView(ViewSetMixin, ListAPIView):
"""查询所有"""
queryset = Book.objects.all()
serializer_class = BookSerializer
class BookDetailView(ViewSetMixin, RetrieveAPIView):
"""查询单个"""
queryset = Book.objects.all()
serializer_class = BookSerializer
"局部登录认证"
authentication_classes = [LoginAuth]
urls.py
- 路由
路由通过自动生成
# 生成路由
router = SimpleRouter()
router.register('user', views.UserView, 'user')
urlpatterns = [
path('api/v1/', include(router.urls)),
]