在復(fù)雜的申請(qǐng)中,你可能經(jīng)常面臨訪問(wèn)權(quán)限決策不僅僅取決于請(qǐng)求者本人(Token)還牽涉到了被申請(qǐng)的對(duì)象的問(wèn)題。這也是 ACLs 系統(tǒng)被制作出來(lái)的原因所在。
使用 ACL 并不是細(xì)微之事,它的的確確對(duì)使用有簡(jiǎn)化功能。當(dāng)然,它可能會(huì)濫殺無(wú)辜。如果你的判定邏輯只是被簡(jiǎn)單的代碼來(lái)描述(比如檢查這個(gè)博客是否被一個(gè)現(xiàn)在的使用者所擁有> ),那么就考慮使用 voters。一個(gè) voter 可以通過(guò)表決來(lái)傳遞對(duì)象,通過(guò)這些,你就可以做出復(fù)雜的決定和更高效地執(zhí)行你的 ACL。此外,強(qiáng)制批準(zhǔn)(比如 isGranted 部分)就會(huì)看起來(lái)和你所看到的這個(gè)條目極其相似,但是你的 voter 類(lèi)就會(huì)在幕后控制判定邏輯了,而不是 ACL 系統(tǒng)。
想象你在設(shè)計(jì)一個(gè)博客系統(tǒng),而你的使用者可以評(píng)論你的工作?,F(xiàn)在,如果你希望一個(gè)使用者能夠修改編輯他們自己的評(píng)論,但并不是所有的用戶(hù);此時(shí),你可以修改所有的評(píng)論。在這種情況下,comment 就會(huì)處理域名對(duì)象,同時(shí)你會(huì)獲得權(quán)限。你可以采用多種方式來(lái)完成這個(gè) Symfony,兩個(gè)基本的方式如下:
Enforce security in your business methods:基本上講,這個(gè)方法意味著在每一個(gè) Comment 之間制作一個(gè)參照,比較這些使用者提供的令牌,就可以做出決策。
每種方法都非常有效。然而,它們結(jié)合了你的授權(quán)邏輯來(lái)負(fù)責(zé)你的商業(yè)代碼,會(huì)限制你的代碼的可通用型,因此這就增加了單元調(diào)試難度。此外,你可能會(huì)撞上一些問(wèn)題,如果用戶(hù)只有一個(gè)簡(jiǎn)單的域名對(duì)象的話。
幸運(yùn)的是,這里有一種更好的方式,你在下面就可以看到。
現(xiàn)在,在你能夠采取行動(dòng)之前,你需要做一些引導(dǎo)指令。首先,你需要安裝你要實(shí)用的 ACL 系統(tǒng)的連接。
YAML:
# app/config/security.yml
security:
acl:
connection: default
XML:
<!-- app/config/security.xml -->
<acl>
<connection>default</connection>
</acl>
PHP:
// app/config/security.php
$container->loadFromExtension('security', 'acl', array(
'connection' => 'default',
));
ACL 體系要求一種連接關(guān)系,這種連接關(guān)系可以由 DBAL 來(lái)提供,也可以由 MongoDB(使用 MongoDBAclBundle)來(lái)提供。然而,那并不意味著你必須用 DoctrineORM 或者 ODM 來(lái)組織你的域?qū)ο?。你可以用任意組織對(duì)象的方法和手段,比如 DoctrineORM,MongoDB ODM,Propel,rawSQL 等等。選擇權(quán)在你手里。
在連接方式確定好之后,你就需要來(lái)輸入基礎(chǔ)的數(shù)據(jù)結(jié)構(gòu)了。幸運(yùn)的是,有一項(xiàng)任務(wù)專(zhuān)門(mén)處理這種情況,只需要運(yùn)行一下面的指令就可以了:
$ php app/console init:acl
回到最開(kāi)始的小例子上去,現(xiàn)在你可以對(duì)它運(yùn)用 ACL 技術(shù)了。
一旦 ACL 被建立起來(lái),你就可以通過(guò)建立一個(gè) Access Control Entry,來(lái)向你的用戶(hù)提供信息獲取通道。當(dāng)然同時(shí)就可以穩(wěn)固你的使用者和你的工作實(shí)體之間的聯(lián)系了。
// src/AppBundle/Controller/BlogController.php
namespace AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Acl\Domain\ObjectIdentity;
use Symfony\Component\Security\Acl\Domain\UserSecurityIdentity;
use Symfony\Component\Security\Acl\Permission\MaskBuilder;
class BlogController extends Controller
{
// ...
public function addCommentAction(Post $post)
{
$comment = new Comment();
// ... setup $form, and submit data
if ($form->isValid()) {
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($comment);
$entityManager->flush();
// creating the ACL
$aclProvider = $this->get('security.acl.provider');
$objectIdentity = ObjectIdentity::fromDomainObject($comment);
$acl = $aclProvider->createAcl($objectIdentity);
// retrieving the security identity of the currently logged-in user
$tokenStorage = $this->get('security.token_storage');
$user = $tokenStorage->getToken()->getUser();
$securityIdentity = UserSecurityIdentity::fromAccount($user);
// grant owner access
$acl->insertObjectAce($securityIdentity, MaskBuilder::MASK_OWNER);
$aclProvider->updateAcl($acl);
}
}
}
在上面的代碼段中有一些重要的實(shí)施策略。現(xiàn)在,我僅僅希望能強(qiáng)調(diào)兩點(diǎn):
首先,你可能注意到 ->createAcl() 不直接接受域?qū)ο?,而只接?ObjectIdentityInterface 的啟用。當(dāng)你手里沒(méi)有實(shí)際的區(qū)域?qū)ο髮?shí)例的時(shí)候,這個(gè)附加步驟間接地允許你能夠和 ACLs 交互。這在你想要檢查一大批對(duì)象的權(quán)限的時(shí)候,將會(huì)起到很大的作用。
另一個(gè)有趣的部分在于 ->insertObjectAce() 的調(diào)用。在例子里,我們可以授予直接聯(lián)機(jī)的用戶(hù)以修改評(píng)論的權(quán)限。而 MaskBuilder::MASK_OWNER 是一個(gè)提前定義的整型位掩碼;不必?fù)?dān)心掩碼生成器會(huì)抽象大部分細(xì)節(jié),但是這種技術(shù)會(huì)幫助你從數(shù)據(jù)庫(kù)里得到一系列不同的權(quán)限數(shù)據(jù),從而讓你的表現(xiàn)能夠顯得更漂亮一些。
ACEs 的檢查順序是很有意義的,作為一項(xiàng)通用的準(zhǔn)則,你應(yīng)該在最開(kāi)始設(shè)立更多的入口。
// src/AppBundle/Controller/BlogController.php
// ...
class BlogController
{
// ...
public function editCommentAction(Comment $comment)
{
$authorizationChecker = $this->get('security.authorization_checker');
// check for edit access
if (false === $authorizationChecker->isGranted('EDIT', $comment)) {
throw new AccessDeniedException();
}
// ... retrieve actual comment object, and do your editing here
}
}
在這個(gè)例子里,你可以檢測(cè)用戶(hù)是否有編輯權(quán)限。在 Symfony 內(nèi)部,Symfony 會(huì)分配權(quán)限給幾個(gè)整型的位掩碼,然后檢測(cè)用戶(hù)是否擁有這些碼。
你可以建立起 32 位的權(quán)限碼(取決于你的 OS,PHP 的碼位可以從 30 到 32 不等)。此外,你也可以定義累加權(quán)限。
在上面的第一個(gè)例子中,你只能保證用戶(hù)得到 owner 的基本權(quán)限。盡管這樣可以有效地管理用戶(hù)的基礎(chǔ)操作權(quán)限,比如可視,編輯等等。但是有時(shí)候我們希望用戶(hù)得到的權(quán)限更加明確清晰。
通過(guò)結(jié)合幾個(gè)基本權(quán)限,MaskBuilder 能夠被用于建立位掩碼。
$builder = new MaskBuilder();
$builder
->add('view')
->add('edit')
->add('delete')
->add('undelete')
;
$mask = $builder->get(); // int(29)
這個(gè)整型位掩碼能夠被用于保證用戶(hù)權(quán)限添加的成功。
$identity = new UserSecurityIdentity('johannes', 'Acme\UserBundle\Entity\User');
$acl->insertObjectAce($identity, $mask);
現(xiàn)在用戶(hù)可以使用可視,編輯,刪除以及取消刪除對(duì)象了。