亚洲国产日韩欧美一区二区三区,精品亚洲国产成人av在线,国产99视频精品免视看7,99国产精品久久久久久久成人热,欧美日韩亚洲国产综合乱

Symfony2中文手冊 / 安全(Security)

安全(Security)

Symfony的安全系統(tǒng)(security system)是非常強大的,但在設置它時也可能令人迷惑。在本大章中,你將學會如何一步步地設置程序的security,從配置防火墻(firewall)以及加載用戶,到拒絕訪問和取到用戶對象。根據你的需求,有時在進行初始化設置時是痛苦的。然而一旦(配置)完成,Symfony的security系統(tǒng)在用起來時,則是既靈活又(希望能夠)有樂趣。

由于有很多話要說,本章按幾個大部頭來組織:

  1. 初始化 security.yml 的設置 (authentication/驗證 );

  2. 拒絕訪問你的程序 (authorization/授權 );

  3. 獲取當前的User對象;

它們被細分為許多小塊內容(但仍然令人著迷),像是 logging out加密用戶密碼。

1)初始化security.yml的設置 (Authentication/驗證) ?

security系統(tǒng)在 app/config/security.yml 中進行配置。默認的配置是這樣的:

PHP:// app/config/security.php$container->loadFromExtension('security', array(
    'providers' => array(
        'in_memory' => array(
            'memory' => null,
        ),
    ),
    'firewalls' => array(
        'dev' => array(
            'pattern'    => '^/(_(profiler|wdt)|css|images|js)/',
            'security'   => false,
        ),
        'default' => array(
            'anonymous'  => null,
        ),
    ),));
XML:<!-- app/config/security.xml --><?xml version="1.0" encoding="UTF-8"?><srv:container xmlns="http://Symfony.com/schema/dic/security"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xmlns:srv="http://Symfony.com/schema/dic/services"    xsi:schemaLocation="http://Symfony.com/schema/dic/services        http://Symfony.com/schema/dic/services/services-1.0.xsd">     <config>
        <provider name="in_memory">
            <memory />
        </provider>         <firewall name="dev"            pattern="^/(_(profiler|wdt)|css|images|js)/"            security="false" />         <firewall name="default">
            <anonymous />
        </firewall>
    </config></srv:container>
YAML:# app/config/security.ymlsecurity:
    providers:
        in_memory:
            memory: ~
    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        default:
            anonymous: ~

firewalls 鍵是security配置的 核心。dev 防火墻并不重要,它只是確保Symfony開發(fā)工具 - 也就是居于 /_profiler/_wdt 之下的那些URL將不會被你的security所阻止。

你也可以針對請求中的其他細節(jié)來匹配一個請求(如 host主機)。閱讀 如何把防火墻限制在特定請求 以了解更多內容和例程。

所有其他的URL將被 default 防火墻中處理(沒有 pattern 鍵意味著它可以匹配所有 URL)。你可以認為防火墻就是你的security系統(tǒng),因此通常只有一個主力防火墻就變得有意義了。但這并 意味著每一個URL都需要驗證 - anonymous 鍵可以搞定這個。事實上,如果你現在去首頁,是可以訪問的,并將看到你以 anon. 身份“通過了驗證”。不要被Authenticated旁邊的“Yes”愚弄,你仍然只是個匿名用戶:

11.png

后面你將學習到如何拒絕訪問特定URL或控制器(中的action)。

Security是高度 可配置的,在 Security配置參考 里展示了全部配置選項,并有一些附加說明。

A) 配置“如何令用戶接受驗證” ?

防火墻的主要工作是去配置如何 讓你的用戶能夠被驗證。它們是否需要使用登錄表單?或是HTTP basic驗證?抑或一個API token?還是上面所有這些?

讓我們從HTTP basic驗證開始(老舊的彈出框)展開。要激活此功能,添加 http_basic 到firewall下面:

PHP:// app/config/security.php$container->loadFromExtension('security', array(
    // ...
    'firewalls' => array(
        // ...
        'default' => array(
            'anonymous'  => null,
            'http_basic' => null,
        ),
    ),));
