验证类可以局部使用(在单独的views中引入), 也可以全局使用(在settings中配置)
使用流程1: 就是先定义一个自定义的认证类(比如MyAuth),然后重写authenticate()方法,把需要认证的逻辑放到这个函数里面写,然后注意返回值是一个元组. 然后在需要使用这个验证类的view视图里面 用authentication_classes = [MyAuth,]列表包裹起来,就可以了.
使用流程2: 上述使用在需要的视图里面写上认证类的列表. 如果视图过多,则写的也多…所以还可以放到settings.py 文件中, 如下格式:
TIPS: 如果在全局中设置了认证类,表示所有的视图都会经过这个认证类,那么如果一些视图需要这个认证类,就只能在这个视图中单独设置authentication_classes = [] 就是这个列表为空,表示不处理.
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
REST_FRAMEWORK = {
#全局使用的认证类
'DEFAULT_AUTHENTICATION_CLASSES':['api.utils.auth.FirstAuthenticate'] #注意这个格式, 这里的DEFAULT_AUTHENTICATION_CLASSES是restframework 配置规定的, 后面的列表就是认证的类. 里面写的是认证类的路径
#下面两句是匿名用户的设置:
'UNAUTHENTICATED_USER':None, #匿名, 则request.user = None
'UNAUTHENTICATED_TOKEN':None #匿名, 则request.token = None
}
验证登陆是不是成功
自定义验证类, 继承BaseAuthentication, 这里继承这个类的好处就是我们只需要写一个authenticate()就可以了,另外一个因为是继承的关系,父类已经有了,就不用重写了,如下:
class MyAuthenticate1(BaseAuthentication):
def authenticate(self, request):
#验证用户带过来的token
token = request._request.GET.get('token')
token_obj = models.UserToken.objects.filter(token=token).first()
print(token)
print(token_obj)
if not token_obj:
raise exceptions.AuthenticationFailed('用户认证失败')
return (token_obj.user, token_obj) #必须返回一个元组, restframeworkd会见两个字段赋值给request,一共后续使用.
def authenticate_header(self, request):
pass
总结流程
第一步: 创建一个认证类: 继承自 BaseAuthentication 实现authenticate方法
- 返回值 有三种:
- None, 表示我不处理,留给下一个认证类处理.
- 失败, raise exceptions.AuthenticationFailed(‘用户认证失败’)
- 成功 会返回一个二元组, (元素1, 元素2) 他们将会赋值给request.user, request.auth
认证类的两种适用范围:
- 一种 局部使用, 只在特定试图中引用使用
- 全局使用, 在settings中写定
源代码如下:
项目的目录结构如下图:
1, models.py 如下:
from django.db import models
class UserInfo(models.Model):
user_type_choices = (
(1, '普通用户'),
(2, 'VIP'),
(3, 'SVIP')
)
user_type = models.IntegerField(choices=user_type_choices)
username = models.CharField(max_length=32, unique=True)
password = models.CharField(max_length=64)
class UserToken(models.Model):
user = models.OneToOneField(to=UserInfo, on_delete=models.CASCADE)
token = models.CharField(max_length=64)
2, urls.py 如下:
from django.contrib import admin
from django.urls import path
from django.conf.urls import url
from api import views
urlpatterns = [
path('admin/', admin.site.urls),
url(r'^api/v1/auth/$', views.AuthView.as_view()),
url(r'^api/v1/order/$', views.OrderView.as_view())
]
3, views.py
from django.shortcuts import render
from django.http import HttpResponse, JsonResponse
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.authentication import BasicAuthentication, BaseAuthentication
from rest_framework import exceptions
from api import models
from api.utils.auth import FirstAuthenticate
#这个md5函数采用用户和时间来生成md5的随机值,用来生成token
def md5(user):
import hashlib
import time
ctime = str(time.time())
m = hashlib.md5(bytes(user, encoding='utf-8'))
m.update(bytes(ctime, encoding='utf-8'))
return m.hexdigest()
class AuthView(APIView):
'''
用于用户登陆
这里拿到前台发过来的用户名和密码,然后去数据库校验,同时我们还生成了token,然后还给用户返回了token,用于用户后面发送请求,必须要
带上这个token的请求.
'''
authentication_classes = [] #这里因为在全局中设置了验证类,所以在这里要把这个列表为空,好让这个视图可以不经过全局的验证,直接执行.
def get(self, request, *args, **kwargs):
return HttpResponse('hello workd')
def post(self, request, *args, **kwargs):
ret = {'code':1000, 'msg':None}
try:
user = request._request.POST.get('username')
password = request._request.POST.get('password')
obj = models.UserInfo.objects.filter(username=user, password=password).first()
if not obj:
ret['code'] = 1001
ret['msg'] = '用户名或者密码错误'
#未登陆用户创建token,采用上面定义的md5()函数来生成token
token = md5(user)
#保存金数据库,有就更新,没有就创建,因为是只能用最新的token,所以需要更新操作.
models.UserToken.objects.update_or_create(user=obj, defaults={'token':token})
#需要给用户返回token
ret['token'] = token
except Exception as e:
ret['code']=1002
ret['msg']='请求异常'
return JsonResponse(ret)
#定义一个订单的字典, 用字典模拟订单
ORDER_DICT= {
1: {
'name': '购买力平价',
'age': 12,
'gender': '男',
'content': '...'
},
2: {
'name': '假数据',
'age': 12,
'gender': '男',
'content': '...'
},
}
#自定义验证类, 没有继承
class MyAuthenticate(object):
def authenticate(self, request):
#验证用户带过来的token
token = request._request.GET.get('token')
token_obj = models.UserToken.objects.filter(token=token).first()
print(token)
print(token_obj)
if not token_obj:
raise exceptions.AuthenticationFailed('用户认证失败')
return (token_obj.user, token_obj) #必须返回一个元组, restframeworkd会见两个字段赋值给request,一共后续使用.
def authenticate_header(self, request):
pass
#自定义验证类, 继承BaseAuthentication, 这里继承这个类的好处就是我们只需要写一个authenticate()就可以了,另外一个因为是继承的关系,父类已经有了,
#就不用重写了.
class MyAuthenticate1(BaseAuthentication):
def authenticate(self, request):
#验证用户带过来的token
token = request._request.GET.get('token')
token_obj = models.UserToken.objects.filter(token=token).first()
print(token)
print(token_obj)
if not token_obj:
raise exceptions.AuthenticationFailed('用户认证失败')
return (token_obj.user, token_obj) #必须返回一个元组, restframeworkd会见两个字段赋值给request,一共后续使用.
def authenticate_header(self, request):
pass
class OrderView(APIView):
authentication_classes = [FirstAuthenticate, MyAuthenticate,] #用restframework的认证类做的, 格式: 首先写一个认证类,然后在这里这样用就可以了,逻辑代码在上面的自定义认证类中实现.
def get(self, request, *args, **kwargs):
print(request.user)
print(request.auth)
ret = {'code':1000, 'msg':None, 'data':None} #用于标实是不是请求成功 和数据
#标记用户登陆了才可以看到这里的数据,否则看不到数据
#这里就用上面说的token,,因为只有登陆了,才会生成token,才标实用户已经登陆了,否则没有token,就表示用户没有登陆
#这里的认证方法比较传统,没有用到restframework的认证类,如果有很多视图,则需要在每个视图中间都要写这个认证函数,
#我们可以自定义一个验证类,然后重写authenticate()方法,把验证登陆的语句写在里面,然后在这里是需要调用就可以了.一劳永逸
# token = request._request.GET.get('token')
# if not token:
# return HttpResponse('用户未登录,无法查看')
try:
ret['data'] = ORDER_DICT
except Exception as e:
pass
return JsonResponse(ret)
4, 自定义验证类 路径: api/utils/auth.py
from rest_framework import exceptions
from api import models
#自定义验证类
class FirstAuthenticate(object):
def authenticate(self, request):
#验证用户带过来的token
token = request._request.GET.get('token')
token_obj = models.UserToken.objects.filter(token=token).first()
print(token)
print(token_obj)
if not token_obj:
raise exceptions.AuthenticationFailed('用户认证失败')
return (token_obj.user, token_obj) #必须返回一个元组, restframeworkd会见两个字段赋值给request,一共后续使用.
def authenticate_header(self, request):
pass
5, settings.py 文件如下:
"""
Django settings for auth2 project.
Generated by 'django-admin startproject' using Django 3.0.5.
For more information on this file, see
https://docs.djangoproject.com/en/3.0/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.0/ref/settings/
"""
import os
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'p)rim!kr+qd(&x$b&p9!%6nwelaw%x)^y6optft1($79h@idro'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'api'
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'auth2.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join (BASE_DIR, 'templates')]
,
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'auth2.wsgi.application'
# Database
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
# Password validation
# https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/3.0/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.0/howto/static-files/
STATIC_URL = '/static/'
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES':['api.utils.auth.FirstAuthenticate',], #注意这个格式, 这里的DEFAULT_AUTHENTICATION_CLASSES是restframework 配置规定的, 后面的列表就是认证的类. 里面写的是认证类的路径
#下面两句是匿名用户的设置:
'UNAUTHENTICATED_USER':None, #匿名, 则request.user = None
'UNAUTHENTICATED_TOKEN':None #匿名, 则request.token = None
}