授权(Authorization)

3.4 版本
维护中的版本

当任何authentication provider(参考 authentication provider)在对一个“尚待验证”的token验证完毕之后,会返回一个authenticated token(已验证token)。authentication listener将通过它的 setToken() 方法直接在 TokenStorageInterface 中进行设置。

从那时起,这个用户就是被验证过的了,也即确认(identified)。现在,程序的其他部分可以使用这个token来决定用户是否可以请求特定的URL或者修改特定的对象。这种决定,是由一个 AccessDecisionManagerInterface 实例完成。

一个authorization decision(授权决定)将始终基于如下几点:

  • 当前token

    举例来说,token中的 getRoles() 方法会被用于取出当前user的roles(如 ROLE_SUPER_ADMIN)),又或,基于token类才能做出一个decision。

  • 一组属性

    每个属性对应着用户所能拥有的特定权利,比如 ROLE_ADMIN 是为了确保用户是一个管理员。

  • 一个对象(可选)

    access control(访问控制)所需要检查任何对象。比如,一个article对象或一个comment对象。

Access Decision Manager 

由于决定一个用户是否拥有授权来执行某个动作可能是个相当复杂的过程,标准的 AccessDecisionManager ,本身拥有若干个voter,最终的裁决要依靠它收到的全部voter才能给出(不管这些voter给出的是正是反还是中立)。Access Decision Manager承认以下三种策略:

affirmative (默认)
只要有一个voter同意授权,则授权立即通过;
consensus
多数voter同意授权时(允许访问的数量>拒绝访问的),即通过授权;
unanimous
只有全部voter都不反对授权时,授权才能通过;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
use Symfony\Component\Security\Core\Authorization\AccessDecisionManager;
 
// instances of Symfony\Component\Security\Core\Authorization\Voter\VoterInterface
// VoterInterface实例的数组
$voters = array(...);
 
// one of "affirmative", "consensus", "unanimous"
// "affirmative", "consensus", "unanimous"中的一个
$strategy = ...;
 
// whether or not to grant access when all voters abstain
// 全部voter弃权时,是否给予授权?
$allowIfAllAbstainDecisions = ...;
 
// whether or not to grant access when there is no majority (applies only to the "consensus" strategy)
// 没有形成多数时,是否给予授权?(仅适合于consensus策略)
$allowIfEqualGrantedDeniedDecisions = ...;
 