XML:<!-- app/config/security.xml --><?xml version="1.0" encoding="UTF-8"?><srv:container xmlns="http://Symfony.com/schema/dic/security"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xmlns:srv="http://Symfony.com/schema/dic/services"    xsi:schemaLocation="http://Symfony.com/schema/dic/services        http://Symfony.com/schema/dic/services/services-1.0.xsd">     <config>
        <!-- ... -->         <firewall name="default">
            <anonymous />
            <http-basic />
        </firewall>
    </config></srv:container>
YAML:# app/config/security.ymlsecurity:    # ...
    firewalls:        # ...
        default:
            anonymous: ~
            http_basic: ~
真簡單!要測試效果,你需要讓用戶登錄并看到頁面。為了讓事情變的有趣,在 /admin 下創(chuàng)建一個新頁面 。例如,如果你使用annotations,創(chuàng)建下例代碼:
// src/AppBundle/Controller/DefaultController.php
// ... use 
Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;use Symfony\Bundle\FrameworkBundle\Controller\Controller;use Symfony\Component\HttpFoundation\Response; class DefaultController extends Controller{
   /**
     * @Route("/admin")
     */
    public function adminAction()
    {
        return new Response('<html><body>Admin page!</body></html>');
    }}

接下來,在 security.yml 中添加一個 access_control 入口,以令用戶必先登錄方可訪問URL:

PHP:// app/config/security.php$container->loadFromExtension('security', array(
    // ...
    'firewalls' => array(
        // ...
        'default' => array(
            // ...
        ),
    ),
   'access_control' => array(
       // require ROLE_ADMIN for /admin*
        array('path' => '^/admin', 'role' => 'ROLE_ADMIN'),
    ),));
XML:<!-- app/config/security.xml --><?xml version="1.0" encoding="UTF-8"?><srv:container xmlns="http://Symfony.com/schema/dic/security"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xmlns:srv="http://Symfony.com/schema/dic/services"    xsi:schemaLocation="http://Symfony.com/schema/dic/services        http://Symfony.com/schema/dic/services/services-1.0.xsd">     <config>
        <!-- ... -->         <firewall name="default">
            <!-- ... -->
        </firewall>         <!-- require ROLE_ADMIN for /admin* -->
        <rule path="^/admin" role="ROLE_ADMIN" />
    </config></srv:container>
YAML:# app/config/security.ymlsecurity:    # ...
    firewalls:        # ...
        default:            # ...
    access_control:        # require ROLE_ADMIN for /admin*
        - { path: ^/admin, roles: ROLE_ADMIN }

你將學到關于 ROLE_ADMIN 以及后面的 2) 拒絕訪問, Roles和其他授權 小節(jié)中的“拒絕訪問”等更多內容。

太好了,如果你去訪問 /admin,會看到HTTP Basic驗證的彈出式登錄:

222.png

但是,你是以何種身份登錄進來?用戶(信息)又來自哪里呢?

想要使用一個傳統(tǒng)的form表單?很好!參考 如何建立一個傳統(tǒng)的登錄表單。還支持其他什么(驗證)方式?參考 Configuration Reference 或者 構建你自己的。

如果你的程序是通過諸如Google,Facebook或者Twitter等三方服務來完成登錄,參閱 HWIOAuthBundle 社區(qū)bundle。

B) 配置“如何加載用戶” ?

當你在輸入用戶名時,Symfony就要從某個地方加載用戶的信息。這就是所謂的“user provider”,由你來負責配置它。Symfony有一個內置方式來 從數據庫中加載用戶,但也可以 創(chuàng)建你自己的user provider。

最簡單的方式(但有很多限制),是配置Symfony從 security.yml 文件里直接加載寫死在其中的用戶。這被稱為“in memory” provider,但把它觀想為“in configuration” provider更合適些

PHP:// app/config/security.php$container->loadFromExtension('security', array(
    'providers' => array(
        'in_memory' => array(
            'memory' => array(
                'users' => array(
                    'ryan' => array(
                        'password' => 'ryanpass',
                        'roles' => 'ROLE_USER',
                    ),
                    'admin' => array(
                        'password' => 'kitten',
                        'roles' => 'ROLE_ADMIN',
                    ),
                ),
            ),
        ),
    ),
    // ...));
