restful api

Django: csrf防御機制

csrf攻擊過程


1.用戶C打開瀏覽器,訪問受信任網站A,輸入用戶名和密碼請求登錄網站A;

2.在用戶信息通過驗證后,網站A產生Cookie信息并返回給瀏覽器,此時用戶登錄網站A成功,可以正常發送請求到網站A;

3.用戶未退出網站A之前,在同一瀏覽器中,打開一個TAB頁訪問網站B;

4.網站B接收到用戶請求后,返回一些攻擊性代碼,并發出一個請求要求訪問第三方站點A;

5.瀏覽器在接收到這些攻擊性代碼后,根據網站B的請求,在用戶不知情的情況下攜帶Cookie信息,向網站A發出請求。網站A并不知道該請求其實是由B發起的,所以會根據用戶C的Cookie信息以C的權限處理該請求,導致來自網站B的惡意代碼被執行。


在django防御csrf攻擊

原理

在客戶端頁面上添加csrftoken, 服務器端進行驗證,服務器端驗證的工作通過'django.middleware.csrf.CsrfViewMiddleware'這個中間層來完成。在django當中防御csrf攻擊的方式有兩種, 1.在表單當中附加csrftoken 2.通過request請求中添加X-CSRFToken請求頭。注意:Django默認對所有的POST請求都進行csrftoken驗證,若驗證失敗則403錯誤侍候。

取消csrftoken驗證

通過csrf_exempt, 來取消csrftoken驗證,方式有兩種。

在視圖函數當中添加csrf_exempt裝飾器


GET, POST, PUT, DELETE 正好可以對應我們 CRUD (Create, Read, Update, Delete) 四種數據操作


Django REST framework教程二: 請求和響應

請求對象(Request object)

????????REST framework引入了一個Request對象, 它繼承自普通的HttpRequest, 但能夠更加靈活的解析收到的請求。Request對象的核心功能,就是其中的request.data屬性。這個屬性跟request.POST相似,但對我們的Web API來說,更加的有用。

????????request.POST ? ? # 只能處理表單(form)數據,只能處理“POST”方法.?

????????request.data ? ? ? # 處理任意數據.可以處理'POST', 'PUT' 和 'PATCH'方法.

響應對象(Response object)

????????REST framework 同時引入了Response對象,是一種TemplateResponse,它攜帶著純粹的內容,通過內容協商(Content Negotiation)來決定,將以何種形式,返回給客戶端。

????????return Response(data) ? ?#根據客戶端的要求,把內容,生成對應的形式。


包裝API視圖(wrapping API views)

????????REST framework提供了兩種編寫API view的封裝:

? ? ? ? ? ? ? ? (1)使用@api_view裝飾器,基于方法的視圖

? ? ? ? ? ? ? ? (2)繼承APIView類,基于類的視圖

? ? ? ?這些視圖封裝,提供了些許的功能,比如:確保你的視圖能夠收到Request實例;還有,將內容賦予Response對象,使得內容協商(content negotiation) 可以正常的運作。


from ?rest_framework ?import ?status

from ?rest_framework.decorators ?import ?api_view

from ?rest_framework.response ?import ?Response

from ?snippets.models ?import ?Snippet

from ?snippets.serializers ?import ?SnippetSerializer