$accessDecisionManager = new AccessDecisionManager(
    $voters,
    $strategy,
    $allowIfAllAbstainDecisions,
    $allowIfEqualGrantedDeniedDecisions

你可以在配置文件中修改默认的Decision Manager策略。

Voters 

Voters是 VoterInterface 的实例,意味着它们必须要实现一些方法以便让decision manager来使用它们:

vote(TokenInterface $token, $object, array $attributes)
这个方法负责真正的表决,返回一个 VoterInterface 接口定义的常量,可以是下面中的一个:VoterInterface::ACCESS_GRANTED, VoterInterface::ACCESS_DENIEDVoterInterface::ACCESS_ABSTAIN;

安全组件中包含了下面这些标准的voters,可以在多种场合使用:

AuthenticatedVoter 

AuthenticatedVoter 支持的属性包括 IS_AUTHENTICATED_FULLYIS_AUTHENTICATED_REMEMBERDIS_AUTHENTICATED_ANONYMOUSLY ,并且基于当前验证的级别来进行授权,比如,当前用户是完全验证过的,还是基于一个“remember-me” cookie验证的,或者只是个匿名用户?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolver;
 
$anonymousClass = 'Symfony\Component\Security\Core\Authentication\Token\AnonymousToken';
$rememberMeClass = 'Symfony\Component\Security\Core\Authentication\Token\RememberMeToken';
 
$trustResolver = new AuthenticationTrustResolver($anonymousClass, $rememberMeClass);
 
$authenticatedVoter = new AuthenticatedVoter($trustResolver);
 
// instance of Symfony\Component\Security\Core\Authentication\Token\TokenInterface
// TokenInterface实例
$token = ...;
 
// any object 任何对象
$object = ...;
 
$vote = $authenticatedVoter->vote($token, $object, array('IS_AUTHENTICATED_FULLY'));

RoleVoter 

RoleVoter 支持以 ROLE_ 开头的属性,当所需之 ROLE_* 属性可以在角色数组中找到并被 token的 getRoles()方法返回时,该用户即被授权:

1
2
3
4
5
use Symfony\Component\Security\Core\Authorization\Voter\RoleVoter;
 
$roleVoter = new RoleVoter('ROLE_');
 
$roleVoter->vote($token, $object, array('ROLE_ADMIN'));

RoleHierarchyVoter 

RoleHierarchyVoter 继承了 RoleVoter 并且提供了附加功能:它知道如可处置“角色架构”(hierarchy of roles)。例如,ROLE_SUPER_ADMIN 角色可以拥有 ROLE_ADMINROLE_USER 子角色,因此当某个特定对象需要 ROLE_ADMIN 角色时,RoleHierarchyVoter会授权给那些真正拥有 ROLE_ADMIN 角色的用户,但也会授权给拥有 ROLE_SUPER_ADMIN 角色的用户:

1
2
3
4
5
6
7
8
9
10
use Symfony\Component\Security\Core\Authorization\Voter\RoleHierarchyVoter;
use Symfony\Component\Security\Core\Role\RoleHierarchy;
 
$hierarchy = array(
    'ROLE_SUPER_ADMIN' => array('ROLE_ADMIN', 'ROLE_USER'),
);
 
$roleHierarchy = new RoleHierarchy($hierarchy);
 
$roleHierarchyVoter = new RoleHierarchyVoter($roleHierarchy);

当你创建自己的voter时,当然可以向构造器中注入任何依赖,以便voter在做出决定时使用。

Roles 

角色(Roles)是对象,对用户应有的权利给出一个表达式。唯一的要求是Roles必须实现 RoleInterface 接口,因此它们都有一个 getRole()方法,用于返回角色本身的字符串。对于默认的 Role 对象,(该方法)直接返回它的第一个构造器参数:

1
2
3
4
5
6
use Symfony\Component\Security\Core\Role\Role;
 
$role = new Role('ROLE_ADMIN');
 
// will show 'ROLE_ADMIN' / 将会显示'ROLE_ADMIN'
var_dump($role->getRole());

多数authentication token(验证token)继承的是 AbstractToken ,这意味着传入构造器的角色字符串,将被自动转换成这些简单的 Role 对象。

使用Decision Manager 

Access Listener 

access decision manager(授权管理器)可以被用于请求到来后的任何阶段,来决定当前用户是否有资格访问给定的资源。有一个可选项,但却很有用,这就是基于URL匹配用来限制访问的 AccessListener ,它是防火墙的监听之一(参考Firewall Listeners,被匹配了防火墙映射(firewall map)的每一次请求所触发(参考 HTTP请求的防火墙)。

Access Listener,使用了一个访问映射(它是 AccessMapInterface,里面有个“请求匹配器”(request matcher)和与之对应的一组属性,这些属性在当前用户访问程序时要用到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
use Symfony\Component\Security\Http\AccessMap;
use Symfony\Component\HttpFoundation\RequestMatcher;
use Symfony\Component\Security\Http\Firewall\AccessListener;
 
$accessMap = new AccessMap();
$requestMatcher = new RequestMatcher('^/admin');
$accessMap->add($requestMatcher, array('ROLE_ADMIN'));
 
$accessListener = new AccessListener(
    $securityContext,
    $accessDecisionManager,
    $accessMap,
    $authenticationManager
);

Authorization Checker 

通过 AuthorizationCheckerisGranted方法,授权管理器(access decision manager)可以被程序的其他部分所用。调用这个方法时,将直接把问题委托给授权管理器(来裁决)。

1
2
3
4
5
6
7
8
9
10
11
12
use Symfony\Component\Security\Core\Authorization\AuthorizationChecker;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
 
$authorizationChecker = new AuthorizationChecker(
    $tokenStorage,
    $authenticationManager,
    $accessDecisionManager
);
 
if (!$authorizationChecker->isGranted('ROLE_ADMIN')) {
    throw new AccessDeniedException();
}

本文,包括例程代码在内,采用的是 Creative Commons BY-SA 3.0 创作共用授权。

登录symfonychina 发表评论或留下问题(我们会尽量回复)