XML:<!-- app/config/security.xml --><?xml version="1.0" encoding="UTF-8"?><srv:container xmlns="http://Symfony.com/schema/dic/security"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xmlns:srv="http://Symfony.com/schema/dic/services"    xsi:schemaLocation="http://Symfony.com/schema/dic/services        http://Symfony.com/schema/dic/services/services-1.0.xsd">     <config>
        <provider name="in_memory">
            <memory>
                <user name="ryan" password="ryanpass" roles="ROLE_USER" />
                <user name="admin" password="kitten" roles="ROLE_ADMIN" />
            </memory>
        </provider>
        <!-- ... -->
    </config></srv:container>
YAML:# app/config/security.ymlsecurity:
    providers:
        in_memory:
            memory:
                users:
                    ryan:
                        password: ryanpass
                        roles: 'ROLE_USER'
                    admin:
                        password: kitten
                        roles: 'ROLE_ADMIN'    # ...

類似 firewalls,你可以擁有多個 providers ,但你幾乎只需要一個。如果你確實 擁有多個,你可以在防火墻的 provider 鍵(如 provider:in_memory)之下,配置“要使用哪一個”provider。

參考 如何使用多個User Providers 來了解multiple providers設置的全部細節(jié)。

試著以用戶名 admin 和密碼 kitten 來登錄。你應該看到一個錯誤!

No encoder has been configured for account "Symfony\Component\Security\Core\User\User"

要修復此問題 ,添加一個 encoders ?。?/p>

PHP:// app/config/security.php$container->loadFromExtension('security', array(
    // ...     'encoders' => array(
        'Symfony\Component\Security\Core\User\User' => 'plaintext',
    ),
    // ...));
XML:<!-- app/config/security.xml --><?xml version="1.0" encoding="UTF-8"?><srv:container xmlns="http://Symfony.com/schema/dic/security"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xmlns:srv="http://Symfony.com/schema/dic/services"    xsi:schemaLocation="http://Symfony.com/schema/dic/services        http://Symfony.com/schema/dic/services/services-1.0.xsd">     <config>
        <!-- ... -->         <encoder class="Symfony\Component\Security\Core\User\User"            algorithm="plaintext" />
        <!-- ... -->
    </config></srv:container>
YAML:app/config/security.ymlsecurity:    # ...
    encoders:
        Symfony\Component\Security\Core\User\User: plaintext    # ...

User providers加載用戶信息,并將其置于一個 User 對象中。如果你從數據庫中加載用戶 或者 從其他來源加載,你需要使用自定義的User類。但當你使用“in memory” provider時,它直接給了你一個 Symfony\Component\Security\Core\User\User 對象。

無論你使用什么樣的User類,你都需要去告訴Symfony它們的密碼加密算法是什么。在本例中,密碼只是明文的文本,但很快,你要把它改成 bcrypt

如果你現在刷新,你就會登錄進來!web除錯工具欄會告訴你,你是何人,你的roles(角色)是什么:

4444t.png

由于此URL需要的是 ROLE_ADMIN ,如果你登錄的是 ryan 用戶,將被拒絕訪問。更多內容參考后面的 [URL受到保護的條件(access_control](#catalog9)。

從數據庫加載用戶 ?

如果你想通過Doctrine Orm加載用戶,很容易!參考 如何從數據庫中(Entity Provider)加載Security系統(tǒng)之用戶 以了解全部細節(jié)。

C) 對用戶密碼進行加密 ?

不管用戶是存儲在 security.yml 里,數據庫里還是其他地方,你都需要去加密其密碼??坝玫淖詈盟惴ㄊ?bcrypt

PHP:// app/config/security.php$container->loadFromExtension('security', array(
    // ...     'encoders' => array(
        'Symfony\Component\Security\Core\User\User' => array(
            'algorithm' => 'bcrypt',
            'cost' => 12,
        )
    ),
    // ...));
XML:<!-- app/config/security.xml --><?xml version="1.0" encoding="UTF-8"?><srv:container xmlns="http://Symfony.com/schema/dic/security"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xmlns:srv="http://Symfony.com/schema/dic/services"    xsi:schemaLocation="http://Symfony.com/schema/dic/services        http://Symfony.com/schema/dic/services/services-1.0.xsd">     <config>        <!-- ... -->         <encoder class="Symfony\Component\Security\Core\User\User"            algorithm="bcrypt"            cost="12" />         <!-- ... -->    </config></srv:container>


YAML:# app/config/security.ymlsecurity:    # ...
    encoders:
        Symfony\Component\Security\Core\User\User:
            algorithm: bcrypt
            cost: 12


