欢迎访问我的个人网站:www.comingnext.cn

前言:

按照前面几篇文章里那样做,使用Django编写RESTful API的基本功能已经像模像样了。我们可以通过不同的URL访问到不同的资源,通过不同的HTTP请求来实现对资源的不同操作。

但是现在我们的API还有一个很明显的缺陷,那就是没有认证和权限功能,任何资源都会任何用户被随意更改,所以我们要改进程序,实现以下功能:

  1. snippet与其创建者相互关联
  2. 只有经过身份验证(登录)的用户才可以创建snippets
  3. 只有创建该snippet的用户才可以对其进行更改或者删除
  4. 未经验证的用户只具有访问(只读)的功能

修改snippet模型

首先,我们想让snippets都和它们的创建用户关联起来,所以我们自然的要在Snippet模型添加一个owner字段来表示。另外,我们还添加一个highlighted字段用来实现代码高亮,修改snippets/models.py的Snippet:

owner = models.ForeignKey('auth.User', related_name='snippets', on_delete=models.CASCADE)
highlighted = models.TextField()

想要实现代码高亮,当然不是上面一行代码就搞定了,它现在还只是一个普通的字段而已。我们要做的是在保存的时候,也就是当执行save()时, 我们使用pygments生成高亮后的HTML,还是在model.py,首先导入相关的库:

from pygments.lexers import get_lexer_by_name
from pygments.formatters.html import HtmlFormatter
from pygments import highlight

然后在Snippet类中添加save()方法:

def save(self, *args, **kwargs):
"""
使用pygments库来生成能使代码高亮的HTML代码
"""
lexer = get_lexer_by_name(self.language)
linenos = self.linenos and 'table' or False
options = self.title and {'title': self.title} or {}
formatter = HtmlFormatter(style=self.style, linenos=linenos,
full=True, **options)
self.highlighted = highlight(self.code, lexer, formatter)
super(Snippet, self).save(*args, **kwargs)

在保存数据的时候就会执行上面这个方法,整个方法的功能如注释所示,在这一篇文章中还不会具体的展示这个功能,在接下来的文章中会展示。

修改了模型当然需要同步一下数据库了,在这里我们和官方文档一样把数据库删了在重新生成,首先把工程目录下的db.sqlite3以及snippets下的migrations文件夹删除,然后再执行迁移步骤:

python manage.py makemigrations snippets
python manage.py migrate

同时,由于我们想要实现的是访问各个snippet时显示相应的创建者,所以这里需要创建几个不同的账户稍后才可以显示。

python manage.py createsuperuser

为我们的用户模型添加端点

原理和之前的SnippetSerializer基本一样,在snippets/serializers.py中添加一个User序列化器:

from django.contrib.auth.models import User

class UserSerializer(serializers.ModelSerializer):
snippets = serializers.PrimaryKeyRelatedField(many=True, queryset=Snippet.objects.all()) class Meta:
model = User
fields = ('id', 'username', 'snippets')

注意到里面的:

snippets = serializers.PrimaryKeyRelatedField(many=True, queryset=Snippet.objects.all())

因为snippets在User模型中是一个反向关系,在使用ModelSerializer类时默认情况是不会包括这个关系,就是说通过Snippet的owner能查询到User,而User这边查询不到一个用户创建的snippet,所以我们需要手动为用户序列添加这个字段。

弄好了User的序列化器,接着就要让其能够显示出来,所以要添加相关的视图类,编辑view.py:

from django.contrib.auth.models import User
from snippets.serializers import UserSerializer class UserList(generics.ListAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer class UserDetail(generics.RetrieveAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer

写好了视图函数,想要通过URL访问到它们,肯定是配置一下路由分发啦,编辑snippets/urls.py,添加下面的匹配模式:

url(r'^users/$', views.UserList.as_view()),
url(r'^users/(?P<pk>[0-9]+)/$', views.UserDetail.as_view()),

把Snippets和Users关联起来

到了这里,如果像之前那样创建代码段的话,我们还不能把Snippets和Users关联起来。因为在使用的时候User的数据是通过request传入的,而不是以序列化的数据传递过来。

而我们刚才添加了一个owner作为外键,这个时候就要看到它的用处了,编辑view.py,为SnippetList视图类添加一个方法:

class SnippetList(generics.ListCreateAPIView):
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,) def perform_create(self, serializer):
serializer.save(owner=self.request.user)

这个perform_create() 可以让用户在通过POST请求创建一个新的Snippet时,在保存新的Snippet数据的时候会把request中的user赋值给Snippet的owner。等下具体使用的时候就可以轻松的理解了。


更新serializer

上一步已经把两者关联起来了,owner会在创建新的Snippet的时候拥有User的各个属性,那么在API中要让owner显示id还是用户名,为了提高可读性,答案当然是显示用户名了,所以我们在SnippetSerializer 下面增加一个字段:

owner = serializers.ReadOnlyField(source='owner.username')
这里的source参数就指定了哪个属性用于填充字段,为了在使用的时候显示owner,但是还要把它添加进Meta类里面,所以整个SnippetSerializer如下:
class SnippetSerializer(serializers.ModelSerializer):
# 这里可以使用也 CharField(read_only=True) 来替换
owner = serializers.ReadOnlyField(source='owner.username') class Meta:
model = Snippet
fields = ('id', 'title', 'code', 'linenos', 'language', 'style','owner')

添加权限

现在Snippet和User已经关联起来并且是可浏览的。接下来我们要实现的及时权限的问题了。也就是我们一开始说的几点中的:

  1. 只有经过身份验证(登录)的用户才可以创建snippet

  2. 只有创建该snippet的用户才可以对其进行更改或者删除

  3. 未经验证的用户只具有访问(只读)的功能

首先在views.py导入一个库:

from rest_framework import permissions

接着为SnippetList 和 SnippetDetail添加权限判断,在这两个视图类中都加入:

permission_classes = (permissions.IsAuthenticatedOrReadOnly,)

这里要特别注意,有一个坑,就是那个逗号一定要加上去,不然就会报错。

这行代码的作用就是判断当前用户是否为该Snippet的创建者,而其他用户只有只读属性,就是只能查看。


为可浏览的API添加登录功能

刚才添加了权限判断,如果没有登录用户,那就相当于游客啦,什么功能都没有只能看,所以在浏览器浏览API的时候就需要登录 功能。在这里,强大的django-rest-framework又为我们做了很多事情,想要在添加登录按钮和页面,只需要修改一个rest_tutorial/urls.py,添加一个URL匹配:

urlpatterns += [
url(r'^api-auth/', include('rest_framework.urls',
namespace='rest_framework')),
]

这里的r'^api-auth/'你可以设置成任意你喜欢的,但是命名空间一定要相同,就是namespace='rest_framework'。

好了,现在打开浏览器,就可以看到在我们的API页面的右上角有一个登录的按钮,点击之后就可以使用之前创建的用户登录了。

这个时候访问单个用户的详情,就可以看到该用户创建的所有Snippet的id值(需要先创建好几个Snippet,可以按照本系列第一篇文章中在shell模式中的方法来创建)。比如访问:

http://127.0.0.1:8000/users/2/

可以看到: 


添加对象权限

接着我们要实现的是让所有的Snippet可以被所有人访问到,但是每个Snippet只有其创建者才可以对其进行更改、删除等操作。

因此,我们需要设置一下自定义权限,使每个Snippet只允许其创建者编辑它。在snippets目录下新建一个permissions.py:

from rest_framework import permissions

class IsOwnerOrReadOnly(permissions.BasePermission):
"""
使每个Snippet只允许其创建者编辑它
""" def has_object_permission(self, request, view, obj):
# 任何用户或者游客都可以访问任何Snippet,所以当请求动作在安全范围内,
# 也就是GET,HEAD,OPTIONS请求时,都会被允许
if request.method in permissions.SAFE_METHODS:
return True # 而当请求不是上面的安全模式的话,那就需要判断一下当前的用户
# 如果Snippet所有者和当前的用户一致,那就允许,否则返回错误信息
return obj.owner == request.user

代码的逻辑已在注释中,简单说就是提供判断功能,然后我们要把它运用起来,在view.py中的SnippetDetail 修改一下:

class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,
IsOwnerOrReadOnly,)

注意要导入IsOwnerOrReadOnly类:

from snippets.permissions import IsOwnerOrReadOnly

现在用浏览器打开单个Snippet详情页,如果你当前登录的用户是这个Snippet的创建者,那你会发现多了DELETE和PUT两个操作,比如访问http://127.0.0.1:8000/snippets/2/,效果如下: 


使用API授权

由于现在我们还没使用authentication 类,所以项目目前还是使用默认的SessionAuthentication 和 BasicAuthentication.

在使用浏览器访问API的时候,浏览器会帮我们保存会话信息,所以当权限满足时就可以对一个Snippet进行删除或者更改,或者是创建一个新的Snippet。

当如果是通过命令行来操作API,我们就必须在每次发送请求的时候添加授权信息,也就是用户名和密码,没有的话就会报错,比如:

http POST http://127.0.0.1:8000/snippets/ code="print 123"

{
"detail": "Authentication credentials were not provided."
}

正确的操作如下:

http -a username1:password POST http://127.0.0.1:8000/snippets/ code="print 789"

{
"id": 1,
"owner": "username1",
"title": "",
"code": "print 789",
"linenos": false,
"language": "python",
"style": "friendly"
}

我们可以看出owner就是提交过来的用户名,这就是上面代码的功能体现:

    def perform_create(self, serializer):
serializer.save(owner=self.request.user)

通过实际使用更能理解程序,owner会在一个用户创建Snippet时得到该用户的信息就是这么来的。


关于认证和权限的部分就先到这了。我们在上面写的代码中,highlight部分,我们说它的功能是生成能让代码段高亮的HTML代码,这一部分还没有使用到,接下来就介绍它。

