Django REST framework 之 API认证
RESTful API 认证
和 Web 应用不同,RESTful APIs 通常是无状态的, 也就意味着不应使用 sessions 或 cookies, 因此每个请求应附带某种授权凭证,因为用户授权状态可能没通过 sessions 或 cookies 维护, 常用的做法是每个请求都发送一个秘密的 access token 来认证用户, 由于 access token 可以唯一识别和认证用户, API 请求应通过 HTTPS 来防止 man-in-the-middle(MitM)中间人攻击。
通常有下面几种方式来发送 access token:
- HTTP 基本认证:access token 当作用户名发送,应用在 access token 可安全存在 API 使用端的场景, 例如,API 使用端是运行在一台服务器上的程序。
- 请求参数:access token 当作 API URL 请求参数发送,例如
https://example.com/users?access-token=xxxxxxxx
, 由于大多数服务器都会保存请求参数到日志, 这种方式应主要用于JSONP
请求,因为它不能使用HTTP头来发送access token - OAuth 2:使用者从认证服务器上获取基于 OAuth2 协议的 access token, 然后通过 HTTP Bearer Tokens 发送到 API 服务器。
Django REST framework 认证
一、身份验证
REST framework 提供了一些开箱即用的身份验证方案,并且还允许你实现自定义方案。这里需要明确一下用户认证(Authentication)和用户授权(Authorization)是两个不同的概念,认证解决的是“有没有”的问题,而授权解决的是“能不能”的问题。
- BasicAuthentication
该认证方案使用 HTTP Basic Authentication,并根据用户的用户名和密码进行签名。Basic Authentication 通常只适用于测试。
- SessionAuthentication
此认证方案使用 Django 的默认 session 后端进行认证。Session 身份验证适用于与您的网站在同一会话环境中运行的 AJAX 客户端。
- TokenAuthentication
此认证方案使用简单的基于令牌的 HTTP 认证方案。令牌身份验证适用于 client-server 架构,例如本机桌面和移动客户端。
- RemoteUserAuthentication
这种身份验证方案允许您将身份验证委托给您的 Web 服务器,该服务器设置 REMOTE_USER 环境变量。
默认的认证方案可以使用DEFAULT_AUTHENTICATION_CLASSES
全局设置,在settings.py
文件配置。在默认情况下,DRF开启了 BasicAuthentication 与 SessionAuthentication 的认证。
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
)
}
关于DRF,几乎所有的配置都定义在MREST_FRAMEWORK
变量中。另外,关于认证方式DRF默认会检测配置在DEFAULT_AUTHENTICATION_CLASSES
变量中的所有认证方式,只要有一个认证方式通过即可登录成功。这里的DEFAULT_AUTHENTICATION_CLASSES
与Django中的MIDDLEWARE
类似,在将request通过url映射到views之前,Django和DRF都会调用定义在MREST_FRAMEWORK
变量中的类的一些方法。
另外,你还可以使用基于APIView
类的视图,在每个视图或每个视图集的基础上设置身份验证方案。
from rest_framework.authentication import SessionAuthentication, BasicAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView class ExampleView(APIView):
authentication_classes = (SessionAuthentication, BasicAuthentication)
permission_classes = (IsAuthenticated,) def get(self, request, format=None):
content = {
'user': unicode(request.user), # `django.contrib.auth.User` instance.
'auth': unicode(request.auth), # None
}
return Response(content)
另外,DRF的认证是在定义有权限类(permission_classes)的视图下才有作用,且权限类(permission_classes)必须要求认证用户才能访问此视图。如果没有定义权限类(permission_classes),那么也就意味着允许匿名用户的访问,自然牵涉不到认证相关的限制了。所以,一般在项目中的使用方式是在全局配置DEFAULT_AUTHENTICATION_CLASSES
认证,然后会定义多个base views,根据不同的访问需求来继承不同的base views即可。
from rest_framework.permissions import (
IsAuthenticated,
IsAdminUser,
IsAuthenticatedOrReadOnly
) class BaseView(APIView):
'''普通用户'''
permission_classes = (
IsOwnerOrReadOnly,
IsAuthenticated
) class SuperUserpermissions(APIView):
'''超级用户'''
permission_classes = (IsAdminUser,) class NotLogin(APIView):
'''匿名用户'''
pass
另外,在前后端分离项目中一般不会使用 BasicAuthentication 与 SessionAuthentication 的认证方式。所以,我们只需要关心 TokenAuthentication 认证方式即可。
二、TokenAuthentication
要使用TokenAuthentication
方案,需要将认证类配置为包含TokenAuthentication
。
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication',
)
}
并在INSTALLED_APPS
设置中另外包含 rest_framework.authtoken
:
INSTALLED_APPS = (
...
'rest_framework.authtoken'
)
注意: rest_framework.authtoken应用一定要放到INSTALLED_APPS,并且确保在更改设置后运行python manage.py migrate
。 rest_framework.authtoken应用需要创建一张表用来存储用户与Token的对应关系。
数据库迁移完成后,可以看到多了一个authtoken_token表,表结构如下
mysql> show create table authtoken_token\G
*************************** 1. row ***************************
Table: authtoken_token
Create Table: CREATE TABLE `authtoken_token` (
`key` varchar(40) NOT NULL,
`created` datetime(6) NOT NULL,
`user_id` int(11) NOT NULL,
PRIMARY KEY (`key`),
UNIQUE KEY `user_id` (`user_id`),
CONSTRAINT `authtoken_token_user_id_35299eff_fk_auth_user_id` FOREIGN KEY (`user_id`) REFERENCES `auth_user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
其中“user_id”字段关联到了用户表。
配置URLconf
使用TokenAuthentication
时,你可能希望为客户提供一种机制,以获取给定用户名和密码的令牌。 REST framework 提供了一个内置的视图来支持这种行为。要使用它,请将obtain_auth_token
视图添加到您的 URLconf 中:
from rest_framework.authtoken import views
urlpatterns += [
url(r'^api-token-auth/', views.obtain_auth_token)
]
其中,r'^api-token-auth/'
部分实际上可以用任何你想使用URL替代。
创建Token
你还需要为用户创建令牌,用户令牌与用户是一一对应的。如果你已经创建了一些用户,则可以为所有现有用户生成令牌,例如:
from django.contrib.auth.models import User
from rest_framework.authtoken.models import Token for user in User.objects.all():
Token.objects.get_or_create(user=user)
你也可以为某个已经存在的用户创建Token:
for user in User.objects.filter(username='admin'): Token.objects.get_or_create(user=user)
创建成功后,会在Token表中生成对应的Token信息。
如果你希望每个用户都拥有一个自动生成的令牌,则只需捕捉用户的post_save
信号即可
from django.conf import settings
from django.db.models.signals import post_save
from django.dispatch import receiver
from rest_framework.authtoken.models import Token @receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_auth_token(sender, instance=None, created=False, **kwargs):
if created:
Token.objects.create(user=instance)
请注意,你需要确保将此代码片段放置在已安装的models.py
模块或 Django 启动时将导入的其他某个位置。
获取Token
上面虽然介绍了多种创建Token的方式,其实我们最简单的就是只需要配置一下urls.py
,然后就可以通过暴露的API来获取Token了。当使用表单数据或 JSON 将有效的username
和password
字段发布到视图时,obtain_auth_token
视图将返回 JSON 响应:
$ curl -d "username=admin&password=admin123456" http://127.0.0.1:8000/api-token-auth/
{"token":"684b41712e8e38549504776613bd5612ba997616"}
请注意,缺省的obtain_auth_token
视图显式使用 JSON 请求和响应,而不是使用你设置的默认的渲染器和解析器类。
当我们正常获取到Token后,obtain_auth_token
视图会自动帮我们在Token表中创建对应的Token。源码如下:
class ObtainAuthToken(APIView):
throttle_classes = ()
permission_classes = ()
parser_classes = (parsers.FormParser, parsers.MultiPartParser, parsers.JSONParser,)
renderer_classes = (renderers.JSONRenderer,)
serializer_class = AuthTokenSerializer
if coreapi is not None and coreschema is not None:
schema = ManualSchema(
fields=[
coreapi.Field(
name="username",
required=True,
location='form',
schema=coreschema.String(
title="Username",
description="Valid username for authentication",
),
),
coreapi.Field(
name="password",
required=True,
location='form',
schema=coreschema.String(
title="Password",
description="Valid password for authentication",
),
),
],
encoding="application/json",
) def post(self, request, *args, **kwargs):
serializer = self.serializer_class(data=request.data,
context={'request': request})
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
token, created = Token.objects.get_or_create(user=user)
return Response({'token': token.key}) obtain_auth_token = ObtainAuthToken.as_view()
默认情况下,没有权限或限制应用于obtain_auth_token
视图。 如果您希望应用throttling
,则需要重写视图类,并使用throttle_classes
属性包含它们。
如果你需要自定义obtain_auth_token
视图,你可以通过继承ObtainAuthToken
视图类来实现,并在你的urls.py
中使用它。例如,你可能会返回超出token
值的其他用户信息:
from rest_framework.authtoken.views import ObtainAuthToken
from rest_framework.authtoken.models import Token
from rest_framework.response import Response class CustomAuthToken(ObtainAuthToken): def post(self, request, *args, **kwargs):
serializer = self.serializer_class(data=request.data,
context={'request': request})
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
token, created = Token.objects.get_or_create(user=user)
return Response({
'token': token.key,
'user_id': user.pk,
'email': user.email
})
还有urls.py
:
urlpatterns += [
url(r'^api-token-auth/', CustomAuthToken.as_view())
]
- 认证Token
当我们获取到Token后,就可以拿着这个Token来认证其他API了。对于客户端进行身份验证,令牌密钥应包含在 Authorization
HTTP header 中。关键字应以字符串文字 “Token” 为前缀,用空格分隔两个字符串。例如:
Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b
注意: 如果你想在 header 中使用不同的关键字(例如Bearer
),只需子类化TokenAuthentication
并设置keyword
类变量。
如果成功通过身份验证,TokenAuthentication
将提供以下凭据。
request.user
是一个User
实例,包含了用户名及相关信息。request.auth
是一个rest_framework.authtoken.models.Token
实例。
未经身份验证的响应被拒绝将导致HTTP 401 Unauthorized
的响应和相应的 WWW-Authenticate header。例如:
WWW-Authenticate: Token
测试令牌认证的API,例如:
$ curl -X GET -H 'Authorization: Token 684b41712e8e38549504776613bd5612ba997616' http://127.0.0.1:8000/virtual/
注意: 如果您在生产中使用TokenAuthentication,则必须确保您的 API 只能通过https访问。
PS:DRF自带的TokenAuthentication认证方式也非常简单,同时弊端也很大,真正项目中用的较少。由于需要存储在数据库表中,它在分布式系统中用起来较为麻烦,并且每次都需要查询数据库,增加数据库压力;同时它不支持Token的过期设置,这是一个很大的问题。在实际前后端分离项目中使用JWT(Json Web Token)标准的认证方式较多,每个语言都有各自实现JWT的方式,Python也不例外。
参考:http://www.ywnds.com/?p=14967
Django REST framework 之 API认证的更多相关文章
- Django Rest framework 框架之认证使用和源码执行流程
用这个框架需要先安装: pip3 install djangorestframework 如果写了一个CBV的东西,继承了View. # 继承Django里面View class APIView(Vi ...
- Django Rest FrameWork 全部API
Django Rest FrameWork .Requests 请求 客服端发送给服务器的请求 .Responses 响应 rest框架支持响应不同格式的内容 .Views 视图 base基础类视图 ...
- Django REST framework 之JWT认证
Json Web Token 1.JWT简介 JWT 是一个开放标准(RFC 7519),它定义了一种用于简洁,自包含的用于通信双方之间以 JSON 对象的形式安全传递信息的方法.JWT 可以使用 H ...
- Django REST framework 自定义(认证、权限、访问频率)组件
本篇随笔在 "Django REST framework 初识" 基础上扩展 一.认证组件 # models.py class Account(models.Model): &qu ...
- Django REST framework 的TokenAuth认证及外键Serializer基本实现
一,Models.py中,ForeignKey记得要有related_name属性,已实现关联对象反向引用. app_name = models.ForeignKey("cmdb.App&q ...
- 快用Django REST framework写写API吧
Django默认是前后端绑定的,提供了Template和Form,现在流行前后端分离项目,Python大佬坐不住了,于是便有了Django REST framework:https://github. ...
- django rest framework权限和认证
Django rest framework之权限 一.Authentication用户认证配置 1.四种验证及官网描述: BasicAuthentication 此身份验证方案使用HTTP基本身份验证 ...
- Django REST framework基础:认证、权限、限制
认证.权限和限制 身份验证是将传入请求与一组标识凭据(例如请求来自的用户或其签名的令牌)相关联的机制.然后 权限 和 限制 组件决定是否拒绝这个请求. 简单来说就是: 认证确定了你是谁 权限确定你能不 ...
- Django REST framework 数据处理api
一.url分发 以防有其他业务线的需要,导致url杂乱,将每个app用到的url都设置在自己的应用中. # 项目下的url url(r"^api/(?P<version>\w+) ...
随机推荐
- 【View】之【SimpleWaveView】可多色可刷新的加速球、进度球【demo】
当前版本:SimpleWaveView_v1.0.20140618 先看效果图,这个加速球是动态的,并且当调用了myView.setRefresh(0.8F);方法后可以从当前值动态降到0再升到80% ...
- MathType编辑物理单位的方法
在用MathType编辑物理公式时,由于物理单位很多都是复合单位,所以在编辑时如果能够有这种复合单位直接使用的话,编辑效率就会大大提高.实际上这种想法在MathType中是可行的,MathType中也 ...
- css文字超出自动显示省略号
只针对单行文本有效: 01.针对块状元素 ul li{ width: 180px; text-overflow: ellipsis; white-space: nowrap;/*禁止自动换行*/ ov ...
- [大数据] zookeeper 安装和配置
ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件.它是一个为分布式应用提供一致性服务的软件,提供的功 ...
- python--数据类型--1
原创博文,转载请标明出处--周学伟http://www.cnblogs.com/zxouxuewei/ 视频教程::http://www.imooc.com/learn/177 http://www. ...
- 文件打包为zip格式文件下载
整个思路是这样的: 1.查询数据库中的文件流放到datatable中2.循环datatable将文件流一个个生成文件,放到对应的文件夹中,3.下载某个文件夹下的所有文件a.循环这个文件夹下的所有文件, ...
- oracle_存储过程_没有参数_更新过期申请单以及写日志事务回滚
CREATE OR REPLACE PROCEDURE A_MEAS_MIINSP_PLAN_UPDATEASvs_msg VARCHAR2(4000);log_body VARCHAR2(400); ...
- Spring------Spring data jpa 定义实体类(@OneToMany等的使用)
转载: https://course.tianmaying.com/spring-data-jpa+one-to-many#2
- 分布式消息队列RocketMQ与Kafka架构上的巨大差异之1 -- 为什么RocketMQ要去除ZK依赖?
我们知道,在早期的RocketMQ版本中,是有依赖ZK的.而现在的版本中,是去掉了对ZK的依赖,转而使用自己开发的NameSrv. 并且这个NameSrv是无状态的,你可以随意的部署多台,其代码也非常 ...
- ContentPriver
共享应用程序内的数据, 在数据修改时可以监听 1.特点 ①.可以将应用中的数据对外进行共享: ②.数据访问方式统一,不必针对不同数据类型采取不同的访问策略: ③.内容提供者将数据封装,只暴露出我们希望 ...