當然,用戶密碼現在需要被這個指定算法加密。對于寫死(在config.yml中)的用戶,你可以用內置命令來完成:

1
$  php bin/console security:encode-password

它將帶給你下面這樣(密碼被加密)的東東:

PHP:// app/config/security.php$container->loadFromExtension('security', array(
    // ...     'providers' => array(
        'in_memory' => array(
            'memory' => array(
                'users' => array(
                    'ryan' => array(
                        'password' => '$2a$12$LCY0MefVIEc3TYPHV9SNnuzOfyr2p/AXIGoQJEDs4am4JwhNz/jli',
                        'roles' => 'ROLE_USER',
                    ),
                    'admin' => array(
                        'password' => '$2a$12$cyTWeE9kpq1PjqKFiWUZFuCRPwVyAZwm4XzMZ1qPUFl7/flCM3V0G',
                        'roles' => 'ROLE_ADMIN',
                    ),
                ),
            ),
        ),
    ),
    // ...));
XML:<!-- app/config/security.xml --><?xml version="1.0" encoding="UTF-8"?><srv:container xmlns="http://Symfony.com/schema/dic/security"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xmlns:srv="http://Symfony.com/schema/dic/services"    xsi:schemaLocation="http://Symfony.com/schema/dic/services        http://Symfony.com/schema/dic/services/services-1.0.xsd">     <config>
        <!-- ... -->         <provider name="in_memory">
            <memory>
                <user name="ryan" password="$2a$12$LCY0MefVIEc3TYPHV9SNnuzOfyr2p/AXIGoQJEDs4am4JwhNz/jli" roles="ROLE_USER" />
                <user name="admin" password="$2a$12$cyTWeE9kpq1PjqKFiWUZFuCRPwVyAZwm4XzMZ1qPUFl7/flCM3V0G" roles="ROLE_ADMIN" />
            </memory>
        </provider>
    </config></srv:container>
YAML:# app/config/security.ymlsecurity:    # ...
    providers:
        in_memory:
            memory:
                users:
                    ryan:
                        password: $2a$12$LCY0MefVIEc3TYPHV9SNnuzOfyr2p/AXIGoQJEDs4am4JwhNz/jli
                        roles: 'ROLE_USER'
                    admin:
                        password: $2a$12$cyTWeE9kpq1PjqKFiWUZFuCRPwVyAZwm4XzMZ1qPUFl7/flCM3V0G
                        roles: 'ROLE_ADMIN'

現在一切都和以前一樣。但如果你有動態(tài)用戶(如,數據庫中的),其密碼在入庫之前,你如何才能程序化的加密之呢?別擔心,參考 手動加密密碼 以了解細節(jié)。

此種方式的加密算法能否受到支持取決于你的PHP版本,但是包括 hash_algos PHP函數所返回的算法,以及一些其他(如 bcrypt)都受到支持。參考 Security參考 中的 encoders 選項鍵作為示例。

針對不同的用戶,分別使用不同的算法也是可能的。參考 如何動態(tài)選擇密碼加密算法 以了解更多細節(jié)。

D) 配置完成! ?

祝賀你!現在你有了一個可以使用的基于HTTP basic驗證、并從 security.yml 文件中加載用戶的“驗證系統(tǒng)”了。

根據你的設置,尚有后續(xù)步驟:

2) 拒絕訪問,Roles和其他授權方式 ?

現在,用戶可以通過 http_basic 或者其他方式來登錄你的程序。了不起呀!現在你需要學習如何拒絕訪問(deny access)并且能與User對象一起工作。這被稱為authorization(授權),它的工作是決定用戶是否可以訪問某些資源(如一個URL、一個model對象、一個被調用的方法等...)。

授權過程分為兩個不同的方面:

  1. 用戶在登錄時收到一組特定的Roles(角色。如 ROLE_ADMIN)。

  2. 你添加代碼,以便某個資源(例如url、控制器)需要一個特定“屬性”(多數時候就是一個類似 ROLE_ADMIN 的role)才能被訪問到。

除了roles(如 ROLE_ADMIN ),你還可以使用其他屬性/字符串來(如 EDIT)來保護一個資源,并且通過使用voters或Symfony的ACL系統(tǒng),來令它們生效。這在你需要檢查用戶A能否“編輯”對象B(如,id為5的產品)時極為好用。參考 Access Control Lists (ACLs):保護單個數據庫對象。

