Security is an important part of any website , But for the Web API It is crucial for us . at present , our Blog API Allow anyone full access . No restrictions ; Any user can do anything extremely dangerous . for example , Anonymous users can create , read , Update or delete any blog posts . They didn't even create one ! obviously , We don't want to do this .
Django REST Framework Comes with some ready-made permission settings , We can use these settings to protect our API. These can be applied at the project level , View level or any individual model level .
In this chapter , We'll add a new user and try multiple permission settings . then , We will create our own custom permissions , So that only the author of the blog post can update or delete it .
First create a second user . such , We can switch between two user accounts to test our permission settings .
Browse to http://127.0.0.1:8000/admin/ The administrator of . And then click “ user ” Lateral “ + add to ”.
Enter the user name and password of the new user , And then click “ preservation ” Button . I chose the user name here testuser
.
The next screen is “ Administrator user changes ” page . I've called my users testuser, Here I can add other information contained in the default user model , For example, name , surname , Email address, etc . But for us , None of this is necessary : We just need user name and password Used to detect .
Scroll down to the bottom of this page , And then click “ preservation ” Button . It will be redirected back to http://127.0.0.1:8000/admin/auth/user/
Main user page .
We can see that there are two users in the list .
In the future , Whenever you want to switch between user accounts , We all need to jump to Django Administrators , Exit an account , Then log in to another account . Every time . And switch back to our API Endpoint .
This is a common situation ,Django REST Framework Have single line settings to add login and log out directly to browsable API In itself . We will implement it immediately .
At the project level urls.py
In file , Add a containing rest_framework.urls
The new URL route . What is puzzling is , The actual route specified can be anything we want ; It is important to rest_framework.urls
Contained somewhere . We will use api-auth route , Because it matches the official documents , But we can easily use anything we want , And all functions will remain the same .
# blog_project/urls.pyfrom django.contrib import admin from django.urls import include, pathurlpatterns = [ path('admin/', admin.site.urls), path('api/v1/', include('posts.urls')), path('api-auth/', include('rest_framework.urls')), # new]
Now? , visit http://127.0.0.1:8000/api/v1/
Browsable on API. There is a slight change : Next to the user name in the upper right corner is a downward arrow .
Since we have logged in with the super user account at this time ( For me, yes wsv ), Therefore, the name... Will be displayed . Click on the link , Then it displays with “ Cancellation ” The drop-down menu for . Click on it .
The link in the upper right corner is now changed to “ Sign in ”. therefore , Please click this button .
We were redirected to Django REST Framework The login page . Use your test user account here .
Last , It will redirect to the main API page , There's... In the upper right corner testuser.
Last , Write off our testuser account .
You should see again in the upper right corner “Log in” link .
At present , Any anonymous unauthorized user can access our PostList Endpoint . The reason why I know this , Because we haven't logged in yet , You can also see our individual blog posts . What's worse is , Anyone has the right to create , edit , Update or delete posts !
On the details page http://127.0.0.1:8000/api/v1/1/
On , This information is also visible , Any random user can update or delete existing blog posts .
The reason why you can still see “ Publish list ” Endpoints and “ Detailed list ” endpoints , Because we were in settings.py Set the project level permission of the project to AllowAny. A brief reminder , It looks something like this :
# blog_project/settings.pyREST_FRAMEWORK = { 'DEFAULT_PERMISSION_CLASSES': [ 'rest_framework.permissions.AllowAny', ]}
What we want now is to API Access is restricted to authenticated users . We can do this in multiple places - Project level , View level or object level - But because we only have two views at present , So let's start there , And add permissions for each view .
In your posts/views.py
In file , from Django REST Import permissions at the top of the frame , Then add one... To each view permission_classes
Field .
# posts/views.pyfrom rest_framework import generics, permissions # newfrom .models import Postfrom .serializers import PostSerializerclass PostList(generics.ListCreateAPIView): permission_classes = (permissions.IsAuthenticated,) # new queryset = Post.objects.all() serializer_class = PostSerializer class PostDetail(generics.RetrieveUpdateDestroyAPIView): permission_classes = (permissions.IsAuthenticated,) # new queryset = Post.objects.all() serializer_class = PostSerializer
This is what we need . adopt http://127.0.0.1:8000/api/v1/
Refresh browsable API. See what happened !
We no longer see our “ List of Posts ” page . In its place , Because we haven't logged in yet , So you will receive unfriendly HTTP 403 Prohibition status code . Because we have no authority , So you can browse API There are no tables in the to edit data .
therefore , At present, only logged in users can view our API. If you use super user or testuser
Account login again , You can visit API Endpoint .
however , Please consider following API What happens with the growth of complexity . In the future, we may have more views and destinations . If we're going to be in the whole API Set the same permission settings in , Add a dedicated... To each view permission_class
It seems to be repeated .
It's best to change permissions only once at the project level , Instead of making a change to every view , Would it be better ?
here , You should nod . It is easier to set strict permission policies at the project level and relax them as needed at the view level , A safer way . This is what we have to do .
Fortunately, ,Django REST Framework along with Attached are many built-in project level permission settings that we can use , Include :
AllowAny- Any authenticated user has full access
IsAuthenticated- Only authenticated registered users can access
IsAdminUser- Only administrators / Super users have access to
IsAuthenticatedOrReadOnly- Unauthorized users can view any page , However, you can only see that authenticated users have write access , Edit or delete permissions
To implement any of these four settings , All need more DEFAULT_PERMISSION_CLASSES
Set and refresh our Web browser . nothing more !
Let's switch to IsAuthenticated
, In this way, only authenticated or logged in users can view API.
to update blog_project/settings.py
, as follows :
# blog_project/settings.pyREST_FRAMEWORK = { 'DEFAULT_PERMISSION_CLASSES': [ 'rest_framework.permissions.IsAuthenticated', # new ]}
Now back to views.py file , And delete the permission change we just made .
# posts/views.pyfrom rest_framework import genericsfrom .models import Postfrom .serializers import PostSerializerclass PostList(generics.ListCreateAPIView): queryset = Post.objects.all() serializer_class = PostSerializer class PostDetail(generics.RetrieveUpdateDestroyAPIView): queryset = Post.objects.all() serializer_class = PostSerializer
If you refresh “ Publish list ” and “ Detailed list ” API page , You will still see the same 403 The status code . Now? , We require all users to authenticate first , Then we can visit API, But we can always make other view level changes as needed .
It's time to get our first custom permission . As our brief review now : We have two users ,testuser And super user accounts . There is a blog post in our database , Created by super user .
We only hope that the author of a specific blog post can edit or delete it ; otherwise , Blog posts should be read-only . therefore , The superuser account should have full access to a single blog instance CRUD Access right , And regular users testuser There should be no .
Use Control + c Stop the local server , And create a new one in our post application permissions.py
file .
(blogapi) $ touch posts/permissions.py
In the internal ,Django REST Framework Depend on BasePermission class , All other permission classes are from this BasePermission Class inheritance . This means things like AllowAny,IsAuthenticated Built in permission settings such as will extend it . This is a Github Available on Actual source code :
class BasePermission(object): """ A base class from which all permission classes should inherit. """ def has_permission(self, request, view): """ Return `True` if permission is granted, `False` otherwise. """ return True def has_object_permission(self, request, view, obj): """ Return `True` if permission is granted, `False` otherwise. """ return True
To create your own custom permissions , We will cover has_object_permission
Method . say concretely , We want to allow read-only for all requests , But for any write request ( Edit or delete, for example ), The author must be the same as the currently logged in user .
This is ours posts / permissions.py The content of the document .
# posts/permissions.pyfrom rest_framework import permissionsclass IsAuthorOrReadOnly(permissions.BasePermission): def has_object_permission(self, request, view, obj): # Read-only permissions are allowed for any request if request.method in permissions.SAFE_METHODS: return True # Write permissions are only allowed to the author of a post return obj.author == request.user
We import permissions at the top , Then create a custom class IsAuthorOrReadOnly, This class extends BasePermission. then , We cover has_object_permission. If the request contains SAFE_METHODS It contains HTTP Verb ( contain GET,OPTIONS and HEAD tuples ), Then the request is read-only , And grant permission .
otherwise , The request is for some type of write , This means that you need to update API resources , To create , Delete or edit functions . under these circumstances , Let's check the author of the object of discussion ( Our blog posts obj.author) Whether or not to make a request request.user The user matches .
go back to views.py In file , We should import IsAuthorOrReadOnly
, Then we can add PostDetail Of ``permission_classes`.
# posts/views.pyfrom rest_framework import genericsfrom .models import Postfrom .permissions import IsAuthorOrReadOnly # new from .serializers import PostSerializerclass PostList(generics.ListCreateAPIView): queryset = Post.objects.all() serializer_class = PostSerializerclass PostDetail(generics.RetrieveUpdateDestroyAPIView): permission_classes = (IsAuthorOrReadOnly,) # new queryset = Post.objects.all() serializer_class = PostSerializer
We finished . Let's test it out . Navigate to “ Post details ” page . Make sure you log in with the author superuser account of the post . Therefore, it will be visible in the upper right corner of the page .
however , If you log out and use testuser Account login , The page will change .
Because read-only permission is allowed , So we can view this page . however , Because of the custom IsAuthorOrReadOnly Permission class , We can't send any PUT or DELETE request .
Please note that , A generic view will only check object level permissions to get a view that retrieves a single model instance . If you need object level filtering on the list view ( For instance collection ), You have to go through Override the initial query set To filter .
Setting the appropriate permissions is any API An important part of . As a general strategy , It is best to set strict project level permission policies , So that only authenticated users can view API. then , As needed in a specific API Easier access to view levels or custom permissions on endpoints .