天天看點

Symfony2Book11:安全

安全是個兩步過程,它的目的就是防止使用者通路他/她無權通路的資源。

過程的第一步,安全系統識别使用者是誰,要求該使用者提供一些有序的驗證。它被稱為認證,它的意思是系統嘗試發現你是誰。

一旦系統知道你是誰之後,下一步就是決定你是否可以通路指定的資源。過程的這一部分稱為授權,它的意思是系統正在檢查,看看你是否有執行某些動作的權利。

<a href="http://blog.51cto.com/attachment/201106/090609915.png" target="_blank"></a>

因為最好的學習方法是看例子,讓我們來看看:

Symfony2的安全元件作為一個獨立的PHP庫可以用于任何PHP項目中。

安全元件可以通過你的應用程式配置來配置。實際上,最标準的安全設定隻是使用了正常的配置。下列配置告訴Symfony2對于任何比對/admin/*的URL都要求安全,要使用基本HTTP認證詢問使用者證書(如老派的使用者名/密碼對話框)

# app/config/config.yml 

security: 

    firewalls: 

        secured_area: 

            pattern:    ^/ 

            anonymous: ~ 

            http_basic: 

                realm: "Secured Demo Area" 

    access_control: 

        - { path: ^/admin, roles: ROLE_ADMIN } 

    providers: 

        in_memory: 

            users: 

                ryan:  { password: ryanpass, roles: 'ROLE_USER' } 

                admin: { password: kitten, roles: 'ROLE_ADMIN' } 

    encoders: 

        Symfony\Component\Security\Core\User\User: plaintext 

一個标準的Symfony2釋出将安全配置單獨放入單個檔案(如:app/config/security.yml)。如果你沒有單獨的安全檔案,你可以直接将配置放入你的主配置檔案中(如app/config/config.yml)。

該配置的最終結果是全功能的安全系統,如下所示:

系統中有兩個使用者 (ryan和admin);

使用者通過基本HTTP認證來認證他們自己;

任何比對/admin/*的URL都是安全的,隻有admin使用者可以通路它;

任何不比對/admin/*的URL可以被所有使用者通路(永遠不會提示使用者登入)。

讓我們簡要地看看安全是如何工作的,以及配置的每一部分是如何一起工作的。

Symfony2安全系統的工作就是确定使用者是誰(認證)然後檢檢視看使用者是否有通路特定資源或URL的權限。

當一個使用者将一個請求發向一個被防火牆保護的URL時,安全系統就被激活了。防火牆的工作就是要確定使用者是不是需要被認證,如果需要的話,發送一個響應傳回給使用者去啟動認證過程。

當傳入請求的URL比對防火牆的正規表達式模式時,防火牆被激活。在本例中,模式(^/)将比對任何傳入的請求。然而,其實防火牆被激活也沒有意義,任何URL都不會使HTTP認證使用者名和密碼對話框出現。例如,任何使用者都可以通路/foo,而不會提示要去認證。

<a href="http://blog.51cto.com/attachment/201106/090739137.png" target="_blank"></a>

之是以這樣首先是因為通過匿名配置參數,防火牆允許匿名使用者通過。換句話說,防火牆不要求使用者立刻進行認證。其次是因為沒有特定的規則需要通路/foo(下面的access_control節),請求甚至可以在沒有要求使用者認證的情況下完成。

如果你删除了匿名關鍵詞,那麼防火牆總是要求使用者立即認證的。

然而如果使用者要求/admin/foo,那麼處理過程是不同的。這是因為access_control配置段說任何比對正規表達式^/admin(如/admin或任何比對/admin/*的URL)都需要ROLE_ADMIN角色。角色是大多數授權的基礎:如果使用者擁有ROLE_ADMIN角色,使用者可以通路/admin/foo。

<a href="http://blog.51cto.com/attachment/201106/090835926.png" target="_blank"></a>

象前面一樣,當使用者最初發送請求時,防火牆并不要求任何身份。然而當通路控制層拒絕使用者通路(因為匿名使用者沒有ROLE_ADMIN使用者)時,防火牆接管該過程,并啟動認證程序。認證依賴你所用的認證機制。例如,如果你使用表單登入認證方式,使用者将被重定向到登入頁面。如果你使用的是HTTP認證,使用者将被發送HTTP的401響應,以便使用者可以看到使用者名密碼對話框。

使用者現在有機會送出它的證書給應用程式。如果證書是有效的,那麼原始請求會被重發。

<a href="http://blog.51cto.com/attachment/201106/090917136.png" target="_blank"></a>

在本例中,使用者ryan成功通過防火牆認證。但因為ryan沒有擁有ROLE_ADMIN角色,它仍然被拒絕通路/admin/foo。最終這意味着使用者将看到消息說明通路被拒絕。

當Symfony2拒絕使用者通路時,使用者可以看到一個錯誤頁面并收到一個HTTP的403狀态碼(Forbidden)。你可以根據食譜(cookbook)錯誤頁面中的自定義403錯誤頁内容來自定義拒絕通路頁。

最後,如果admin使用者請求/admin/foo,相拟的程序會發生。但是在認證之後,通路控制層将讓請求通過:

<a href="http://blog.51cto.com/attachment/201106/090950434.png" target="_blank"></a>

當使用者請求一個受限資源時産生的請求流是十分簡單的,但相當靈活。正如你稍後将看到的那樣,認證可以通過多樣方式來處理,包括通過表單登入、X.509證書或通過Twitter來認證使用者。無論認證的模式如何,請求流總是相同的:

使用者通路受限資源;

應用程式将使用者重定向給登入表單;

使用者送出它的證書(如:使用者名/密碼);

防火牆認證使用者;

認證使用者重發原始請求。

确切的過程實際取決于你所使用的認證機制。舉個例子,當使用表單登入時,使用者送出它的證書到一個處理表單的URL(如 /login_check),然後重定向到最初請求的URL(如 /admin/foo)。但是如果是HTTP認證,使用者将直接提供它的證書到原始URL(如 /admin/foo),然後在同一請求中将頁面傳回給使用者(如:不進行重定向)

這些特質不會引起任何問題,但記住它們是有好處的。

你也将在稍後學到在Symfony2中如何保證其它事物的安全,包括特定的控制器、對象、甚至是PHP方法。

在目前為止,你已經看到如何将你的應用程式放置在防火牆下,然後根據規則限制通路某些區域。通過使用HTTP認證,你可以毫不費力地進入所有浏覽器都可以提供的使用者名/密碼框。然而,Symfony2支援許多對話框以外的認證機制。所有這一切的細節,可參見安全配置參考。

在本節中,你将讓使用者通過一個傳統的HTML登入表單來增強這一過程。

首先,在你的防火牆下啟動表單登入:

            form_login: 

                login_path:  /login 

                check_path:  /login_check 

如果你不需要自定義你的login_path或check_path的值(這裡的值是預設值),你可以縮寫你的配置:

form_login: ~ 

現在,當安全系統啟動認證過程時,它将使用者重定向到登入表單(預設是/login)。實作這個登入表單。首先建立兩個路由:一個顯示登入表單(如:/login),一個将處理登入表單的送出(如:/login_check):

# app/config/routing.yml 

login: 

    pattern:   /login 

    defaults:  { _controller: AcmeSecurityBundle:Security:login } 

login_check: 

    pattern:   /login_check 

你不需要為/login_check的URL實作控制器,因為防火牆會自動捕捉和處理這一URL上的任何表單送出。它是可選但卻有用,建立路由以便你可以用它在接下來的登入模闆中生成表單送出URL。

注意login路由名是不重要的,主要的是路由(/login)的URL比對check_pach的配置值,因為在那裡安全系統将需要登入的使用者進行重定向。

下一步,建立顯示登入表單的控制器:

// src/Acme/SecurityBundle/Controller/Main; 

namespace Acme\SecurityBundle\Controller; 

use Symfony\Bundle\FrameworkBundle\Controller\Controller; 

use Symfony\Component\Security\Core\SecurityContext; 

class SecurityController extends Controller 

    public function loginAction() 

    { 

        // get the login error if there is one 

        if ($this-&gt;get('request')-&gt;attributes-&gt;has(SecurityContext::AUTHENTICATION_ERROR)) { 

            $error = $this-&gt;get('request')-&gt;attributes-&gt;get(SecurityContext::AUTHENTICATION_ERROR); 

        } else { 

            $error = $this-&gt;get('request')-&gt;getSession()-&gt;get(SecurityContext::AUTHENTICATION_ERROR); 

        } 

        return $this-&gt;render('AcmeSecurityBundle:Security:login.html.twig', array( 

            // last username entered by the user 

            'last_username' =&gt; $this-&gt;get('request')-&gt;getSession()-&gt;get(SecurityContext::LAST_USERNAME), 

            'error'         =&gt; $error, 

        )); 

    } 

不要讓這個控制器迷惑你。正如你将稍後看到的那樣,當使用者送出表單時,安全系統自動為你處理表單送出。如果使用者送出了一個非法的使用者名或密碼,控制器會從安全系統中讀到表單送出的錯誤,以便傳回給使用者顯示。

換句話說,你的工作是顯示登入表單以及可能發生的錯誤,但安全系統自身檢查送出的使用者名和密碼,并對該使用者進行認證。

最後,建立相應的表單:

{# src/Acme/SecurityBundle/Resources/views/Security/login.html.twig #} 

{% if error %} 

    &lt;div&gt;{{ error.message }}&lt;/div&gt; 

{% endif %} 

&lt;form action="{{ path('login_check') }}" method="post"&gt; 

    &lt;label for="username"&gt;Username:&lt;/label&gt; 

    &lt;input type="text" id="username" name="_username" value="{{ last_username }}" /&gt; 

    &lt;label for="password"&gt;Password:&lt;/label&gt; 

    &lt;input type="password" id="password" name="_password" /&gt; 

    {# 

        If you want to control the URL the user is redirected to on success (more details below) 

        &lt;input type="hidden" name="_target_path" value="/account" /&gt; 

    #} 

    &lt;input type="submit" name="login" /&gt; 

&lt;/form&gt; 

表單有着非常少的要求。首先,通過送出表單到/login_check(通過login_check路由),安全系統自動為你截取表單送出并進行表單處理。其次,安全系統預期被送出的表單項是_username和_password(這些表單項名可以被配置)。

這是這樣!當你送出表單時,安全系統将自動檢查使用者的證書,要麼認證通過使用者,要麼将使用者重定向到登入表單以顯示錯誤資訊。

讓我們回顧一下整個過程:

使用者嘗試通路受限使用者;

防火牆通過将使用者重定向到登入表單(/login)并啟動認證過程;

在本例中,/login頁通過建立路由和控制器來渲染登入頁面;

使用者送出登入表單到/login_check;

安全系統攔截請求,檢查使用者送出的證書,如果正确的話就認證通過使用者,反之則将使用者送回登入頁面。

預設狀況下,如果送出的證書是正确的,使用者将被重定向到被請求的原始頁(如:/admin/foo)。如果使用者最初是直接通路的login頁面,它将被重定向到首頁。這可以允許你去定制,舉個例子,重定向使用者到指定的URL。

關于這個的更多細節,以及如何自定義表單登入過程,請參見如何自定義你的表單登入。

避開常見陷阱

當設定你的登入表單時,小心一些常見的陷阱。

1. 建立正确的路由

首先,確定你已經正确地定義了/login和/login_check路由,它們對應着login_path和check_path配置值。這裡的一個錯誤配置可能會将你重定向到404頁,而非登入頁面,或者送出登入表單之後什麼事情也沒發生(你隻是一遍又一遍地看到登入頁面)。

2. 確定登入頁面是不安全的

同樣,也需要確定登入頁面是不要求任何角色就可以檢視的。例如,下面的配置,為所有的URL要求ROLE_ADMIN角色(包括/login的URL),會引起循環重定義的:

access_control: 

    - { path: ^/, roles: ROLE_ADMIN } 

在/login的URL上删除通路控制以修複該問題:

    - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY } 

同樣,如果你的防火牆不允許匿名使用者的話,你也需要建立一個特殊的防火牆來讓匿名使用者使用登入頁:

firewalls: 

    login_firewall: 

        pattern:    ^/login$ 

        anonymous:  ~ 

    secured_area: 

        pattern:    ^/ 

        login_form: ~ 

3. 確定“/login_check”在防火牆之後

接下來,確定你check_path的URL(如:/login_check)在你登入表單的防火牆之後(在本例中,單個防火牆比對所有URL,包含/login_check)。如果/login_check沒有比對任何防火牆,你将得到不能為路徑“/login_check"找到控制器的異常。

4. 多個防火牆不能共享安全内容

如果你使用多重防火牆,并且你在針對一個防火牆進行認證,那麼你将不會再自動針對其它的防火牆進行認證。不同的防火牆就象不同的安全系統。這也是為什麼對于大多數應用程式而言,有一個主要的防火牆就足夠了的原因。

安全的第一個總是認證:确認使用者是誰的過程。在Symfony2中,認證可以用任何方式實作,可以通過表單登入、基本HTTP認證甚至通過Facebook。

一旦使用者被認證,授權就開始了。授權提供一個标準、強大的方式去決定使用者是否可以通路資源(URL、模型對象、方法調用...)。它是通過對每個使用者指定一個特定的角色,然後要求不同角色對應不同的資源。

授權過程有兩個不同的方法:

使用者有一個指定的角色集;

資源需要能夠通路它的特定角色。

在本節,你将關注如何保護不同角色對不同資源(如URL、方法調用等)的安全。稍後,你将學習到更多如何建立角色并将其指定給使用者。

保護應用程式的最好方式就是保護URL模式。你已經在本章的第一個例子中看到了這一點,任何比對正規表達式模式^/admin請求的URL都要求ROLE_ADMIN角色。

你可以定義許多的你所需的URL模式,每個都是一個正規表達式。

    # ... 

        - { path: ^/admin/users, roles: ROLE_SUPER_ADMIN } 

路徑前的^ 確定隻有以該URL開頭的模式可以被比對。舉個例子,簡單的/admin,可以比對/admin/foo,也可以比對/foo/admin。

對于每個輸入請求而言,Symfony2嘗試找出一個比對的通路控制規則(第一個赢)。如果使用者還沒有認證,那麼授權過程被啟動(如使用者得到一個登入的機會)。然而,如果使用者通過認證,但沒有擁有要求的角色,一個拒絕通路異常被抛出,你可以對其進行處理并轉入“拒絕通路”錯誤頁。更多資訊參見如何自定義錯誤頁。

因為Symfony2隻使用它比對到的第一個通路控制規則,類似/admin/users/new的URL将被比對,而且僅要求ROLE_SUPER_ADMIN角色。一些類似/admin/blog的URL将比對第2條規則,并要求ROLE_ADMIN角色。

你也可以通過一條access_control條目來強制使用HTTP或HTTPS。更多資訊參見如何為不同的URL強制使用HTTP或HTTPS。

基于URL模式來保護你的應用程式是容易的,但在某種情況下控制粒度不夠。在必要時,你可以很容易地在控制器内部強制授權:

use Symfony\Component\Security\Core\Exception\AccessDeniedException 

// ... 

public function helloAction($name) 

    if (false === $this-&gt;get('security.context')-&gt;isGranted('ROLE_ADMIN')) { 

        throw new AccessDeniedException(); 

    // ... 

你也可以選擇安裝和使用可選的SecurityExtraBundle,可以通過注釋來保護你的控制器:

use JMS\SecurityExtraBundle\Annotation\Secure; 

/** 

 * @Secure(roles="ROLE_ADMIN") 

 */ 