Roles/角色 ?

當有用戶登錄時,他們會接收到一組roles(如 ROLE_ADMIN)。在上例中,這些(roles)都被寫死到 security.yml 中了。如果你從數據庫中加載用戶,它們應該存在了表的某一列中。

你要分給一個用戶的所有roles,一定要以 ROLE_ 前綴開始。否則,它們不會被Symfony的Security系統(tǒng)按常規(guī)方式來操作(除非使用高級手段,否則你把 FOO 這樣的role分給一個用戶,然后像 下面這樣 去檢查 FOO 是行不通的)。

roles很簡單,而且基本上都是你根據需要自行創(chuàng)造的字符串。例如,如果你打算限制訪問你網站博客的admin部分,可以使用ROLE_BLOG_ADMIN 這個role來進行保護。role毋須在其他地方定義-你就可以開始用它了。

確保每個用戶至少有一個 角色,否則他們會被當作未驗證之人。常見的做法就是給每個 普通用戶一個 ROLE_USER。

你還可以指定role層級,此時,擁有某些roles意味著你同時擁有了其他的roles。

添加代碼以拒絕訪問 ?

要拒絕訪問(deny access)某些東東,有兩種方式可以實現:

通過條件匹配來保護URL(access_control) ?

對程序的某一部分進行保護時的最基本方法是對URL進行完整的條件匹配。之前你已看到,任何匹配了正則表達式 ^/admin 的頁面都需要 ROLE_ADMIN

PHP:// app/config/security.php$container->loadFromExtension('security', array(
    // ...     'firewalls' => array(
        // ...
        'default' => array(
            // ...
        ),
    ),
   'access_control' => array(
       // require ROLE_ADMIN for /admin*
        array('path' => '^/admin', 'role' => 'ROLE_ADMIN'),
    ),));
XML:<!-- app/config/security.xml --><?xml version="1.0" encoding="UTF-8"?><srv:container xmlns="http://Symfony.com/schema/dic/security"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xmlns:srv="http://Symfony.com/schema/dic/services"    xsi:schemaLocation="http://Symfony.com/schema/dic/services        http://Symfony.com/schema/dic/services/services-1.0.xsd">     <config>
        <!-- ... -->         <firewall name="default">
            <!-- ... -->
        </firewall>         <!-- require ROLE_ADMIN for /admin* -->
        <rule path="^/admin" role="ROLE_ADMIN" />
    </config></srv:container>
YAML:# app/config/security.ymlsecurity:    # ...
    firewalls:        # ...
        default:            # ...
    access_control:        # require ROLE_ADMIN for /admin*
        - { path: ^/admin, roles: ROLE_ADMIN }

能夠保護全部(URL所屬的)區(qū)域固然很好,但你可能還想 保護控制器的某一action。

如果需要,你可以定義任意多個URL匹配條件 - 每個條件都是正則表達式。但是,僅僅有一個會被匹配。Symfony會從(文件中的)頂部開始尋找,一旦發(fā)現 access_control 中的某個入口與URL相匹配,就立即結束。

PHP:// app/config/security.php$container->loadFromExtension('security', array(
    // ...     'access_control' => array(
        array('path' => '^/admin/users', 'role' => 'ROLE_SUPER_ADMIN'),
        array('path' => '^/admin', 'role' => 'ROLE_ADMIN'),
    ),));
XML:<!-- app/config/security.xml --><?xml version="1.0" encoding="UTF-8"?><srv:container xmlns="http://Symfony.com/schema/dic/security"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xmlns:srv="http://Symfony.com/schema/dic/services"    xsi:schemaLocation="http://Symfony.com/schema/dic/services        http://Symfony.com/schema/dic/services/services-1.0.xsd">     <config>
        <!-- ... -->         <rule path="^/admin/users" role="ROLE_SUPER_ADMIN" />
        <rule path="^/admin" role="ROLE_ADMIN" />
    </config></srv:container>
YAML:# app/config/security.ymlsecurity:    # ...
    access_control:
        - { path: ^/admin/users, roles: ROLE_SUPER_ADMIN }
        - { path: ^/admin, roles: ROLE_ADMIN }

在path中加了一個 ^ 是指,僅當URL“按照正則條件那樣起頭”時,才會匹配到。例如,一個path若只有 /admin(不包含 ^)則會匹配到 /admin/foo 但同時也匹配了 /foo/admin 這種。