@api_view(['GET', 'POST’])

def ?snippet_list(request):

????????"""

????????列出所有的代碼片段(snippets),或者創建一個代碼片段(snippet)

? ? ? ? """

????????if ?request.method == 'GET':

????????????????snippets = Snippet.objects.all()

????????????????serializer = SnippetSerializer(snippets, many=True)

????????????????return ?Response (serializer.data)

????????elif ?request.method == 'POST':

????????????????serializer = SnippetSerializer (data = request.data)

????????????????if ?serializer.is_valid():

????????????????????????serializer.save()

????????????????????????return ?Response (serializer.data, status=status.HTTP_201_CREATED)

????????????????return ?Response (serializer.errors, status=status.HTTP_400_BAD_REQUEST)


@api_view(['GET', 'PUT', 'DELETE’])

def ?snippet_detail(request, pk):

????????"""

????????????讀取, 更新 或 刪除 一個代碼片段實例(snippet instance)。

????????"""

????????try:

????????????????snippet = Snippet.objects.get (pk = pk)

????????except ?Snippet.DoesNotExist:

????????????????return ?Response (status = status.HTTP_404_NOT_FOUND)

????????if ?request.method == 'GET':

????????????????serializer = SnippetSerializer (snippet)

????????????????return ?Response (serializer.data)

????????elif ?request.method == 'PUT':

????????????????serializer = SnippetSerializer(snippet, data = request.data)

????????????????if ?serializer.is_valid():

????????????????????????serializer.save()

????????????????????????return ?Response (serializer.data)

????????????????return ?Response (serializer.errors, status=status.HTTP_400_BAD_REQUEST)

????????elif ?request.method == 'DELETE':

????????????????snippet.delete()

????????????????return ?Response (status=status.HTTP_204_NO_CONTENT)


? ? ? ? 在這里,我們已經不再明確地,解析/定義視圖中 Request/Response的內容類型。request.data會自行處理輸入的json請求,當然,也能處理別的格式。同樣的,我們只需返回響應對象以及數據,REST framework會幫我們,將響應內容,渲染(render)成正確的格式。 ? ?


為URLs添加可選的格式后綴

????????現在,我們的響應,不再硬性綁定在,某一種返回格式上,利用這點優勢,我們可以為API端,添加格式的后綴。使用格式后綴,可以定制我們的URLs,使它明確的指向指定的格式,這意味著,我們的API可以處理一些URLs,類似這樣的格式http://example.com/api/items/4/.json。 ? ?????

????????首先,需要添加一個format關鍵字參數,如下所示: ? ?

????????????????def ?snippet_list (request, format=None):

????????然后對urls.py文件,做些修改:

????????from ?rest_framework.urlpatterns ?import ?format_suffix_patterns

????????urlpatterns = format_suffix_patterns(urlpatterns) ? ?


Django REST framework教程三: 基于類的視圖

????????與其使用基于方法(function based)的視圖,我們更加傾向使用基于類(class based)的視圖。

from ?snippets.models ?import ?Snippet

from ?snippets.serializers ?import ?SnippetSerializer

from ?django.http ?import ?Http404

from ?rest_framework.views ?import ?APIView

from ?rest_framework.response ?import ?Response

from ?rest_framework ?import ?status

class ?SnippetList(APIView):

????????"""

????????列出所有代碼片段(snippets), 或者新建一個代碼片段(snippet).

????????"""

????????def ?get (self, request, format=None):

????????????????snippets = Snippet.objects.all()

????????????????serializer = SnippetSerializer (snippets, many=True)

????????????????return ?Response (serializer.data)


? ? ? ? def ?post (self, request, format=None):

????????????????serializer = SnippetSerializer(data=request.data)

????????????????if ?serializer.is_valid():

????????????????????????serializer.save()

????????????????????????return ?Response (serializer.data, status=status.HTTP_201_CREATED)

????????????????return ?Response ?(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


class ?SnippetDetail (APIView):

????????"""

????????讀取, 更新 or 刪除一個代碼片段(snippet)實例(instance).

????????"""

????????def ?get_object(self, pk):

????????????????try:

????????????????????????return ?Snippet.objects.get(pk=pk)

????????????????except ?Snippet.DoesNotExist:

????????????????????????raise ?Http404


????????def ?get(self, request, pk, format=None):

????????????????snippet = self.get_object(pk)

????????????????serializer = SnippetSerializer(snippet)

????????????????returnResponse(serializer.data)


????????def ?put(self, request, pk, format=None):

????????????????snippet = self.get_object(pk)

????????????????serializer = SnippetSerializer(snippet, data=request.data)

????????????????if ?serializer.is_valid():

????????????????????????serializer.save()

????????????????????????return ?Response(serializer.data)

????????????????returnResponse(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


? ? ? ?def ?delete(self, request, pk, format=None):

????????????????snippet = self.get_object(pk)

????????????????snippet.delete()

????????????????return ?Response(status=status.HTTP_204_NO_CONTENT)


urlpatterns = [

????????url(r'^snippets/$', views.SnippetList.as_view()),

????????url(r'^snippets/(?P[0-9]+)/$', views.SnippetDetail.as_view()),

]

urlpatterns = format_suffix_patterns(urlpatterns)


使用混入(mixins)

????????目前為止,我們所用的增刪改查操作,在我們創建的,任何支持模型的視圖里,都沒有太大區別。這些通用的行為,在REST framework的混入(mixin)類中,都已經實現(implemented)了。

from ?snippets.models ?import ?Snippet

from ?snippets.serializers ?import ?SnippetSerializer

from ?rest_framework ?import ?mixins

from ?rest_framework ?import ?generics

class ?SnippetList(mixins.ListModelMixin,mixins.CreateModelMixin,

????????????????????????????????????generics.GenericAPIView):

????????queryset = Snippet.objects.all()

????????serializer_class = SnippetSerializer

????????def ?get(self, request, *args, **kwargs):

????????????????return ?self.list(request, *args, **kwargs)

????????def ?post(self, request, *args, **kwargs):

????????????????return ?self.create(request, *args, **kwargs)

????????我們使用GenericAPIView創建了我們的視圖,并且加入了ListModelMixin和CreateModelMixin

????????基本類提供了核心的功能,而混入(mixin)類提供了.list()和.create()行為。然后,我們顯式地在get和post方法里面,放入對應的行動。非常簡單,但目前夠用。


? class ?SnippetDetail(mixins.RetrieveModelMixin,mixins.UpdateModelMixin,

????????????????????mixins.DestroyModelMixin,

????????????????????????????????????????generics.GenericAPIView):

????????queryset = Snippet.objects.all()

????????serializer_class = SnippetSerializer

????????def ?get(self, request, *args, **kwargs):

????????????????return ?self.retrieve(request, *args, **kwargs)

????????def ?put(self, request, *args, **kwargs):

????????????????return ?self.update(request, *args, **kwargs)

????????def ?delete(self, request, *args, **kwargs):

????????????????return ?self.destroy(request, *args, **kwargs)

????????我們使用了GenericAPIView類提供了核心功能,而混入(mixin)類 提供了.retrieve(),.update()和.destroy()行為。


使用基于泛類(generic class)的視圖

????????REST framework提供了一套已經實現了混入類的通用(generic)視圖,我們可以使我們的views.py模塊,更加瘦身!

from ?snippets.models ?import ?Snippet

from ?snippets.serializers ?import ?SnippetSerializer

from ?rest_framework ?import ?generics

class ?SnippetList(generics.ListCreateAPIView):

????????queryset = Snippet.objects.all()

????????serializer_class = SnippetSerializer

class ?SnippetDetail(generics.RetrieveUpdateDestroyAPIView):

????????queryset = Snippet.objects.all()

????????serializer_class = SnippetSerializer


Django REST framework的各種技巧——3.權限

權限的類型

????????(1)用戶是否有訪問某個api的權限

????????(2)用戶對于相同的api不同權限看到不同的數據(其實一個filter)

????????(3)不同權限用戶對于api的訪問頻次,其他限制等等

????????(4)假刪除,各種級聯假刪除


基本講解

????????首先在django中,group以及user都可以有很多的permission,一個user會有他自身permission+所有隸屬group的permission。比如:user可能顯示的有3個permission,但他隸屬于3個組,每個組有2個不同的權限,那么他有3+2*3個權限。

????????permission會在api的models.py種統一建立,在Meta中添加對應的permission然后跑migrate數據庫就會有新的權限。

????????由于django對每一個不同的Model都會建立幾個基本的權限,我會在api/models.py里面單獨建一個ModulePermission的Model,沒有任何其他屬性,就只有一個Meta class上面對應各種權限,這就是為了syncdb用的,另外還一個原因后面再說。


當前我們的API在編輯或者刪除的時候沒有任何限制,我們不希望有些人有高級的行為,確保:

(1)代碼段始終與創建者相關聯

(2)只允許授權的用戶可以創建代碼段

(3)只允許代碼段創建者可以更新和刪除

(4)沒有認證的請求應該有一個完整的只讀權限列表


添加用戶信息在我們的models中

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


為我們user models添加serializers

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'是User模型上的反向關系,所以在使用ModelSerializer類時,它不會被默認包含,因此我們需要為它添加一個顯式字段。 ? ?????


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


將代碼段與用戶關聯

????????現在,如果我們創建了代碼段,就無法將創建代碼段的用戶與代碼段實例相關聯。用戶不作為序列化表示的一部分發送,而是傳入請求的屬性。

????????我們處理它的方式是通過在我們的代碼片段視圖上覆蓋一個perform_create()方法,允許我們修改實例保存的方式,并處理在傳入請求或請求的URL中隱含的任何信息。

????????在我們SnippetList 視圖類中,添加下面的方法

????def perform_create(self, serializer):

????????????serializer.save(owner=self.request.user)

當我們的serializer里create()方法被調用時,將自動添加owner字段和驗證合法的請求數據


更新我們的seralizer

????????現在sippets創建的時候已經和我們的用戶關聯起來,讓我們更新我們的SnippetSerializer,添加以下定義的字段:

????????owner= serializers.ReadOnlyField(source='owner.username’)

????????source參數用于控制那個屬性被用來填充字段,并且可以指向序列化實例上的任何屬性。 它也可以采用上面所示的虛線符號,在這種情況下,它將遍歷給定的屬性,與使用Django的模板語言類似的方式。

????????我們添加的字段是無類型的ReadOnlyField類,與其他類型的字段,例如CharField,BooleanField等相反。無類型的ReadOnlyField總是只讀的,并且將用于序列化表示,但不會 用于在反序列化時更新模型實例。 我們也可以在這里使用CharField(read_only = True)

????????同時需要在meta class中添加owner字段

????????class ?Meta:

????????????????model = Snippet? ? ? ??

????????????????fields = ('id','title','code','linenos','language','style','owner')


在views中添加請求權限

????????現在snippets 已經和我們的用戶關聯上了,我們需要確保僅僅通過驗證的用戶能夠增刪改

REST framework 包含有一些權限類,我們可以用來限制誰可以訪問給定的視圖,在這種情況下,首先我們查找IsAuthenticatedOrReadOnl,這將確保已驗證的請求獲得讀寫訪問權限,并且未驗證的請求獲得只讀訪問權限。

然后在SnippetList和SnippetDetailview中添加下面這個屬性

????????permission_classes = (permissions.IsAuthenticatedOrReadOnly,)


對象級別的權限

????????我們希望所有的代碼片段對任何人都可見,但也確保只有創建代碼片段的用戶能夠更新或刪除它。為了實現這個,我們需要創建一個自定義權限

在snippets.app中,創建一個新的文件,名為permissions.py

from ?rest_framework ?import ?permissions

classIsOwnerOrReadOnly(permissions.BasePermission):

????????def ?has_object_permission(self, request, view, obj):

????????"

????????讀取權限對任何的request都適用,接下來將要添加GET,HEAD,OPTIONS請求權限

????????''

????????????????if request.method in permissions.SAFE_METHODS:

????????????????????????return ?True

????????????????return ?obj.owner == request.user


????????現在我們在SnippetDetail view中通過編輯permission_classes屬性為snippet實例中添加自定義的權限

????????permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly,)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,646評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,595評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,560評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,035評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,814評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,224評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,301評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,444評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,988評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,804評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,998評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,544評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,237評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,665評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,927評論 1 287
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,706評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,993評論 2 374

推薦閱讀更多精彩內容