1. 程式人生 > >Spring和Django安全機制的比較

Spring和Django安全機制的比較

介紹

      SpringJava語言開發的一站式Web框架。包括:SpringMVCSpring,SpringSecurity,SpringAOP等子框架。Spring在資料庫訪問層可以整合Hibernate,iBatis等第三方框架。構成了一個完整的Web應用程式框架。

     Spring大量使用了策略模式、模板方法模式,提供了鉤子回撥第三方的API,因而可以整合大量第三方框架。

     DjangoPython語言開發的一站式Web應用程式框架。其獨立開發了從Web層到資料庫訪問層在內的所有框架。

     SpringDjango在功能上基本對等,都是Web應用程式開發的基礎平臺。

      Django利用了Python語言自身的優勢,優雅地實現了一整套Web應用框架。

       Spring為人廣為詬病的一點就是,其大量使用了xml格式的配置檔案。配置工作量相當大。在Java5引入annotation之後,雖然可以在Java原始碼中直接加上配置。但是每次修改配置必須重新編譯程式,殊為不便!

      Python是動態語言,因而Django直接使用Python原始碼作為配置檔案。Python程式可以即時編譯,不需要額外的編譯過程。

        本文將對SpringDjango的安全機制做一系統比較。

傳統的Web安全解決方案

HttpRequest攔截方法

              大部分Web應用程式在Action層實現安全控制。Action層負責接收請求和發出響應訊息的這一層次。

            通常的做法是,在資料庫中為使用者定義許可權。每一個許可權使用一個URL標識。

             使用者登入後發給使用者瀏覽器一個cookie。伺服器端也儲存這個cookie,並把這個cookie和一個使用者關聯起來。

           使用者再次發出請求之後,根據使用者發來的cookie到資料庫中查詢對應的使用者,取得User物件和相應的許可權集合。儲存在HttpRequest或者HttpSession中,或者ThreadLocal

中。

           編寫一個Filter,對Http請求進行預過濾。比對User的許可權中是否有這個URL。如果沒有,那麼就直接返回錯誤訊息,不會把這個request傳送到URL對應的Action方法中處理。

           這一方案可以在Action層實現安全控制,有效攔截非法訪問。

EJB安全機制

           JavaEJB框架也有自己的基於角色的一套安全控制機制。它可以對EJB物件而不是Action層實現對服務的訪問控制,粒度更低。

但是使用EJB安全機制很麻煩。必須按照EJB的要求定義角色和安全模型,必須編寫大段的xml配置檔案指定訪問控制策略。

小結:

         EJBBusiness層實現了安全控制,這對於EJB架構的程式是有意義的。因為EJB架構中,EJB是獨立部署的服務元件。客戶端使用RMI遠端協議訪問它。

         EJB的客戶端可以是Web伺服器,也可以是富客戶端程式。

      但是,EJB這樣的架構是否必須呢?這個在業界有很長時間的爭論。很多人包括筆者本人都認為EJB這種架構已經過時了。

      富客戶端程式同樣可以通過Http協議與Web伺服器程式通訊。Web伺服器可以同時支援B/SC/S雙架構。

      Web伺服器程式同樣也可以提供TCP/UDP介面供富客戶端程式訪問。

          最後一個問題,如EJB這樣把Business元件單獨部署是否有必要?EJB叢集 VS Web伺服器叢集誰優誰劣?

        Web伺服器同時包括http介面和Business邏輯。Web伺服器可以和EJB一樣實現叢集部署。EJB伺服器使用RMI對外介面通訊。Web伺服器使用Http對外介面通訊。應該說EJB叢集沒有提供比Web伺服器叢集更多的優勢。

因此,我認為EJB安全機制並不比HttpRequest攔截安全機制更優秀。

SpringDjango安全機制

      SpringDjango都使用了AOP(面向方面程式設計)技術來實現安全控制。

     SpringAOPSpring開發的一個AOP框架。利用了Java動態執行的特性,使用反射技術實現了面向方面(AOP)程式設計。

     Spring框架負責安全的子系統是Spring Security框架。Spring Security就是使用Spring AOP實現安全控制的。

     Python與生俱來就支援AOPPython的介面卡函式就可以輕鬆實現AOP

        Python的裝飾者函式在語法上和JavaAnnotation很相似,但實際實現完全不同。

JavaAnnotation是執行時可以通過發射得到的描述型資料。

AnnotationPython中的對應物是Python中的DocDoc也是在執行時可以得到的描述型資料,用於生成JavaDoc這樣的文件,或者是執行時通過help(模組名/類名/函式名)得到幫助資訊。PythonDoc一般沒有像JavaAnnotation這樣使用的。

Python的裝飾者函式不是執行時可以得到的元資料。Python的裝飾者函式就是一個普通的Python函式。它的第一個引數是被修飾的函式。因此可以直接實現AOP中的round。我們知道AOP包括3種攔截機制:before,afterroundRound是同時beforeafter

因此Python的裝飾者函式直接等價於JavaAOP

Spring Security安全機制

下面內容摘自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(...), 傳遞到返回的驗證物件中。

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.

技術說明:

SecurityContextSpring Security框架儲存使用者授權資訊的物件。在使用者登入時建立。

         每一次請求開始時,Spring Security使用FilterHttpSession中的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下執行。因此ActionAOP存放下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 Securityanontation的語法對Aciton函式實現宣告式的安全控制!

如:

from django.contrib.auth.decorators import login_required

@login_required

def my_view(request):

# ...

這裡的@login_required宣告表示必須是登入使用者才可以呼叫這個ActionView)函式。@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控制。因為它沒有使用FilterThreadLocal中儲存User資訊。因此在Service層中無法獲得User資訊。

Python自身支援AOP,也支援ThreadLocal。因此Django也可以像Spring Security一樣提供對業務層函式的安全控制。只是Django沒有這樣做而已。

也許是Django作為一個相對新生的社群,沒有精力做這件事。或者更可能的是,Django社群的人認為在業務層實現安全控制沒有必要。Action層直接控制不就完了嗎?