理解access_control是如何工作的

access_control 部分異常強大,但如果你不明白它的工作原理,它也會很危險(畢竟涉及到安全性)。 access_control 除了匹配URL,還可匹配IP地址、主機名和HTTP method。它也可以用于將用戶重定向到 https 版本的URL條件中去。

要了解這一切,參考 Security的access_control是如何工作的。

保護控制器和代碼中的其他部分 ?

在控制器中你可以輕松謝絕訪問:

// ... public function helloAction($name){
    // The second parameter is used to specify on what object the role is tested.
    // 第二個參數用于指定“要將role作用到什么對象之上”
    $this->denyAccessUnlessGranted('ROLE_ADMIN', null, 'Unable to access this page!');     // Old way / 老辦法:
    // if (false === $this->get('security.authorization_checker')->isGranted('ROLE_ADMIN')) {
    //     throw $this->createAccessDeniedException('Unable to access this page!');
    // }     // ...}

兩種情況下,一個特殊的 AccessDeniedException 會被拋出,這最終觸發(fā)了Symfony內部的一個403 HTTP響應。

就是這樣!如果是尚未登錄的用戶,他們會被要求登錄(如,重定向到登錄頁面)。如果他們已經 登錄,但不具備 ROLE_ADMIN 角色,則會被顯示403拒絕訪問頁面(此頁可以 自定義)。如果他們已登錄同時擁有正確的roles,代碼就會繼續(xù)執(zhí)行。

多虧了SensioFrameworkExtraBundle,你可以在控制器中使用annotations