更多資訊,參見SecurityExtraBundle文檔。如果你已經使用了Symfony2的标準釋出,這個Bundle預設就是可用的。如果沒有的話,你也可以很容易地下載下傳并安裝它。

實際上,保護Symfony中的任何東西都可以使用與上一節相似的政策。例如,假設你有一個服務(如PHP類),它的工作是從一個使用者發送郵件到另一個。你可以限制該類的使用,無需關心發送使用者,但接收使用者需要擁有特定的角色。

關于你可以使用安全元件去保護你應用程式不同的服務和方法的更多資訊,檢視如何在你應用程式中保護服務和方法。

想象一下,你正在設計一個部落格系統,你的使用者可以在你的博文上發表評論。現在,你想一個使用者能夠編輯他自己的評論,而不是其他人的評論。還有,作為管理使用者,隻要你想,你就可以去編輯所有的評論。

安全元件附帶了一個可選的通路控制清單(ACL)系統,當你需要控制你系統中的某個對象執行個體時你可以用它。沒有ACL,你可以保護你的系統,通常隻是某個使用者可以編輯部落格評論。但有了ACL,你就可以限制和允許一個評論一個評論地去限制或允許通路。

更多資訊,可以參見食譜(cookbook)檔案:通路控制清單

在上一節,你學會了如何保護要求一組角色權限的不同資源。在這節,你們将了解授權的另一部分:使用者。

