一 什么是RESTful
- REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为“表征状态转移”
- REST从资源的角度类审视整个网络,它将分布在网络中某个节点的资源通过URL进行标识,客户端应用通过URL来获取资源的表征,获得这些表征致使这些应用转变状态
- 所有的数据,不管是通过网络获取的还是操作(增删改查)的数据,都是资源,将一切数据视为资源是REST区别与其他架构风格的最本质属性
- 对于REST这种面向资源的架构风格,有人提出一种全新的结构理念,即:面向资源架构(ROA:Resource Oriented Architecture)
二. RESTful API设计
- API与用户的通信协议,总是使用HTTPs协议。
- 域名
- https://api.example.com 尽量将API部署在专用域名(会存在跨域问题)
- https://example.org/api/ API很简单
- 版本
- URL,如:https://api.example.com/v1/
- 请求头 跨域时,引发发送多次请求
- 路径,视网络上任何东西都是资源,均使用名词表示(可复数)
- https://api.example.com/v1/zoos
- https://api.example.com/v1/animals
- https://api.example.com/v1/employees
- method
- GET :从服务器取出资源(一项或多项)
- POST :在服务器新建一个资源
- PUT :在服务器更新资源(客户端提供改变后的完整资源)
- PATCH :在服务器更新资源(客户端提供改变的属性)
- DELETE :从服务器删除资源
- 过滤,通过在url上传参的形式传递搜索条件
- https://api.example.com/v1/zoos?limit=10:指定返回记录的数量
- https://api.example.com/v1/zoos?offset=10:指定返回记录的开始位置
- https://api.example.com/v1/zoos?page=2&per_page=100:指定第几页,以及每页的记录数
- https://api.example.com/v1/zoos?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序
- https://api.example.com/v1/zoos?animal_type_id=1:指定筛选条件
- 状态码
OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。 Accepted - [*]:表示一个请求已经进入后台排队(异步任务) NO CONTENT - [DELETE]:用户删除数据成功。 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。 更多看这里:http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html 常用状态码列表
- 错误处理,状态码是4xx时,应返回错误信息,error当做key。
123{
error:
"Invalid API key"
}
- 返回结果,针对不同操作,服务器向用户返回的结果应该符合以下规范。
123456GET
/
collection:返回资源对象的列表(数组)
GET
/
collection
/
resource:返回单个资源对象
POST
/
collection:返回新生成的资源对象
PUT
/
collection
/
resource:返回完整的资源对象
PATCH
/
collection
/
resource:返回完整的资源对象
DELETE
/
collection
/
resource:返回一个空文档
- Hypermedia API,RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。
123456{
"link"
: {
"rel"
:
"collection https://www.example.com/zoos"
,
"href"
:
"https://api.example.com/zoos"
,
"title"
:
"List of zoos"
,
"type"
:
"application/vnd.yourformat+json"
}}
摘自:http://www.ruanyifeng.com/blog/2014/05/restful_api.html
三 认证
1 认证简单使用
urls.py
from django.conf.urls import url from django.contrib import admin from api import views urlpatterns = [ url(r'api/v1/auth/$',views.Auth.as_view()), url(r'api/v1/order/$',views.Order.as_view()), url(r'api/v1/userinfo/$',views.UserInfo.as_view()), ]
modules.py
from django.db import models class UserInfo(models.Model): user_type_choice = ( (1,'普通用户'), (2,'VIP'), (3,'SVIP'), ) user_type = models.IntegerField(choices=user_type_choice) username = models.CharField(max_length=64,unique=True) password = models.CharField(max_length=128) class Token(models.Model): user = models.OneToOneField(to='UserInfo') token = models.CharField(max_length=64)
views.py
from django.shortcuts import render,HttpResponse from django.http import JsonResponse from rest_framework.views import APIView from api import models from api.utils import auth ORDER_DICT = { 1:{ 'name': "媳妇", 'age':18, 'gender':'男', 'content':'...' }, 2:{ 'name': "老狗", 'age':19, 'gender':'男', 'content':'...。。' }, } # 生成随机字符串当作token def md5(user): import hashlib import time ctime = str(time.time()) m = hashlib.md5('user'.encode('utf-8')) m.update(ctime.encode('utf-8')) return m.hexdigest() class Auth(APIView): """ 用于用户登陆认证,生成token,并插入到表中 """ authentication_classes = [] def post(self,request,*args,**kwargs): ret = {'code':1000, 'msg':None} try: user = request._request.POST.get('username') pwd = request._request.POST.get('password') print(user,pwd) obj = models.UserInfo.objects.filter(username=user,password=pwd).first() print(obj) if not obj: ret['code'] = 1001 ret['msg'] = '用户名或者密码不存在' # 为用户创建token token = md5(user) # 不存在就创建,存在就更新 models.Token.objects.update_or_create(user=obj,defaults={'token':token}) ret['token'] = token except Exception as e: ret['code'] = 1002 ret['msg'] = '请求异常' return JsonResponse(ret) class Order(APIView): def get(self,request,*args,**kwargs): self.dispatch ret = {'code':1000, 'msg':None, 'data':None} try: ret['data'] = ORDER_DICT except Exception as e: pass return JsonResponse(ret) class UserInfo(APIView): def get(self,request,*args,**kwargs): return HttpResponse('用户信息')
utils/auth.py
from rest_framework import exceptions from rest_framework.authentication import BaseAuthentication from api import models class Authencation(BaseAuthentication): def authenticate(self, request): token = request._request.GET.get('token') token_obj = models.Token.objects.filter(token=token).first() if not token_obj: raise exceptions.AuthenticationFailed('用户认证失败') return (token_obj.user,token_obj) def authenticate_header(self, request): return 'Basic realm="api"'
settings.py
REST_FRAMEWORK = { "DEFAULT_AUTHENTICATION_CLASSES" : ['api.utils.auth.Authencation',] }
使用:
1)获取token
2)得到token,并使用
下面我们对认证的源码进行分析
2 djangorestframework认证源码流程
1)一切从urls.py文件开始,其他关于django流程省略
urlpatterns = [ # url(r'^admin/', admin.site.urls), url(r'api/v1/auth/$',views.Auth.as_view()), url(r'api/v1/order/$',views.Order.as_view()), url(r'api/v1/userinfo/$',views.UserInfo.as_view()), ]
首先执行api/v1/auth这个url,然后执行views.Auth.as_view()这个函数,这里需要注意的是,执行views.Auth.as_view(),就相当于是views.Auth.as_view()()
2)首先查看views.Auth这个类中是否有as_view()方法,如果没有执行父类中的as_view()方法
@classmethod
def as_view(cls, **initkwargs):
"""
Store the original class on the view function.
This allows us to discover information about the view when we do URL
reverse lookups. Used for breadcrumb generation.
"""
if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet):
def force_evaluation():
raise RuntimeError(
'Do not evaluate the `.queryset` attribute directly, '
'as the result will be cached and reused between requests. '
'Use `.all()` or call `.get_queryset()` instead.'
)
cls.queryset._fetch_all = force_evaluation
# 执行APIView父类的as_view方法,并将返回值return view赋值给view
view = super(APIView, cls).as_view(**initkwargs)
view.cls = cls
view.initkwargs = initkwargs
# Note: session based authentication is explicitly CSRF validated,
# all other authentication is CSRF exempt.
return csrf_exempt(view)
执行到这里
view = super(APIView, cls).as_view(**initkwargs)
这里的cls表示类,这里特指Auth类,执行父类的as_view()
# 这是一个类方法 @classonlymethod def as_view(cls, **initkwargs): """ Main entry point for a request-response process. """ for key in initkwargs: if key in cls.http_method_names: raise TypeError("You tried to pass in the %s method name as a " "keyword argument to %s(). Don't do that." % (key, cls.__name__)) if not hasattr(cls, key): raise TypeError("%s() received an invalid keyword %r. as_view " "only accepts arguments that are already " "attributes of the class." % (cls.__name__, key)) def view(request, *args, **kwargs): # self = OrderView() self = cls(**initkwargs) if hasattr(self, 'get') and not hasattr(self, 'head'): self.head = self.get self.request = request self.args = args self.kwargs = kwargs # handler = getattr(self, request.method.lower(), self.http_method_not_allowed) # return handler(request, *args, **kwargs) # 这个as_view()返回了一个OrderView().dispatch(request, *args, **kwargs)方法 # 这里返回了dispatch(),所以这个dispath方法会被执行,但是需要从头开始找起 return self.dispatch(request, *args, **kwargs) view.view_class = cls view.view_initkwargs = initkwargs # take name and docstring from class """ WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__qualname__', '__doc__', '__annotations__') 这里把cls的上面的属性通过getattr拿到,然后setattr赋值给view,并且 wrapper.__wrapped__ = wrapped表示view.__cls__ = cls,然后返回view """ update_wrapper(view, cls, updated=()) # and possible attributes set by decorators # like csrf_exempt from dispatch """ WRAPPER_UPDATES = ('__dict__',) for attr in updated: getattr(wrapper, attr).update(getattr(wrapped, attr, {})) getattr(wrapper, attr)就是getattr(view, attr)返回值是一个字典 getattr(wrapped, attr, {})就是getattr(cls.dispatch, attr, {}) 返回view """ update_wrapper(view, cls.dispatch, assigned=()) # 由于调用url会views.OrderView.as_view()()所以,这里会执行view函数 return view
这里返回view,上面的as_view()也是返回view,
3)开始执行view,这里为什么会执行view啊,命名返回的是一个view而不是view(),因为在urls.py中调用的时候是实际上是执行的views.Auth.as_view()(),这里早在1)时已经说明。
这里的view方法,直接是在View类中as_view方法中的view方法。这个方法返回 return self.dispatch(request, *args, **kwargs),这个需要先从Auth类中查找,没有的话,就去APIView类中查找,可以找到并执行他。
def dispatch(self, request, *args, **kwargs):
"""
`.dispatch()` is pretty much the same as Django's regular dispatch,
but with extra hooks for startup, finalize, and exception handling.
"""
self.args = args
self.kwargs = kwargs
"""
return Request(
request,
parsers=self.get_parsers(),
# 认证相关,类的对象,先从类中找,找不到最后可以读取配置文件
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
"""
# 返回一个Request对象(这个对象不是原来的request对象)
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers # deprecate?
try:
self.initial(request, *args, **kwargs)
# Get the appropriate handler method
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(),
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
response = handler(request, *args, **kwargs)
except Exception as exc:
response = self.handle_exception(exc)
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
执行到这里之后,主要就是对request进行封装,然后把封装后的request对象赋值给新的request
request = self.initialize_request(request, *args, **kwargs)
跳转到,这里返回一个Request对象
def initialize_request(self, request, *args, **kwargs): """ Returns the initial request object. """ """ return { 'view': self, 'args': getattr(self, 'args', ()), 'kwargs': getattr(self, 'kwargs', {}) } """ parser_context = self.get_parser_context(request) return Request( request, # 这里是原始的request # return [parser() for parser in self.parser_classes] parsers=self.get_parsers(), # return [auth() for auth in self.authentication_classes] # 这里需要注意的是返回的是类(),是对象 authenticators=self.get_authenticators(), # 认证相关 negotiator=self.get_content_negotiator(), parser_context=parser_context )
def get_authenticators(self): """ Instantiates and returns the list of authenticators that this view can use. """ # 返回一个列表生成式 return [auth() for auth in self.authentication_classes]
基本上都是返回要给列表生成式
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
这里有一个配置文件,从这里可以看出来。这里的self.authentication_classes先从Auth类中开始查找,没有的话,读取上面的api_settings中的配置文件。这里很明显2者都没有。于是开始执行
再写一遍吧。这里有点啰嗦
def dispatch(self, request, *args, **kwargs):
"""
`.dispatch()` is pretty much the same as Django's regular dispatch,
but with extra hooks for startup, finalize, and exception handling.
"""
self.args = args
self.kwargs = kwargs
"""
return Request(
request,
parsers=self.get_parsers(),
# 认证相关,类的对象,先从类中找,找不到最后可以读取配置文件
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
"""
# 返回一个Request对象(这个对象不是原来的request对象)
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers # deprecate?
try:
self.initial(request, *args, **kwargs)
# Get the appropriate handler method
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(),
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
response = handler(request, *args, **kwargs)
except Exception as exc:
response = self.handle_exception(exc)
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
开始执行标红的部分。
a. 开始判断请求的方法是否在被允许的方法名中,注意这里的request已经不是原装的request了,而是封装过的。如果要调用原装的request,request._request,但是这里为啥还能使用reqeust.method这就涉及到其他的了,暂时先不提。先看一下http_method_names都有哪些吧。
http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
有上面这么多。然后执行handler,这里使用了反射,handler具体就是get,post,put,delete等等的对象了。而不是字符串了,getattr可以把字符串反射为对象。
看到
response = handler(request, *args, **kwargs)
从这里可以看到我们定义get,post等方法的时候传递的参数,这里已经写好了。最后返回response,然后开始执行Auth类中的get,post,等方法。
############################################## 总结 认证源码流程 ##############################################
1 源码流程图
1 ) 请求先到dispatch
dispatch()主要做了两件事
- 封装request
- 认证
具体看我写的代码里面的注释
def dispatch(self, request, *args, **kwargs): """ `.dispatch()` is pretty much the same as Django's regular dispatch, but with extra hooks for startup, finalize, and exception handling. """ self.args = args self.kwargs = kwargs #对原始request进行加工,丰富了一些功能 #Request( # request, # parsers=self.get_parsers(), # authenticators=self.get_authenticators(), # negotiator=self.get_content_negotiator(), # parser_context=parser_context # ) #request(原始request,[BasicAuthentications对象,]) #获取原生request,request._request #获取认证类的对象,request.authticators #1.封装request request = self.initialize_request(request, *args, **kwargs) self.request = request self.headers = self.default_response_headers # deprecate? try: #2.认证 self.initial(request, *args, **kwargs) # Get the appropriate handler method if request.method.lower() in self.http_method_names: handler = getattr(self, request.method.lower(), self.http_method_not_allowed) else: handler = self.http_method_not_allowed response = handler(request, *args, **kwargs) except Exception as exc: response = self.handle_exception(exc) self.response = self.finalize_response(request, response, *args, **kwargs) return self.response
1.1.reuqest
1)initialize_request()
可以看到initialize()就是封装原始request
def initialize_request(self, request, *args, **kwargs): """ Returns the initial request object. """ parser_context = self.get_parser_context(request) return Request( request, parsers=self.get_parsers(), authenticators=self.get_authenticators(), #[BasicAuthentication(),],把对象封装到request里面了 negotiator=self.get_content_negotiator(), parser_context=parser_context )
(2)get_authenticators()
通过列表生成式,返回对象的列表
def get_authenticators(self): """ Instantiates and returns the list of authenticators that this view can use. """ return [auth() for auth in self.authentication_classes]
(3)authentication_classes
APIView里面有个 authentication_classes 字段
可以看到默认是去全局的配置文件找(api_settings)
class APIView(View): # The following policies may be set at either globally, or per-view. renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES parser_classes = api_settings.DEFAULT_PARSER_CLASSES authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES content_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS metadata_class = api_settings.DEFAULT_METADATA_CLASS versioning_class = api_settings.DEFAULT_VERSIONING_CLASS
1.2.认证
self.initial(request, *args, **kwargs)
def dispatch(self, request, *args, **kwargs): """ `.dispatch()` is pretty much the same as Django's regular dispatch, but with extra hooks for startup, finalize, and exception handling. """ self.args = args self.kwargs = kwargs #对原始request进行加工,丰富了一些功能 #Request( # request, # parsers=self.get_parsers(), # authenticators=self.get_authenticators(), # negotiator=self.get_content_negotiator(), # parser_context=parser_context # ) #request(原始request,[BasicAuthentications对象,]) #获取原生request,request._request #获取认证类的对象,request.authticators #1.封装request request = self.initialize_request(request, *args, **kwargs) self.request = request self.headers = self.default_response_headers # deprecate? try: #2.认证 self.initial(request, *args, **kwargs) # Get the appropriate handler method if request.method.lower() in self.http_method_names: handler = getattr(self, request.method.lower(), self.http_method_not_allowed) else: handler = self.http_method_not_allowed response = handler(request, *args, **kwargs) except Exception as exc: response = self.handle_exception(exc) self.response = self.finalize_response(request, response, *args, **kwargs) return self.response
(1)initial()
主要看 self.perform_authentication(request),实现认证
def initial(self, request, *args, **kwargs): """ Runs anything that needs to occur prior to calling the method handler. """ self.format_kwarg = self.get_format_suffix(**kwargs) # Perform content negotiation and store the accepted info on the request neg = self.perform_content_negotiation(request) request.accepted_renderer, request.accepted_media_type = neg # Determine the API version, if versioning is in use. version, scheme = self.determine_version(request, *args, **kwargs) request.version, request.versioning_scheme = version, scheme # Ensure that the incoming request is permitted #3.实现认证 self.perform_authentication(request) self.check_permissions(request) self.check_throttles(request)
(2)perform_authentication()
调用了request.user
def perform_authentication(self, request): """ Perform authentication on the incoming request. Note that if you override this and simply 'pass', then authentication will instead be performed lazily, the first time either `request.user` or `request.auth` is accessed. """ request.user
(3)user
request.user的request的位置
这里的request是加工过的request
点进去可以看到Request有个user方法,加 @property 表示调用user方法的时候不需要加括号“user()”,可以直接调用:request.user
@property def user(self): """ Returns the user associated with the current request, as authenticated by the authentication classes provided to the request. """ if not hasattr(self, '_user'): with wrap_attributeerrors(): #获取认证对象,进行一步步的认证 self._authenticate() return self._user
(4)_authenticate()
循环所有authenticator对象
def _authenticate(self): """ Attempt to authenticate the request using each authentication instance in turn. """ #循环认证类的所有对象 #执行对象的authenticate方法 for authenticator in self.authenticators: try: #执行认证类的authenticate方法 #这里分三种情况 #1.如果authenticate方法抛出异常,self._not_authenticated()执行 #2.有返回值,必须是元组:(request.user,request.auth) #3.返回None,表示当前认证不处理,等下一个认证来处理 user_auth_tuple = authenticator.authenticate(self) except exceptions.APIException: self._not_authenticated() raise if user_auth_tuple is not None: self._authenticator = authenticator self.user, self.auth = user_auth_tuple return self._not_authenticated()
返回值就是例子中的:
token_obj.user-->>request.user
token_obj-->>request.auth
#在rest framework内部会将这两个字段赋值给request,以供后续操作使用 return (token_obj.user,token_obj) #例子中的return
当都没有返回值,就执行self._not_authenticated(),相当于匿名用户,没有通过认证
def _not_authenticated(self): """ Set authenticator, user & authtoken representing an unauthenticated request. Defaults are None, AnonymousUser & None. """ self._authenticator = None if api_settings.UNAUTHENTICATED_USER: self.user = api_settings.UNAUTHENTICATED_USER() #AnonymousUser匿名用户 else: self.user = None if api_settings.UNAUTHENTICATED_TOKEN: self.auth = api_settings.UNAUTHENTICATED_TOKEN() #None else: self.auth = None
面向对象知识:
子类继承 父类,调用方法的时候:
- 优先去自己里面找有没有这个方法,有就执行自己的
- 只有当自己里面没有这个方法的时候才会去父类找
因为authenticate方法我们自己写,所以当执行authenticate()的时候就是执行我们自己写的认证
https://www.cnblogs.com/wanstack/articles/7065822.html
父类中的authenticate方法
def authenticate(self, request): return (self.force_user, self.force_token)
我们自己写的
class Authentication(APIView): '''用于用户登录验证''' def authenticate(self,request): token = request._request.GET.get('token') token_obj = models.UserToken.objects.filter(token=token).first() if not token_obj: raise exceptions.AuthenticationFailed('用户认证失败') #在rest framework内部会将这两个字段赋值给request,以供后续操作使用 return (token_obj.user,token_obj)
认证的流程就是上面写的,弄懂了原理,再写代码就更容易理解为什么了。
1.3.配置文件
class APIView(View): # The following policies may be set at either globally, or per-view. renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES parser_classes = api_settings.DEFAULT_PARSER_CLASSES authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES content_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS metadata_class = api_settings.DEFAULT_METADATA_CLASS versioning_class = api_settings.DEFAULT_VERSIONING_CLASS
默认是去全局配置文件中找,所以我们应该在settings.py中配置好路径
api_settings源码
api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS) def reload_api_settings(*args, **kwargs): setting = kwargs['setting'] if setting == 'REST_FRAMEWORK': api_settings.reload()
setting中‘REST_FRAMEWORK’中找
全局配置方法:
API文件夹下面新建文件夹utils,再新建auth.py文件,里面写上认证的类
settings.py
#设置全局认证 REST_FRAMEWORK = { "DEFAULT_AUTHENTICATION_CLASSES":['API.utils.auth.Authentication',] #里面写你的认证的类的路径 }
auth.py
# API/utils/auth.py from rest_framework import exceptions from API import models 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 exceptions.AuthenticationFailed('用户认证失败') #在rest framework内部会将这两个字段赋值给request,以供后续操作使用 return (token_obj.user,token_obj) def authenticate_header(self, request): pass
在settings里面设置的全局认证,所有业务都需要经过认证,如果想让某个不需要认证,只需要在其中添加下面的代码:
authentication_classes = [] #里面为空,代表不需要认证
from django.shortcuts import render,HttpResponse from django.http import JsonResponse from rest_framework.views import APIView from API import models from rest_framework.request import Request from rest_framework import exceptions from rest_framework.authentication import BasicAuthentication ORDER_DICT = { 1:{ 'name':'apple', 'price':15 }, 2:{ 'name':'dog', 'price':100 } } 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): '''用于用户登录验证''' authentication_classes = [] #里面为空,代表不需要认证 def post(self,request,*args,**kwargs): ret = {'code':1000,'msg':None} try: user = request._request.POST.get('username') pwd = request._request.POST.get('password') obj = models.UserInfo.objects.filter(username=user,password=pwd).first() if not obj: ret['code'] = 1001 ret['msg'] = '用户名或密码错误' #为用户创建token token = md5(user) #存在就更新,不存在就创建 models.UserToken.objects.update_or_create(user=obj,defaults={'token':token}) ret['token'] = token except Exception as e: ret['code'] = 1002 ret['msg'] = '请求异常' return JsonResponse(ret) class OrderView(APIView): '''订单相关业务''' def get(self,request,*args,**kwargs): # self.dispatch #request.user #request.auth ret = {'code':1000,'msg':None,'data':None} try: ret['data'] = ORDER_DICT except Exception as e: pass return JsonResponse(ret) API/view.py代码
再测试一下我们的代码
不带token发请求
带token发请求
二 内置认证类
rest_framework里面内置了一些认证,我们自己写的认证类都要继承内置认证类 "BaseAuthentication"
2.1.BaseAuthentication源码
class BaseAuthentication(object): """ All authentication classes should extend BaseAuthentication. """ def authenticate(self, request): """ Authenticate the request and return a two-tuple of (user, token). """ #内置的认证类,authenticate方法,如果不自己写,默认则抛出异常 raise NotImplementedError(".authenticate() must be overridden.") def authenticate_header(self, request): """ Return a string to be used as the value of the `WWW-Authenticate` header in a `401 Unauthenticated` response, or `None` if the authentication scheme should return `403 Permission Denied` responses. """ #authenticate_header方法,作用是当认证失败的时候,返回的响应头 pass
2.2 修改自己写的认证类
自己写的Authentication必须继承内置认证类BaseAuthentication
# API/utils/auth/py from rest_framework import exceptions from API import models 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 exceptions.AuthenticationFailed('用户认证失败') #在rest framework内部会将这两个字段赋值给request,以供后续操作使用 return (token_obj.user,token_obj) def authenticate_header(self, request): pass
2.3 其它内置认证类
rest_framework里面还内置了其它认证类,我们主要用到的就是BaseAuthentication,剩下的很少用到
三 总结认证
自己写认证类方法梳理
(1)创建认证类
- 继承BaseAuthentication --->>1.重写authenticate方法;2.authenticate_header方法直接写pass就可以(这个方法必须写)
(2)authenticate()返回值(三种)
- None ----->>>当前认证不管,等下一个认证来执行
- raise exceptions.AuthenticationFailed('用户认证失败') # from rest_framework import exceptions
- 有返回值元祖形式:(元素1,元素2) #元素1复制给request.user; 元素2复制给request.auth
(3)局部使用
- authentication_classes = [BaseAuthentication,]
(4)全局使用
#设置全局认证 REST_FRAMEWORK = { "DEFAULT_AUTHENTICATION_CLASSES":['API.utils.auth.Authentication',] }
源码流程
--->>dispatch
--封装request
---获取定义的认证类(全局/局部),通过列表生成式创建对象
---initial
----peform_authentication
-----request.user (每部循环创建的对象)