本文地址:http://www.cnblogs.com/zivwong/p/7456591.html
作者博客:ziv
欢迎转载,请在明显位置给出出处及链接

Django编写RESTful API(四):认证和权限的更多相关文章

  1. Django编写RESTful API(一):序列化

    欢迎访问我的个人网站:www.comingnext.cn 关于RESTful API 现在,在开发的过程中,我们经常会听到前后端分离这个技术名词,顾名思义,就是前台的开发和后台的开发分离开.这个技术方 ...

  2. Spring Boot 2.x 编写 RESTful API (四) 使用 Mybatis

    用Spring Boot编写RESTful API 学习笔记 添加依赖 <dependency> <groupId>org.mybatis.spring.boot</gr ...

  3. Django编写RESTful API(五):添加超链接提高模型间的关联性

    前言 在第四篇中,加入了用户模型,以及相关的认证和权限的功能.但是我们在使用的时候,会发现在访问http://127.0.0.1:8000/users/时看到的用户列表,不能够直接点击某个链接然后查看 ...

  4. Django编写RESTful API(二):请求和响应

    欢迎访问我的个人网站:www.comingnext.cn 前言 在上一篇文章,已经实现了访问指定URL就返回了指定的数据,这也体现了RESTful API的一个理念,每一个URL代表着一个资源.当然我 ...

  5. Django编写RESTful API(六):ViewSets和Routers

    欢迎访问我的个人网站:www.comingnext.cn 前言 在本系列的文章中,我在第一篇和第二篇文章中写的编写Django视图时,使用的都是基于函数的方法,并且每个视图函数之前都会加一个djang ...

  6. Django编写RESTful API(三):基于类的视图

    欢迎访问我的个人网站:www.comingnext.cn 前言 在上一篇文章中,主要讲的是请求和响应,项目里面views.py中的视图函数都是基于函数的,并且我们介绍了@api_view这个很有用的装 ...

  7. 利用 Django REST framework 编写 RESTful API

    利用 Django REST framework 编写 RESTful API Updateat 2015/12/3: 增加 filter 最近在玩 Django,不得不说 rest_framewor ...

  8. python 全栈开发,Day95(RESTful API介绍,基于Django实现RESTful API,DRF 序列化)

    昨日内容回顾 1. rest framework serializer(序列化)的简单使用 QuerySet([ obj, obj, obj]) --> JSON格式数据 0. 安装和导入: p ...

  9. 关于RESTFUL API 安全认证方式的一些总结

    常用认证方式 在之前的文章REST API 安全设计指南与使用 AngularJS & NodeJS 实现基于 token 的认证应用两篇文章中,[译]web权限验证方法说明中也详细介绍,一般 ...

随机推荐

  1. 记一次 联想杨天A4600K 安装操作系统遇到的尴尬

    故事的开始: 某日,有一台联想扬天A4600K台式机,该机器原来使用的系统为windowsXP,先需要安装win7系统. 准备好gho镜像,准备ghost安装系统.不料,提示镜像文件损坏,无法安装.起 ...

  2. Xamarin Android绑定微信SDK

    现在几乎所有的APP都集成了向微博,微信等社交平台分享的功能.这些社交平台官方也提供了SDK让开发者使用,对于Android和IOS平台而言,只需要下载官方的SDK,按照官方说明文档进行集成就可以轻松 ...

  3. Java之StringBuffer,StringBuilder,Math,Date,SimpleDateFormat,UUID,File

    java.lang 类 StringBuffer java.lang.Object java.lang.StringBuffer 所有已实现的接口: Serializable, Appendable, ...

  4. innodb关键特性之double write

    # 脏页刷盘的风险 两次写的原理机制 1.解决问题 2.使用场景 3.doublewrite的工作流程 4.崩溃恢复 # doublewrite的副作用 1.监控doublewrite负载 2.关闭d ...

  5. [javascript] visible - 待写

    摘要 jquery 有个筛选器 visible , 一般用于选择 可见元素 $('p:visible') 就是选择可见的 p 元素. 但发现有时候不可用.!!

  6. ECMAScript 6 学习(二)async函数

     1.什么是async函数 2.用法 2.1基本用法 3.语法 3.1返回promise对象 3.2promise状态的变化 3.3await命令 1.什么是async函数 async函数也是异步编程 ...

  7. HDU 6069

    Counting Divisors Problem Description In mathematics, the function d(n) denotes the number of diviso ...

  8. Java中容器的两种初始化方式比较

    List,Set,Map的两种初始化赋值方式  List List<Integer> list2 = new ArrayList<Integer>(); for (int i= ...

  9. Cordova + idea 环境搭建

    1.安装前期工作 1).安装Node.js http://nodejs.cn/download/ 里面内置了npm,可以用来安装 Cordova,把该路径添加到环境变量,这样就可以在 cmd 里面任何 ...

  10. idea如何添加外部jar包

    假设我们要将G:\ModuleAPI_Java_2.2.0.0 .jar导入工程中: 首先,在mvn命令行执行下面命令: mvn install:install-file -Dfile=G:\Modu ...