002---rest_framework认证组件
rest_framework认证组件
问题:有些API,需要用户登录才能访问,有些无需登录就能访问。
解决:
-
a. 创建两个模型类:UserInfo---OneToOne---UsetToken
-
b. 登录成功后保存在数据库的UserToken表中
# urls.py from django.urls import path from api import views urlpatterns = [ path('api/v1/auth/',views.AuthView.as_view()), ] # views.py import hashlib, time from rest_framework.views import APIView from rest_framework.response import Response from api import models def md5(username): """ 生成token :param username: :return: """ m = hashlib.md5(username.encode('utf-8')) m.update(str(time.time()).encode('utf-8')) return m.hexdigest() class AuthView(APIView): """ 用户登录 """ def post(self, request, *args, **kwargs): ret = {'code': 1000, 'msg': None} username = request._request.POST.get('username') password = request._request.POST.get('password') user = models.UserInfo.objects.filter(username=username, password=password).first() if not user: ret['msg'] = '用户名或密码错误' return Response(ret) token = md5(username) # 存在就更新,不存在就创建 models.UserToken.objects.update_or_create(user=user, defaults={'token': token}) ret['code'] = 2000 ret['msg'] = '登录成功' ret['token'] = token return Response(ret)
-
c. 创建自定义Auth认证类和订单,访问订单,判断是否携带token访问,不然不让访问
# urls.py
urlpatterns = [
path('api/v1/auth/',views.AuthView.as_view()),
path('api/v1/order/',views.OrderView.as_view()),
]
# views.py
ORDERS = [
{
'id': '1',
'name': '华为MATE 20 Pro',
'price': 6999,
},
{
'id': '2',
'name': '一加6t',
'price': 3999,
},
{
'id': '3',
'name': '狗比亚',
'price': 2699,
},
]
class Authentication(object):
"""
自定义认证类
"""
def authenticate(self, request):
token = request._request.GET.get('token')
token_obj = models.UserToken.objects.filter(token=token).first()
if not token_obj:
raise AuthenticationFailed('用户认证失败')
# 返回一个元祖 一个是用户对象。一个是token对象
return token_obj.user, token_obj
def authenticate_header(self, request):
pass
class OrderView(APIView):
"""
订单相关业务
"""
authentication_classes = [Authentication]
def get(self, request, *args, **kwargs):
ret = {'code': 2000, 'msg': '获取成功', 'data': ORDERS}
return Response(ret)
-
d. 测试接口:只测试了正确的用户名和密码,错误的自己测
import requests auth_url = 'http://127.0.0.1:8000/api/v1/auth/' res = requests.post(auth_url, data={'username': 'alex', 'password': 'root1234'}).json() print(res) # {'code': 2000, 'msg': '登录成功', 'token': '02fdedaa2c6c5773e00706b5890e289a'} token = res.get('token') order_url = 'http://127.0.0.1:8000/api/v1/order/' res = requests.get(order_url,params={'token':token}).json() print(res) # {'code': 2000, 'msg': '获取成功', 'data': [{'id': '1', 'name': '华为MATE 20 Pro', 'price': 6999}, {'id': '2', 'name': '一加6t', 'price': 3999}, {'id': '3', 'name': '狗比亚', 'price': 2699}]}
-
e. 再写一个API用户获取用户信息(通过认证通过后注册的request.user对象和request.token对象)
urlpatterns = [ path('api/v1/auth/',views.AuthView.as_view()), path('api/v1/order/',views.OrderView.as_view()), path('api/v1/user_info/',views.UserInfoView.as_view()), ] class UserInfoView(APIView): """ 获取用户信息相关 """ authentication_classes = [Authentication] def get(self, request, *args, **kwargs): data = { 'username': request.user.username, 'token': request.auth.token } ret = {'code': 2000, 'msg': '获取成功', 'data': data} return Response(ret)
-
f. 测试认证成功之后返回元祖的两个对象
import requests auth_url = 'http://127.0.0.1:8000/api/v1/auth/' res = requests.post(auth_url, data={'username': 'alex', 'password': 'root1234'}).json() print(res) # {'code': 2000, 'msg': '登录成功', 'token': '05810e2756bc6cc27c1ce3af827aa45f'} token = res.get('token') order_url = 'http://127.0.0.1:8000/api/v1/user_info/' res = requests.get(order_url,params={'token':token}).json() print(res) # {'code': 2000, 'msg': '获取成功', 'data': {'username': 'alex', 'token': '05810e2756bc6cc27c1ce3af827aa45f'}}
全局使用(再次看源码)
def get_authenticators(self):
return [auth() for auth in self.authentication_classes]
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
def reload_api_settings(*args, **kwargs):
setting = kwargs['setting']
if setting == 'REST_FRAMEWORK':
api_settings.reload()
# 所以通过配置api.settings可达到全局使用的效果
- 全局使用
# 在api应用下面创建utils\auth.py 把之前写的自定义类剪切过来
# utils\auth.py
from api import models
from rest_framework.exceptions import AuthenticationFailed
from rest_framework.authentication import BaseAuthentication
class Authentication(BaseAuthentication):
"""
自定义认证类
"""
def authenticate(self, request):
token = request._request.GET.get('token')
token_obj = models.UserToken.objects.filter(token=token).first()
if not token_obj:
raise AuthenticationFailed('用户认证失败')
# 返回一个元祖 一个是用户对象。一个是token对象
return token_obj.user, token_obj
def authenticate_header(self, request):
pass
# settings.py
##############rest_framework配置################
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES':['api.utils.auth.Authentication']
}
# views.py
import hashlib, time
from rest_framework.views import APIView
from rest_framework.response import Response
from api import models
ORDERS = [
{
'id': '1',
'name': '华为MATE 20 Pro',
'price': 6999,
},
{
'id': '2',
'name': '一加6t',
'price': 3999,
},
{
'id': '3',
'name': '狗比亚',
'price': 2699,
},
]
def md5(username):
"""
生成token
:param username:
:return:
"""
m = hashlib.md5(username.encode('utf-8'))
m.update(str(time.time()).encode('utf-8'))
return m.hexdigest()
class AuthView(APIView):
"""
用户登录
"""
# 不需要认证为空列表即可
authentication_classes = []
def post(self, request, *args, **kwargs):
ret = {'code': 1000, 'msg': None}
username = request._request.POST.get('username')
password = request._request.POST.get('password')
user = models.UserInfo.objects.filter(username=username, password=password).first()
if not user:
ret['msg'] = '用户名或密码错误'
return Response(ret)
token = md5(username)
# 存在就更新,不存在就创建
models.UserToken.objects.update_or_create(user=user, defaults={'token': token})
ret['code'] = 2000
ret['msg'] = '登录成功'
ret['token'] = token
return Response(ret)
class OrderView(APIView):
"""
订单相关业务
"""
def get(self, request, *args, **kwargs):
ret = {'code': 2000, 'msg': '获取成功', 'data': ORDERS}
return Response(ret)
class UserInfoView(APIView):
"""
获取用户信息相关
"""
def get(self, request, *args, **kwargs):
data = {
'username': request.user.username,
'token': request.auth.token
}
ret = {'code': 2000, 'msg': '获取成功', 'data': data}
return Response(ret)
梳理:
-
全局使用:创建自定义认证类,继承BaseAuthentication,实现:authenticate()
-
局部使用(使用自带的认证类):authentication_classes = [BasicAuthentication,]
-
该方法有三种返回值
- None 当前认证类不做认证,交给下一个认证来来执行
- 抛出异常 raise AuthenticationFailed('用户认证失败')
- 元祖(元素1,元素2):元素1赋值给request.user;元素2赋值给request.token
-
注意:当所有认证类都返回None时,此时是匿名用户,默认是AnonymousUser,可以通过配置文件来更改显示
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES":['api.utils.auth.FirstAuthtication','api.utils.auth.Authtication', ],
#"UNAUTHENTICATED_USER":lambda :"匿名用户",
"UNAUTHENTICATED_USER":None, # 匿名,request.user = None
"UNAUTHENTICATED_TOKEN":None,# 匿名,request.auth = None
}
源码流程
-
dispath()-->initialize_request()
- 封装request对象,获取认证类(全局&局部),通过列表生成式创建认证类对象 -
initial-->perform_authentication()-->request.user
- for循环每个认证类 return -
认证成功基于反射根据method不同做不同处理