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 将有效的usernamepassword字段发布到视图时,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认证的更多相关文章

  1. Django Rest framework 框架之认证使用和源码执行流程

    用这个框架需要先安装: pip3 install djangorestframework 如果写了一个CBV的东西,继承了View. # 继承Django里面View class APIView(Vi ...

  2. Django Rest FrameWork 全部API

    Django Rest FrameWork .Requests 请求 客服端发送给服务器的请求 .Responses 响应 rest框架支持响应不同格式的内容 .Views 视图 base基础类视图 ...

  3. Django REST framework 之JWT认证

    Json Web Token 1.JWT简介 JWT 是一个开放标准(RFC 7519),它定义了一种用于简洁,自包含的用于通信双方之间以 JSON 对象的形式安全传递信息的方法.JWT 可以使用 H ...

  4. Django REST framework 自定义(认证、权限、访问频率)组件

    本篇随笔在 "Django REST framework 初识" 基础上扩展 一.认证组件 # models.py class Account(models.Model): &qu ...

  5. Django REST framework 的TokenAuth认证及外键Serializer基本实现

    一,Models.py中,ForeignKey记得要有related_name属性,已实现关联对象反向引用. app_name = models.ForeignKey("cmdb.App&q ...

  6. 快用Django REST framework写写API吧

    Django默认是前后端绑定的,提供了Template和Form,现在流行前后端分离项目,Python大佬坐不住了,于是便有了Django REST framework:https://github. ...

  7. django rest framework权限和认证

    Django rest framework之权限 一.Authentication用户认证配置 1.四种验证及官网描述: BasicAuthentication 此身份验证方案使用HTTP基本身份验证 ...

  8. Django REST framework基础:认证、权限、限制

    认证.权限和限制 身份验证是将传入请求与一组标识凭据(例如请求来自的用户或其签名的令牌)相关联的机制.然后 权限 和 限制 组件决定是否拒绝这个请求. 简单来说就是: 认证确定了你是谁 权限确定你能不 ...

  9. Django REST framework 数据处理api

    一.url分发 以防有其他业务线的需要,导致url杂乱,将每个app用到的url都设置在自己的应用中. # 项目下的url url(r"^api/(?P<version>\w+) ...

随机推荐

  1. Canny边缘检测——学习笔记

    Sobel Canny 非极大值抑制NMS,上表为角度,下表为灰度 26度,在0-45°之间,离45°更近. 把不是极大值的点改为0,这样边缘会细很多. 双阈值判定 深度优先遍历

  2. centos7 安装zookeeper 集群

    data 创建myid  设置1 zookeeper默认端口2181  同步端口 20881 和 30881 设置zoo.cfg文件

  3. Extjs学习笔记--(三,调试技巧)

    FireFox 1.firedebug(略) 2.illuminations 在illuminations页面可也看到缩写的extjs的代码,同时可以进行相应的调试 3,Firedebug AutoC ...

  4. Android NDK开发-1-环境搭建

    1.NDK介绍 Android NDK 是在SDK前面又加上了“原生”二字,即Native Development Kit,因此又被Google称为“NDK”.众所周知,Android程序运行在Dal ...

  5. Python 内部类

    内部类也就是在类的内部再定义类,如下: #!/usr/bin/env python #-*- coding:utf-8 -*- class People(object): class Chinese( ...

  6. strip() 、lstrip() 、rstrip()

    strip() 用于移除字符串开头和结尾的空格或换行符,如果指定参数,则表示移除指定的字符lstrip() 用于移除字符串开头的空格或换行符,如果指定参数,则表示移除指定的字符rstrip() 用于移 ...

  7. Ubuntu13.10:[3]如何开启SSH SERVER服务

    作为最新版本的UBUNTU系统而言,开源,升级全部都不在话下.传说XP已经停止补丁更新了,使用UBUNTU也是一个很好的选择.ubuntu默认安装完成后只有ssh-agent(客户端模式),宾哥百度经 ...

  8. 使用vue-cli结合express获取mongodb里面的数据

    最近一直在看node有关的内容,空闲时间做了一个小小的爬虫,用于爬取电影天堂的数据然后写到mongodb里面,代码地址:https://github.com/fangming666/dianyingt ...

  9. VC项目程序运行时设置指定目录读取Dll

    方法一: 选择当前工程,右击"Properties" -> "Configuration Properties" -> "Debuggin ...

  10. PyQt4网格布局

    最通用的布局类别是网格布局(QGridLayout).该布局方式将窗口空间划分为许多行和列.要创建该布局方式,我们需要使用QGridLayout类. #!/usr/bin/python # -*- c ...