Django编写RESTful API(四):认证和权限
欢迎访问我的个人网站:www.comingnext.cn
前言:
按照前面几篇文章里那样做,使用Django编写RESTful API的基本功能已经像模像样了。我们可以通过不同的URL访问到不同的资源,通过不同的HTTP请求来实现对资源的不同操作。
但是现在我们的API还有一个很明显的缺陷,那就是没有认证和权限功能,任何资源都会任何用户被随意更改,所以我们要改进程序,实现以下功能:
- snippet与其创建者相互关联
- 只有经过身份验证(登录)的用户才可以创建snippets
- 只有创建该snippet的用户才可以对其进行更改或者删除
- 未经验证的用户只具有访问(只读)的功能
修改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已经关联起来并且是可浏览的。接下来我们要实现的及时权限的问题了。也就是我们一开始说的几点中的:
只有经过身份验证(登录)的用户才可以创建snippet
只有创建该snippet的用户才可以对其进行更改或者删除
未经验证的用户只具有访问(只读)的功能
首先在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(四):认证和权限的更多相关文章
- Django编写RESTful API(一):序列化
欢迎访问我的个人网站:www.comingnext.cn 关于RESTful API 现在,在开发的过程中,我们经常会听到前后端分离这个技术名词,顾名思义,就是前台的开发和后台的开发分离开.这个技术方 ...
- Spring Boot 2.x 编写 RESTful API (四) 使用 Mybatis
用Spring Boot编写RESTful API 学习笔记 添加依赖 <dependency> <groupId>org.mybatis.spring.boot</gr ...
- Django编写RESTful API(五):添加超链接提高模型间的关联性
前言 在第四篇中,加入了用户模型,以及相关的认证和权限的功能.但是我们在使用的时候,会发现在访问http://127.0.0.1:8000/users/时看到的用户列表,不能够直接点击某个链接然后查看 ...
- Django编写RESTful API(二):请求和响应
欢迎访问我的个人网站:www.comingnext.cn 前言 在上一篇文章,已经实现了访问指定URL就返回了指定的数据,这也体现了RESTful API的一个理念,每一个URL代表着一个资源.当然我 ...
- Django编写RESTful API(六):ViewSets和Routers
欢迎访问我的个人网站:www.comingnext.cn 前言 在本系列的文章中,我在第一篇和第二篇文章中写的编写Django视图时,使用的都是基于函数的方法,并且每个视图函数之前都会加一个djang ...
- Django编写RESTful API(三):基于类的视图
欢迎访问我的个人网站:www.comingnext.cn 前言 在上一篇文章中,主要讲的是请求和响应,项目里面views.py中的视图函数都是基于函数的,并且我们介绍了@api_view这个很有用的装 ...
- 利用 Django REST framework 编写 RESTful API
利用 Django REST framework 编写 RESTful API Updateat 2015/12/3: 增加 filter 最近在玩 Django,不得不说 rest_framewor ...
- python 全栈开发,Day95(RESTful API介绍,基于Django实现RESTful API,DRF 序列化)
昨日内容回顾 1. rest framework serializer(序列化)的简单使用 QuerySet([ obj, obj, obj]) --> JSON格式数据 0. 安装和导入: p ...
- 关于RESTFUL API 安全认证方式的一些总结
常用认证方式 在之前的文章REST API 安全设计指南与使用 AngularJS & NodeJS 实现基于 token 的认证应用两篇文章中,[译]web权限验证方法说明中也详细介绍,一般 ...
随机推荐
- 登录界面Demo
今天记载一个Demo,这个是我练习项目中用到,供新手看看,界面图:
- 平方根的C语言实现(一)
曾经做一个硬件成本极度控制的项目,因为硬件成本极低,并且还需要实现较高的精度测量,过程中也自己用C语言实现了正弦.余弦.反正切.平方根等函数. 以下,无论是在我的实际项目中还是本地的计算机系统,int ...
- (转)java并发之Executor
场景: 线程池在面试时候经常会碰到,在工作中用的场景更多,所以很有必要弄清楚. 1 简介 Java自1.5以来加入了处理一批线程的方法,也就是java并发包里的Executor.本文主要介绍Execu ...
- UVA - 1639 -Candy
题目链接:https://vjudge.net/problem/UVA-1639 题目大意: 有两个糖果盒,每个盒子里面有n个糖果,每天随机选一个(概率分别为p,1-p),然后吃一颗糖.直到有一天,打 ...
- 高效搭建lnmp环境
1:安装nginx sudo apt-get install nginx 检测 nginx : sudo nginx -t 出现如下表示成功 2:安装配置m ...
- 数据结构随笔-php实现栈
栈(Stack)满足后进先出(LIFO)的原则: 下面利用php实现栈的相关操作: 本实例栈的基本操作: 入栈(push):向栈内压入一个元素,栈顶指针指向栈顶元素 出栈(pop): 从栈顶去除元素, ...
- web 前端路线
- coder该何去何从
无论是什么语言的学习,都不是一帆风顺的,如今随着编程大军的壮大,工作越来越难找,各位coder已经把中心偏移到了学历上面,导致技术水平的参差不齐,以及虚假学历的泛滥,这样的恶性循环下,不知前路在何方?
- .net core 2.0学习笔记(一):开发运行环境搭建
期待已久的.net core 2.0终于发布了!大家等的花儿都谢了. 不过比预期提前了一个多月,这在微软历史上还真的不多见.按照历史经验看,2.0版本应该比较靠谱,我猜这也是社区非常火爆的原因吧.下面 ...
- CSS规范--春风十里不如写好CSS
先吟几句: 最近看了看春风十里不如你,本来很少看剧的,暑假有点闲就看了,感觉不错,挺喜欢这部剧,就套了个名字,嘿嘿嘿.剧里面印象深刻的是<致橡树>这首诗,念几句: 我如果爱你,绝不像攀援的 ...