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 将有效的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

原文地址:https://www.cnblogs.com/freely/p/10326015.html

时间: 2024-09-30 15:37:00

Django REST framework 之 API认证的相关文章

Django rest framework 使用自定义认证方式

Django rest framework 使用自定义认证方式 Django使用自定义认证方式 介绍了 "Django使用自定义认证方式",这一篇说说怎样在前一篇的基础上提供rest api. 修改settings.py中INSTALLED_APPS,添加 'login' app. 给login app增加serializers.py文件 #coding:utf-8 from django.contrib.auth.models import User from rest_framew

Django REST framework 之JWT认证

Json Web Token 1.JWT简介 JWT 是一个开放标准(RFC 7519),它定义了一种用于简洁,自包含的用于通信双方之间以 JSON 对象的形式安全传递信息的方法.JWT 可以使用 HMAC 算法或者是 RSA 的公钥密钥对进行签名.它具备两个特点: 简洁(Compact) 可以通过URL, POST 参数或者在 HTTP header 发送,因为数据量小,传输速度快 自包含(Self-contained) 负载中包含了所有用户所需要的信息,避免了多次查询数据库 2.JWT 组成

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

一,Models.py中,ForeignKey记得要有related_name属性,已实现关联对象反向引用. app_name = models.ForeignKey("cmdb.App",related_name='deploy_app', verbose_name="App") 二,Settings.py文件中,加入对Django REST framework的基本设置. REST_FRAMEWORK = { 'DEFAULT_PERMISSION_CLASSE

python(Django之Logging、API认证)

一.Loging模块 用于方便的记录日志的模块 1 import logging 2 3 4 logging.basicConfig(filename='log.log', 5 format='%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s', 6 datefmt='%Y-%m-%d %H:%M:%S %p', 7 level=10) 8 9 logging.debug('debug') 10 logging.inf

django rest framework 的api返回html

return Response(data=serializer.data, status=status.HTTP_200_OK, content_type="application/json") content_type="application/json"去掉,写成: return Response(data=serializer.data, status=status.HTTP_200_OK)即可 原文地址:https://www.cnblogs.com/yus

Getting Started with Django Rest Framework and AngularJS

转载自:http://blog.kevinastone.com/getting-started-with-django-rest-framework-and-angularjs.html A ReSTful API is becoming a standard component of any modern web application. The Django Rest Framework is powerful framework for developing ReST endpoints

Django Restful Framework【第三篇】认证、权限、限制访问频率

一.认证 认证请求头 views.py #!/usr/bin/env python # -*- coding:utf-8 -*- from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.authentication import BaseAuthentication from rest_framework.permissions import

源码剖析Django REST framework的认证方式及自定义认证

源码剖析Django REST framework的认证方式 由Django的CBV模式流程,可以知道在url匹配完成后,会执行自定义的类中的as_view方法. 如果自定义的类中没有定义as_view方法,根据面向对象中类的继承可以知道,则会执行其父类View中的as_view方法 在Django的View的as_view方法中,又会调用dispatch方法. 现在来看看Django restframework的认证流程 Django restframework是基于Django的框架,所以基

Django rest framework源码分析(一) 认证

一.基础 最近正好有机会去写一些可视化的东西,就想着前后端分离,想使用django rest framework写一些,顺便复习一下django rest framework的知识,只是顺便哦,好吧.我承认我是故意的,因为我始终觉得,如果好的技术服务于企业,顺便的提高一下自己.大家都很开心不是不.再次强调一下,真的只是顺便. 安装吧 pip install djangorestframework 1.2.需要先了解的一些知识 理解下面两个知识点非常重要,django-rest-framework