// ...use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security; /**
 * @Security("has_role('ROLE_ADMIN')")
 */public function helloAction($name){
    // ...}

參考 FrameworkExtraBundle 以了解更多。

模版中的訪問控制 ?

如果你想在模版中檢查當前用戶是否具有一個role,可以使用內置的 is_granted() helper函數:

PHP:<?php if ($view['security']->isGranted('ROLE_ADMIN')): ?>
    <a href="...">Delete</a><?php endif ?>
Twig:{% if is_granted('ROLE_ADMIN') %}
    <a href="...">Delete</a>{% endif %}

保護其他服務 ?

若像“保護控制器的”那樣編寫一些類似代碼,Symfony中的任何地方都可以被保護。假設你有一個服務(即一個php類)用于發(fā)送電子郵件。你可以限制使用此類 - 不管它被用在何處 - 只有特定用戶可以使用它。

參考 如何保護程序中的服務和方法 以了解多。

檢查用戶是否已登錄(IS_AUTHENTICATED_FULLY) ?

目前為止,你已經檢查了基于role的訪問 - 那些以 ROLE_ 前綴開頭的字符串,被分配給了用戶。但是,如果你 想檢查用戶是否登錄(并不關心什么role不role的),那么你可以使用 IS_AUTHENTICATED_FULLY// ... public function helloAction($name){

    if (!$this->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_FULLY')) {
        throw $this->createAccessDeniedException();
    }     // ...}

你當然也可以使用在 access_control 中使用它。

IS_AUTHENTICATED_FULLY 不是一個role,但是它的某些行為又像是role,并且每個成功登錄的用戶都有這么一個。事實上,類似的特殊屬性共有三個:

  • IS_AUTHENTICATED_REMEMBERED所有 已登錄用戶都有它,哪怕他們是通過“remember me cookie”登錄進來的。就算你并沒有使用 remember me功能,你依然能夠通過這個屬性來檢查用戶是否已經登錄。

  • IS_AUTHENTICATED_FULLY:類似于 IS_AUTHENTICATED_REMEMBERED ,但更健壯。那些僅憑 “remember me cookie”中的 IS_AUTHENTICATED_REMEMBERED 登錄進來的用戶,并不會擁有 IS_AUTHENTICATED_FULLY 。

  • IS_AUTHENTICATED_ANONYMOUSLY所有 用戶(甚至是匿名用戶)都有此屬性-當把URL置于白名單 以確保能被訪問時,它很有用。參考 Security的access_control是如何工作的 以了解更多。

你還可以在模版中使用表達式:

PHP:<?php if ($view['security']->isGranted(new Expression(
    '"ROLE_ADMIN" in roles or (user and user.isSuperAdmin())'))): ?>
    <a href="...">Delete</a><?php endif; ?>
Twig:{% if is_granted(expression(
    '"ROLE_ADMIN" in roles or (user and user.isSuperAdmin())')) %}
    <a href="...">Delete</a>{% endif %}

關于表達式和security性的更多細節(jié),參考 Security: 復雜的Access Controls表達式.

Access Control Lists (ACLs):保護單個數據庫對象 ?

試想,你正在設計一個博客,用戶可以在主題下面發(fā)表評論。你還想讓用戶能編輯自己的評論,但其他用戶不要想。此外,作為管理員用戶,你希望能夠編輯所有 評論。

要做到這一點,你有兩個選擇:

  • Voters 允許用戶編寫自己的業(yè)務邏輯(如,用戶可以編輯這篇文章是因為他們是創(chuàng)建人)來檢查訪問。你可能會使用這個選擇 - 它足夠靈活,可以解決以上問題。

  • ACLs 允許你創(chuàng)建一個數據庫結構,在其中,你可以對任意 用戶分配針對任意對象的任意 訪問權限(如,EDIT、VIEW)。要使用ACLs,你需要一個管理員用戶,以便通過某些管理界面,來對你的系統(tǒng)進行自定義的權限分配(grant custom access)。

兩種情況下,你仍然需要使用與前面例子中相類似的方法來實施deny access(拒絕訪問)。

獲取用戶對象 ?

驗證之后,就可以通過 security.token_storage 服務來訪問當前用戶的 User 對象。在控制器內,這樣寫:

public function indexAction(){
    if (!$this->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_FULLY')) {
        throw $this->createAccessDeniedException();
    }     $user = $this->getUser();     // the above is a shortcut for this / 上面的寫法是通過以下取法(再進行授權)的快捷方式
    $user = $this->get('security.token_storage')->getToken()->getUser();}


用戶將是一個對象,該對象所屬的類,則取決于你的user provider。


3.2通過方法簽名(method signature)來取得用戶的功能,是從Symfony 3.2開始引入的。如果你繼承了 Controller,則仍然可以通過調用 $this->getUser() 來獲取。

現在,基于你的 User對象,可以調用任何方法。例如,如果User對象中有一個 getFirstName() 方法,你可以使用它:

use Symfony\Component\HttpFoundation\Response;
// ... public function indexAction(){
  // ...     return new Response('Well hi there '.$user->getFirstName());}

始終檢測用戶是否登錄 ?

對用戶的初次驗證十分重要。如果未登陸,$user 就是 null 或者 anon. 字符串之一。等一下,為什么呢?是的,這很奇怪。如果你沒有登錄,嚴格來講,用戶應該是 anon.,盡管控制器中的 getUser() 快捷方法會出于方便將其轉變?yōu)?null。當使用 UserInterface 的類型提示并且登入(being logged in)是可選的時候,你可以為參數配置null值:

public function indexAction(UserInterface $user = null){
    // $user is null when not logged-in or anon.
    // 當用戶沒有登陸,或者是anno.時,$user是null}

觀點是這樣的:在使用User對象之前應該始終檢查用戶是否已登錄,可使用 isGranted 方法(或 access_control)來完成:

// yay! Use this to see if the user is logged in// 耶!使用這個來查看用戶是否已登陸if (!$this->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_FULLY')) {
    throw $this->createAccessDeniedException();} // boo :(. Never check for the User object to see if they're logged in// 哄!:(. 切勿通過檢查User對象來判斷用戶是否已登陸if ($this->getUser()) { }

在模版中獲取用戶 ?

對象在twig模版中可以使用app.user鍵:

PHP:<?php if ($view['security']->isGranted('IS_AUTHENTICATED_FULLY')): ?>
    <p>Username: <?php echo $app->getUser()->getUsername() ?></p><?php endif; ?>
Twig:{% if is_granted('IS_AUTHENTICATED_FULLY') %}
    <p>Username: {{ app.user.username }}</p>{% endif %}

Logging out/退出登錄 ?

注意,當使用http-basic驗證方式的防火墻時,是沒有辦法退出的:log out 的唯一方式就是令瀏覽器停止在每次請求中發(fā)送你的用戶名和密碼。清除瀏覽器緩存或重啟它,往往有用。某些web開發(fā)工具(譯注:可能是指瀏覽器的f12)也可能有用。