在認證期間,使用者送出一組證書(通常是使用者名和密碼)。認證系統的工作是從一些使用者池中比對那些證書。那麼這個使用者清單是從哪裡來的呢?

在symfony2中,使用者可以來自任何地方,配置檔案、資料表、WEB服務或你能想到的任何東西。提供一個或更多使用者到認證系統的被稱為“user provider"。Symfony2标配有兩個常用的使用者提供器:一個是從配置檔案中引導,一個是從資料表中引導。

指定使用者最容易的方式是在配置檔案中直接指定。實際上,你已經在本章的示例中看到過。

        default_provider: 

任何使用者提供器都可以直接從配置中引導使用者,通過指定使用者配置參數并列出它之下的使用者。

如果你的使用者名完全是數字(如77)或包含短橫線(如user-name),那麼當在YAML中指定使用者時,你需要使用另一種文法:

users: 

    - { name: 77, password: pass, roles: 'ROLE_USER' } 

    - { name: user-name, password: pass, roles: 'ROLE_USER' } 

對于小的網站,這個方式可以快速友善地進行設定。對于更複雜的系統,你将想從資料庫中引導你的使用者。

如果你想要通過Doctrine ORM來引導你的使用者,那麼你可以通過建立一個User類,配置實體提供器來很友善地實作這一功能。

