登录的token操作
# app.models.py:表结构
class User(models.Model):
user = models.CharField(max_length=32)
pwd = models.CharField(max_length=32)
class UserToken(models.Model):
token = models.CharField(max_length=64)
user = models.OneToOneField(to='User')
# app.objson.py:序列化模块
from rest_framework import serializers
from app import models
class UserJson(serializers.ModelSerializer):
class Meta:
model = models.User
fields = '__all__'
# app.views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from app import common, models, objson
class Login(APIView):
def post(self, request):
# 用前台提交的数据完成数据库查询校验
data_dic = request.data
user = models.User.objects.filter(**data_dic).first()
if user:
# 登录成功操作token
token = common.get_token()
# token的数据库操作,第一次产生token是新建,再次就是更新
models.UserToken.objects.update_or_create(user=user, defaults={"token": token})
user_data = objson.UserJson(user).data
return Response({
"status": 0,
"msg": 'login success',
"token": token, # 将token返回给前台
"results": user_data
})
return Response({
"status": 1,
"msg": 'login failed'
})
认证方法的实现
# 源码分析
# as_view() => APIView.dispatch => self.initial(request, *args, **kwargs) => 封装后的drf的request => request.user => self._authenticate() => authenticate(self, request) 的源码根据地
# app.views.py
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
class LoginAuthenticate(BaseAuthentication):
def authenticate(self, request):
# 登录逻辑:如果用户登录了,登录操作产生了token,且前后台同步了
# 登录判断:再次发生的请求,没有token代表没登录,错误token,代表无效的登录,token正确才是正常的登录用户
# 如何将token取出, 规定token用请求头传递
token = request.META.get('HTTP_TOKEN')
result = models.UserToken.objects.filter(token=token).first()
if result:
# 认证通过,可以返回None (有多个认证时),可以返回两个值:user,auth
return result.user, token
else:
# 验证失败,抛出APIException或其子类对象
raise AuthenticationFailed("认证失败")
class Books(APIView):
authentication_classes = [LoginAuthenticate]
# 视图函数处理的逻辑
def get(self, request):
# 通过认证后,用request.user拿到当前登录的用户,用request.auth拿到认证值(该值通常自定义)
print(request.user)
return Response({
"status": 0,
"msg": 'ok',
"results": []
})
局部认证
# app.auth.py 完成校验的所有逻辑
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from app import models
class LoginAuthenticate(BaseAuthentication):
def authenticate(self, request):
token = request.META.get('HTTP_TOKEN')
result = models.UserToken.objects.filter(token=token).first()
if result:
return result.user, token
else:
raise AuthenticationFailed("认证失败")
# 局部认证: 在需要认证的类上方标注认证类:authentication_classes = [auth1, ... authn]
from app import auth
class Books(APIView):
authentication_classes = [auth.LoginAuthenticate]
def get(self, request):
print(request.user)
return Response({
"status": 0,
"msg": 'ok',
"results": []
})
# 案例:访问当前登录用户信息 - 个人主页
class Home(APIView):
# 添加登录认证即可
authentication_classes = [auth.LoginAuthenticate]
def get(self, request):
return Response({
"status": 0,
"msg": 'ok',
"results": objson.UserJson(request.user).data
})
全局认证
# 1.在settings.py中配置
# 全局认证
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication',
'app.auth.LoginAuthenticate'
),
}
# 2.所有的CBV类都不需要添加类属性:authentication_classes
# 局部禁用
# 3.在不需要认证的CBV类中添加类属性:authentication_classes = []
注销:在全局认证情况下
class Logout(APIView):
# 在全局认证情况下,如果能走get方法,代表已经通过登录认证(登录状态),就可以注销
def get(self, request):
models.UserToken.objects.update_or_create(user=request.user, defaults={'token': common.get_token()})
return Response({
"status": 0,
"msg": 'logout success',
})
补充:前台操作cookie
<body>
<button class="login">登录请求</button>
<button class="token">获取token</button>
</body>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdn.bootcss.com/jquery-cookie/1.4.1/jquery.cookie.min.js"></script>
<script>
$('.login').click(function () {
token = '88888';
$.cookie('qt', '前台操作cookie');
$.cookie('token', token);
});
$('.token').click(function () {
alert($.cookie('token'))
})
</script>