通常情況下,你希望用戶能夠注銷。幸運的是,當你激活 logout 配置參數后,防火墻可以幫助你自動處理:

PHP:// app/config/security.php$container->loadFromExtension('security', array(
    // ...     'firewalls' => array(
        'secured_area' => array(
            // ...
            'logout' => array('path' => '/logout', 'target' => '/'),
        ),
    ),));
Twig:<!-- app/config/security.xml --><?xml version="1.0" encoding="UTF-8"?><srv:container xmlns="http://Symfony.com/schema/dic/security"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xmlns:srv="http://Symfony.com/schema/dic/services"    xsi:schemaLocation="http://Symfony.com/schema/dic/services        http://Symfony.com/schema/dic/services/services-1.0.xsd">     <config>
        <!-- ... -->         <firewall name="secured_area">
            <!-- ... -->
            <logout path="/logout" target="/" />
        </firewall>
    </config></srv:container>
YAML:# app/config/security.ymlsecurity:    # ...
    firewalls:
        secured_area:            # ...
            logout:
                path:   /logout
                target: /

接下來,你需要去給這個URL創(chuàng)建一個路由(但不需要控制器):

PHP:// app/config/routing.phpuse Symfony\Component\Routing\RouteCollection;use Symfony\Component\Routing\Route; $collection = new RouteCollection();$collection->add('logout', new Route('/logout')); return $collection;
XML:<!-- app/config/routing.xml --><?xml version="1.0" encoding="UTF-8" ?><routes xmlns="http://Symfony.com/schema/routing"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:schemaLocation="http://Symfony.com/schema/routing        http://Symfony.com/schema/routing/routing-1.0.xsd">     <route id="logout" path="/logout" /></routes>
YAML:# app/config/routing.ymllogout:
    path: /logout

就是這樣!通過把用戶發(fā)送到 /logout (或者你任意配置的 path 選項),Symfony將取消當前用戶的驗證信息。

一旦用戶被注銷,他們會被重定向到已經定義的 target 參數所對應的路徑中(如 homepage)。

如果你需要在注銷后做一些更有趣的事,可以通過添加 success_handler 鍵來指定一個“登出成功控制器”(logout success handler),將它填寫成一個服務定義之id,該服務的class必須實現 LogoutSuccessHandlerInterface接口。參考 Security Configuration Reference。

按等級劃分的Roles ?

并非把大量roles統(tǒng)統(tǒng)關聯到用戶,通過創(chuàng)建role hierarchy(角色層級),你可以定義一套“角色繼承規(guī)則”:

PHP:// app/config/security.php$container->loadFromExtension('security', array(
    // ...     'role_hierarchy' => array(
        'ROLE_ADMIN'       => 'ROLE_USER',
        'ROLE_SUPER_ADMIN' => array(
            'ROLE_ADMIN',
            'ROLE_ALLOWED_TO_SWITCH',
        ),
    ),));
XML:<!-- app/config/security.xml --><?xml version="1.0" encoding="UTF-8"?><srv:container xmlns="http://Symfony.com/schema/dic/security"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xmlns:srv="http://Symfony.com/schema/dic/services"    xsi:schemaLocation="http://Symfony.com/schema/dic/services        http://Symfony.com/schema/dic/services/services-1.0.xsd">     <config>
        <!-- ... -->         <role id="ROLE_ADMIN">ROLE_USER</role>
        <role id="ROLE_SUPER_ADMIN">ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH</role>
    </config></srv:container>
YAML:# app/config/security.ymlsecurity:    # ...
    role_hierarchy:
        ROLE_ADMIN:       ROLE_USER
        ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]

在上面的配置中,用戶 ROLE_ADMIN 也將具備 ROLE_USER role。ROLE_SUPER_ADMIN role,同時擁有 ROLE_ADMINROLE_ALLOWED_TO_SWITCH 以及 ROLE_USER(繼承自 ROLE_ADMIN)。

總結 ?

喔~干得好!你已經了解到比security基礎更多的內容。最難的部分就是當你有自定義需求時:像是定義一個驗證策略(如,api tokens),復雜的授權邏輯以及許多其他事情(因為security本來就很復雜?。?。

幸運的是:在這里有很多的文章,意在述清各種狀況。同時,參考 Security參考。許多配置選項并無細節(jié),然而當看到完整的配置樹時,仍會有一定幫助。

祝好運!