使用這種方式,你首先需要建立你自己的使用者類,該類将被儲存在資料庫中。

// src/Acme/UserBundle/Entity/User.php 

namespace Acme\UserBundle\Entity; 

use Symfony\Component\Security\Core\User\UserInterface; 

use Doctrine\ORM\Mapping as ORM; 

 * @ORM\Entity 

class User implements UserInterface 

    /** 

     * @ORM\Column(type="string", length="255") 

     */ 

    protected $username; 

接下來,配置一個實體使用者提供器,并将其指向你的User類:

# app/config/security.yml 

        main: 

            entity: { class: Acme\UserBundle\Entity\User, property: username } 

根據這個新提供器的介紹,認證系統将嘗試從資料庫中通過該類的username來引導使用者對象。

到目前為止,為了簡單起見,所有示例都是用純文字來儲存使用者密碼的(無論這些使用者是儲存在配置檔案還是資料庫中)。當然,在實際應用程式中,為了安全起見你會加密使用者的密碼。将你的User類映射到幾個内建“加密器”之一上是很容易實作的。舉個例子,為了将你的使用者儲存在記憶體中,并通過sha1加密它們的密碼,可以如下所示:

                ryan:  { password: bb87a29949f3a1ee0559f8a57357487151281386, roles: 'ROLE_USER' } 

                admin: { password: 74913f5cd5f61ec0bcfdb775414c2fb3d161b620, roles: 'ROLE_ADMIN' } 

        Symfony\Component\Security\Core\User\User: 

            algorithm:   sha1 

            iterations: 1 

            encode_as_base64: false 

