Spring和Django安全機制的比較
介紹
Spring是Java語言開發的一站式Web框架。包括:SpringMVC,Spring,SpringSecurity,SpringAOP等子框架。Spring在資料庫訪問層可以整合Hibernate,iBatis等第三方框架。構成了一個完整的Web應用程式框架。
Spring大量使用了策略模式、模板方法模式,提供了鉤子回撥第三方的API,因而可以整合大量第三方框架。
Django是Python語言開發的一站式Web應用程式框架。其獨立開發了從Web層到資料庫訪問層在內的所有框架。
Spring和Django在功能上基本對等,都是Web應用程式開發的基礎平臺。
Django利用了Python語言自身的優勢,優雅地實現了一整套Web應用框架。
Spring為人廣為詬病的一點就是,其大量使用了xml格式的配置檔案。配置工作量相當大。在Java5引入annotation之後,雖然可以在Java原始碼中直接加上配置。但是每次修改配置必須重新編譯程式,殊為不便!
Python是動態語言,因而Django直接使用Python原始碼作為配置檔案。Python程式可以即時編譯,不需要額外的編譯過程。
本文將對Spring和Django的安全機制做一系統比較。
傳統的Web安全解決方案
HttpRequest攔截方法
大部分Web應用程式在Action層實現安全控制。Action層負責接收請求和發出響應訊息的這一層次。
通常的做法是,在資料庫中為使用者定義許可權。每一個許可權使用一個URL標識。
使用者登入後發給使用者瀏覽器一個cookie。伺服器端也儲存這個cookie,並把這個cookie和一個使用者關聯起來。
使用者再次發出請求之後,根據使用者發來的cookie到資料庫中查詢對應的使用者,取得User物件和相應的許可權集合。儲存在HttpRequest或者HttpSession中,或者ThreadLocal
編寫一個Filter,對Http請求進行預過濾。比對User的許可權中是否有這個URL。如果沒有,那麼就直接返回錯誤訊息,不會把這個request傳送到URL對應的Action方法中處理。
這一方案可以在Action層實現安全控制,有效攔截非法訪問。
EJB安全機制
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安全機制
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
。
Spring Security安全機制
下面內容摘自Spring3.0指南:
Spring Security可以用在多種不同的驗證環境下。 我們推薦人們使用Spring Security進行驗證,而不是與現存的容器管理驗證相結合, 然而這種方式也是被支援的 - 作為與你自己的 驗證系統相整合的一種方式。
什麼是Spring Security的驗證呢?
讓我們考慮一種標準的驗證場景,每個人都很熟悉的那種。
-
一個使用者想使用一個賬號和密碼進行登陸。
-
系統(成功的)驗證了密碼對於這個使用者名稱 是正確的。
-
這個使用者對應的資訊唄獲取 (他們的角色列表以及等等)。
-
為使用者建立一個安全環境。
-
使用者會執行一些操作,這些都是潛在被 許可權控制機制所保護的,通過對操作的授權, 使用當前的安全環境資訊。【函式需要校驗使用者的許可權是否滿足自身的要求。因此函式必須知道從哪裡獲得使用者的授權資訊】
前三個專案執行了驗證過程,所以我們可以看一下 Spring Security的作用。
-
使用者名稱和密碼被獲得,並進行比對, 在一個
UsernamePasswordAuthenticationToken
的例項中 (它是Authentication
介面的一個例項, 我們在之前已經見過了)。 -
這個標誌被髮送給一個
AuthenticationManager
的例項進行校驗。 -
AuthenticationManager
返回一個完全的Authentication
例項, 在成功校驗後。 -
安全環境被建立,通過呼叫
SecurityContextHolder.getContext().setAuthentication(...)
, 傳遞到返回的驗證物件中。
SecurityContext
跨請求儲存:
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物件儲存到ThreadLocal中。
方法原型:
setContext
public static void setContext
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
安全機制
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
層直接控制不就完了嗎?