作了一年多的python 方面的web開發工做,昨個有個同窗問我Django的安全機制,我是一臉的茫然。天天使用公司開發框架的我,對這些東西瞭解的甚少,儼然成爲一個真正的"碼農",只知其然而不知其因此然。我要改變,故從網絡上,查了下,記下被查,內容以下:java
介紹python
Spring是Java語言開發的一站式Web框架。包括:SpringMVC,Spring,SpringSecurity,SpringAOP等子框架。Spring在數據庫訪問層能夠整合Hibernate,iBatis等第三方框架。構成了一個完整的Web應用程序框架。程序員
Spring大量使用了策略模式、模板方法模式,提供了鉤子回調第三方的API,於是能夠整合大量第三方框架。web
Django是Python語言開發的一站式Web應用程序框架。其獨立開發了從Web層到數據庫訪問層在內的全部框架。數據庫
Spring和Django在功能上基本對等,都是Web應用程序開發的基礎平臺。django
Django利用了Python語言自身的優點,優雅地實現了一整套Web應用框架。編程
Spring爲人廣爲詬病的一點就是,其大量使用了xml格式的配置文件。配置工做量至關大。在Java5引入annotation以後,雖然能夠在Java源碼中直接加上配置。可是每次修改配置必須從新編譯程序,殊爲不便!瀏覽器
Python是動態語言,於是Django直接使用Python源代碼做爲配置文件。Python程序能夠即時編譯,不須要額外的編譯過程。安全
本文將對Spring和Django的安全機制作一系統比較。服務器
大部分Web應用程序在Action層實現安全控制。Action層負責接收請求和發出響應消息的這一層次。
一般的作法是,在數據庫中爲用戶定義權限。每個權限使用一個URL標識。
用戶登陸後發給用戶瀏覽器一個cookie。服務器端也保存這個cookie,並把這個cookie和一個用戶關聯起來。
用戶再次發出請求以後,根據用戶發來的cookie到數據庫中查詢對應的用戶,取得User對象和相應的權限集合。保存在HttpRequest或者HttpSession中,或者ThreadLocal中。
編寫一個Filter,對Http請求進行預過濾。比對User的權限中是否有這個URL。若是沒有,那麼就直接返回錯誤消息,不會把這個request發送到URL對應的Action方法中處理。
這一方案能夠在Action層實現安全控制,有效攔截非法訪問。
Java的EJB框架也有本身的基於角色的一套安全控制機制。它能夠對EJB對象而不是Action層實現對服務的訪問控制,粒度更低。
可是使用EJB安全機制很麻煩。必須按照EJB的要求定義角色和安全模型,必須編寫大段的xml配置文件指定訪問控制策略。
EJB在Business層實現了安全控制,這對於EJB架構的程序是有意義的。由於EJB架構中,EJB是獨立部署的服務組件。客戶端使用RMI遠程協議訪問它。
EJB的客戶端能夠是Web服務器,也能夠是富客戶端程序。
可是,EJB這樣的架構是否必須呢?這個在業界有很長時間的爭論。不少人包括筆者本人都認爲EJB這種架構已通過時了。
富客戶端程序一樣能夠經過Http協議與Web服務器程序通訊。Web服務器能夠同時支持B/S和C/S雙架構。
Web服務器程序一樣也能夠提供TCP/UDP接口供富客戶端程序訪問。
最後一個問題,如EJB這樣把Business組件單獨部署是否有必要?EJB集羣 VS Web服務器集羣誰優誰劣?
Web服務器同時包括http接口和Business邏輯。Web服務器能夠和EJB同樣實現集羣部署。EJB服務器使用RMI對外接口通信。Web服務器使用Http對外接口通信。應該說EJB集羣沒有提供比Web服務器集羣更多的優點。
所以,我認爲EJB安全機制並不比HttpRequest攔截安全機制更優秀。
Spring和Django都使用了AOP(面向方面編程)技術來實現安全控制。
SpringAOP是Spring開發的一個AOP框架。利用了Java動態運行的特性,使用反射技術實現了面向方面(AOP)編程。
Spring框架負責安全的子系統是Spring Security框架。Spring Security就是使用Spring AOP實現安全控制的。
Python與生俱來就支持AOP。Python的適配器函數就能夠輕鬆實現AOP。
Python的裝飾者函數在語法上和Java的Annotation很類似,但實際實現徹底不一樣。
Java的Annotation是運行時能夠經過發射獲得的描述型數據。
Annotation在Python中的對應物是Python中的Doc。Doc也是在運行時能夠獲得的描述型數據,用於生成JavaDoc這樣的文檔,或者是運行時經過help(模塊名/類名/函數名)獲得幫助信息。Python的Doc通常沒有像Java的Annotation這樣使用的。
Python的裝飾者函數不是運行時能夠獲得的元數據。Python的裝飾者函數就是一個普通的Python函數。它的第一個參數是被修飾的函數。所以能夠直接實現AOP中的round。咱們知道AOP包括3種攔截機制:before,after和round。Round是同時before和after。
所以Python的裝飾者函數直接等價於Java的AOP。
下面內容摘自Spring3.0指南:
Spring Security能夠用在多種不一樣的驗證環境下。咱們推薦人們使用Spring Security進行驗證,而不是與現存的容器管理驗證相結合,然而這種方式也是被支持的 - 做爲與你本身的驗證系統相整合的一種方式。
什麼是Spring Security的驗證呢?
讓咱們考慮一種標準的驗證場景,每一個人都很熟悉的那種。
1.一個用戶想使用一個帳號和密碼進行登錄。
2.系統(成功的)驗證了密碼對於這個用戶名 是正確的。
3.這個用戶對應的信息唄獲取 (他們的角色列表以及等等)。
4.爲用戶創建一個安全環境。
5.用戶會執行一些操做,這些都是潛在被 權限控制機制所保護的,經過對操做的受權, 使用當前的安全環境信息。【函數須要校驗用戶的權限是否知足自身的要求。所以函數必須知道從哪裏得到用戶的受權信息】
前三個項目執行了驗證過程,因此咱們能夠看一下 Spring Security的做用。
1.用戶名和密碼被得到,並進行比對, 在一個UsernamePasswordAuthenticationToken的實例中 (它是Authentication接口的一個實例, 咱們在以前已經見過了)。
2.這個標誌被髮送給一個AuthenticationManager 的實例進行校驗。
3.AuthenticationManager返回一個徹底的 Authentication實例, 在成功校驗後。
4.安全環境被創建,經過調用 SecurityContextHolder.getContext().setAuthentication(...), 傳遞到返回的驗證對象中。
In Spring Security, the responsibility for storing the
SecurityContext between requests falls to the SecurityContextPersistenceFilter,
which by default stores the context as an HttpSession attribute between HTTP requests.
It restores the context to the SecurityContextHolder for each request and, crucially, clears the SecurityContextHolder when the request completes.
技術說明:
SecurityContext是Spring Security框架保存用戶受權信息的對象。在用戶登陸時建立。
每一次請求開始時,Spring Security使用Filter把HttpSession中的SecurityContext恢復到 SecurityContextHolder中。SecurityContextHolder是一個Java類,包含多個靜態函數。
用SecurityContextHolder的方法:public static void setContext(SecurityContext context)方法把SecurityContext對象保存到ThreadLocal中。
方法原型:
setContext
public static void setContext(SecurityContext context)
Associates a new SecurityContext with the current thread of execution.
Parameters:
context - the new SecurityContext (may not be null)
SecurityContextHolder.getContext()方法返回保存在ThreadLocal中的SecurityContext對象。
由於Action層和Service層都在同一個Thread下執行。所以Action層AOP存放下SecurityContext能夠被Service層重用。
用戶不該該直接操做HttpSession中的SecurityContext對象。老是應該用 SecurityContextHolder提供的方法獲取SecurityContext對象。
每一次請求結束時,Filter都會把當前線程中的SecurityContext對象清除。由於線程可能會被重用。不清除可能會引起安全問題。
Spring Security可使用xml配置文件或者java annotation在業務層對方法聲明安全限制。Spring Security使用Spring AOP技術在業務層方法執行時對其攔截,用SecurityContextHolder.getContext()對象內的用戶受權和函數上聲明的受權進行比對。若是不符合,就拋出異常返回。從而實現了對業務層方法的安全控制。
Django使用App的概念實現各個子框架。Django負責安全的子框架是Auth應用。
Django用戶登陸後,會在HttpSession中保存User對象。在Aciton層(Django的術語是View,我在本文中爲了和Java術語相同,使用Action代替View)能夠獲得User對象及其Perm受權集合。程序員能夠在Action層中手工對用戶的權限和Action要求的權限進行比對,實現訪問控制。
這和前文HttpRequest攔截方法相似。
可是,Django的能力不止與此。使用Python裝飾者函數,Django可使用相似於Spring Security的anontation的語法對Aciton函數實現聲明式的安全控制!
如:
from django.contrib.auth.decorators import login_required
@login_required
def my_view(request):
# ...
這裏的@login_required聲明表示必須是登陸用戶才能夠調用這個Action(View)函數。@login_required就是Python的裝飾者函數。
更進一步的訪問限制:
from django.contrib.auth.decorators import permission_required
@permission_required('polls.can_vote', login_url="/login/")
def vote(request):
# ...
注意, permission_required() 也有一個可選的login_url 參數, 這個參數默認爲'/accounts/login/' 。
這裏能夠根據用戶的權限對Action進行限制。若是用戶通不過受權,就返回給用戶一個登陸頁面。
這些都不須要程序員編寫一行代碼!神奇吧?!
比較:
Python Django框架只能在Action層進行ACL控制。由於它沒有使用Filter在ThreadLocal中保存User信息。所以在Service層中沒法得到User信息。
Python自身支持AOP,也支持ThreadLocal。所以Django也能夠像Spring Security同樣提供對業務層函數的安全控制。只是Django沒有這樣作而已。
也許是Django做爲一個相對新生的社區,沒有精力作這件事。或者更可能的是,Django社區的人認爲在業務層實現安全控制沒有必要。Action層直接控制不就完了嗎?