如果你正在動态建立你的使用者(并将其儲存在資料庫中),你甚至可以強制使用雜湊演算法,然後依靠一個實際的密碼加密對象來幫助你加密密碼。舉個例子,假設你的User對象是Acme\UserBundle\Entity\User(如上例所示)。首先,為使用者配置加密器:

        Acme\UserBundle\Entity\User: sha512 

在本例中,你使用更為健壯的sha512算法。此外,由于你簡單地指定sha512算法作為字元串,是以系統将預設将你的密碼哈希5000次,然後對其做base64加密。換句話說,該密碼已經足夠安全,以緻于無法進行解密(如你不能從被哈希過的密碼中算出原密碼來)。

如果你已經擁有了使用者的系統資料庫單,那麼你将需要确定被哈希的密碼,以便你可以為你的使用者進行設定。無論你為你的User對象配置為何種算法,被哈希的密碼總是可以從控制器得到:

$factory = $this-&gt;get('security.encoder_factory'); 

$user = new Acme\UserBundle\Entity\User(); 

$encoder = $factory-&gt;getEncoder($user); 

$password = $encoder-&gt;encodePassword('ryanpass', $user-&gt;getSalt()); 

$user-&gt;setPassword($password); 

在認證之後,目前使用者的User對象可以通過security.context服務來通路。從控制器内部來看,如下所示:

After authentication, the User object of the current user can be accessed via the security.context service. From inside a controller, this will look like:

public function indexAction() 

    $user = $this-&gt;get('security.context')-&gt;getToken()-&gt;getUser(); 

匿名使用者可以在技術上被認證,也就是說匿名使用者對象的isAuthenticated()方法将傳回true。要檢查你的使用者實際上是否被認證,請檢查IS_AUTHENTICATED_FULL角色。

每個認證機制(如:HTTP認證、表單登入等)隻使用一個使用者提供器,并且預設情況下隻使用第1個聲明的使用者提供器。但你是否想指定一些使用者通過配置認證,而剩下的使用者是在資料庫中呢?通過建立一個新的提供器,并将兩者串在一起是可能實作的:

        chain_provider: 

            providers: [in_memory, user_db] 

                foo: { password: test } 

        user_db: 

現在所有的認證機器都将使用chain_provider,因為它是第一個被指定的。chain_provider将反過來嘗試從in_memony和user_db提供器中引導使用者。

如果你沒有理由從你的user_db使用者中分隔出in_memory使用者來,那麼你甚至可以通過将兩個源合并成單個的提供器來更友善地實作它:

        main_provider: 

你也可以配置防火牆或者單個認證機制來使用特定的提供器。同樣,除非指定一個提供器,否則總是使用第1個提供器:

            # ... 

            provider: user_db 

                provider: in_memory 

            form_login: ~

在本例中,如果使用者嘗試通過HTTP認證登入,認證系統将使用in_memory使用者提供器。但如果使用嘗試通過表單登入,那麼user_db提供器将被使用(因為它對整個防火牆來說是預設的)。

"role"的概念是授權過程的關鍵。每個使用者都被指定了一組角色,然後每個資源要求一個或更多的角色。如果使用者擁有要求的角色,那麼被授予通路權限。否則通路将被拒絕。

角色非常簡單,基本上都是字元串,你可以在必要時找到并使用(雖然角色在内部也是對象)。舉個例子,如果你需要在你網站的部落格管理部分開始受限的通路,你可以使用ROLE_BLOG_ADMIN角色來保護該部分。該角色無須在某處定義,你隻需要使用即可。

所有角色必須以ROLE_做為字首,以便被Symfony2管理。如果你通過專用的Role類(更為進階)來定義你的角色,那麼不要使用ROLE_字首。

為了避免為使用者關聯許多的角色,你可以通過建立一個角色繼承來定義角色繼承規則:

    role_hierarchy: 

        ROLE_ADMIN:       ROLE_USER 

        ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH] 

在上面的配置裡,有着ROLE_ADMIN角色的使用者也擁有ROLE_USER角色。ROLE_SUPER_ADMIN角色則擁有ROLE_ADMIN、ROLE_ALLOWED_TO_SWITCH和 ROLE_USER(繼承自ROLE_ADMIN)等角色

通常,你也希望你的使用者能夠登出。幸運地是,防火牆可以為你自動處理這一過程,你隻需要激活登出配置參數即可:

            logout: 

                path:   /logout 

                target: / 

一旦在你防火牆下配置完成,發送使用者到/logout(或者無論何時你将path配置成去那),将登出目前使用者。使用者然後被發送到首頁(通過target參數定義)。path和target配置參數預設是指向這兒的。換句話說,除非你要自定義它們,否則你可以整個忽略它們并減縮你的配置:

logout: ~ 

注意,你不需要為/logout的URL去實作控制器,防火牆完成了這一切。然而,你也許希望建立一個路由,以便你可以用它來生成URL:

logout: 

    pattern:   /logout 

如果你想在模闆中檢查目前使用者是否擁有角色,那麼使用内建的幫手函數:

{% if is_granted('ROLE_ADMIN') %} 

    &lt;a href="..."&gt;Delete&lt;/a&gt; 

如果你使用該函數,且不是防火牆激活的URL,一個異常将被抛出。而且使用一個主防火牆去覆寫所有的URL(在本章中被顯示)總是個好主意。

如果你希望在控制器中檢查目前使用者是否擁有角色,使用security.context的isGranted方法:

    // show different content to admin users 

    if($this-&gt;get('security.context')-&gt;isGranted('ADMIN')) { 

        // Load admin content here 

    // load other regular content here 

當isGranted方法被調用時防火牆必須是激活的,否則将抛出一個異常。參見上面關于模闆部分的說明以得到更多細節

有時,下面情形是有用的,從一個使用者切換到另一個使用者,而無須登出再登入(例如當你正在調試或嘗試了解一個就該使用者而言無法重制的錯誤時)。通過激活防火牆監聽器switch_user是很容易實作的:

            switch_user: true 

要切換到另一個使用者,隻需要為目前URL添加一個帶有_switch_user參數的查詢字元串,且該參數的值是使用者值:

<a href="http://example.com/somewhere?_switch_user=thomas">http://example.com/somewhere?_switch_user=thomas</a>

而要切回最初的使用者,指定_exit使用者名。

<a href="http://example.com/somewhere?_switch_user=_exit">http://example.com/somewhere?_switch_user=_exit</a>

當然,這一功能有必要提供給一小部分使用者。預設情況下,權限會被限制到擁有ROLE_ALLOWED_TO_SWITCH角色的使用者。該角色的使用者可以通過角色設定來修改。為了得到更多的安全,你也可以通過參數設定來改變查詢參數名:

            // ... 

            switch_user: { role: ROLE_ADMIN, parameter: _want_to_be_this_user } 

預設情況下,Symfony2依賴cookie(會話)去持久化使用者的安全上下文。但如果你使用證書或HTTP認證,持久化不需要每個請求都有證書可用。在那種情況下,如果不需要在兩個請求之間儲存什麼的話,你可以激活無狀态認證(意思是沒有cookie被Symfony2建立):

            http_basic: ~ 

            stateless:  true 

如果你使用表單登入的話,即使你将stateless設定true,Symfony2也将建立一個cookie。

要在你應用程式中正确地解決安全,可是個深刻而複雜的問題。幸運的是,Symfony2的安全元件遵循一個久經考驗的安全模型,該模型是基于認證和授權的。認證總是最先發生,它被防火牆處理,防火牆的工作是通過幾種不同的方式(如HTTP認證、登入表單等)來确定使用者的身份。在食譜(cookbook)中,你會找到使用其它方式來處理認證的例子,包括如何實作“記住我”cookie的功能。

一旦使用者被認證了,授權層就可以決定使用者是否有通路特定資源的權限了。通常情況下,角色被應用到URL、類或方法上。如果目前使用者沒有該角色,那麼通路被拒絕。然而,授權層更為深入,它遵循表決系統以便多方确定目前使用者是否有權通路指定的資源。在食譜(cookbook)中可以找到更多關于這個和其它的主題。 

本文轉自 firehare 51CTO部落格,原文連結:http://blog.51cto.com/firehare/583329,如需轉載請自行聯系原作者