防火墙和授权

3.4 版本
维护中的版本

Security组件的核心是授权(authorization)。授权是通过AuthorizationCheckerInterface的实例完成的。当验证用户的步骤全部被走完之后,你可以询问authorization checker“一个已验证用户是否可以访问程序的特定action或特定资源”:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
use Symfony\Component\Security\Core\Authorization\AuthorizationChecker;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
 
// instance of Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface
// TokenStorageInterface实例
$tokenStorage = ...;
 
// instance of Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface
// AuthenticationManagerInterface实例
$authenticationManager = ...;
 
// instance of Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface
// AccessDecisionManagerInterface实例
$accessDecisionManager = ...;
 
$authorizationChecker = new AuthorizationChecker(
    $tokenStorage,
    $authenticationManager,
    $accessDecisionManager
);
 
// ... authenticate the user 验证用户
 
if (!$authorizationChecker->isGranted('ROLE_ADMIN')) {
    throw new AccessDeniedException();
}

参阅验证授权独立章节以了解更多。

HTTP请求的防火墙 

验证用户的过程是由防火墙完成的。一套程序可能会有多个受保护区域(secured areas),所以,防火墙(firewall)的配置就是要使用这些受保护内容的“映射关系”(map)。对于每个区域的内容,映射关系中包含了一个请求匹配器(request matcher),以及一组监听器(listeners)。请求匹配器赋予了防火墙确定“当前请求是否与受保护区域匹配”的能力。如果这个请求能用于验证用户,监听器会被请求(译注:即当前请求满足某个secured area的话,即调用相应listeners):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
use Symfony\Component\Security\Http\FirewallMap;
use Symfony\Component\HttpFoundation\RequestMatcher;
use Symfony\Component\Security\Http\Firewall\ExceptionListener;
 
$map = new FirewallMap();
 
$requestMatcher = new RequestMatcher('^/secured-area/');
 
// instances of Symfony\Component\Security\Http\Firewall\ListenerInterface
// ListenerInterface实例组成的数组
$listeners = array(...);
 
$exceptionListener = new ExceptionListener(...);
 
$map->add($requestMatcher, $listeners, $exceptionListener);

防火墙映射(firewall map)将作为防火墙的第一个参数,同时还传入了会被HttpKernel用到的事件派遣器(event dispatcher):

1
2
3
4
5
6
7
8
9
10
11
12
13
use Symfony\Component\Security\Http\Firewall;
use Symfony\Component\HttpKernel\KernelEvents;
 
// the EventDispatcher used by the HttpKernel
// 派遣器是给HttpKernel用的
$dispatcher = ...;
 
$firewall = new Firewall($map, $dispatcher);
 
$dispatcher->addListener(
    KernelEvents::REQUEST,
    array($firewall, 'onKernelRequest')
);

防火墙注册的是kernel.request事件的监听,该事件被HttpKernel在处理每个请求的先期阶段被派遣。这样一来,防火墙就能避免用户在被允许之前继续前行。

Firewall监听 

当防火墙收到kernel.request事件的通知时,它要向映射关系询问当前请求是否匹配“受保护区域”中的一个(译注:即请求的URL是否在firewall的正则范围之内)。最先与请求匹配的secured area将返回一组相应的防火墙监听(firewall listeners,实现的都是ListenerInterface接口)。这些监听将被要求处理当前的请求。处理过程可以概括为:找到当前请求中所包含的任何信息,通过这些信息,用户有可能被验证通过(比如,Basic HTTP authentication的监听会检查当前的请求头中是否有PHP_AUTH_USER)。

Exception监听 

当任何一个监听抛出一个AuthenticationException异常时,异常监听器将会介入,它是在“添加[受保护区域]到防火墙映射关系”时被提供的。

异常监听基于它被创建时所提供的参数,可以决定下一步做什么。它可能会启动验证进程,也许会要求用户再一次提供凭证(当用户仅凭“remember-me” cookie被验证时),或者把异常转换成一个AccessDeniedException,最终生成一个“HTTP/1.1 403:Access Denied”响应。

入口点 

当用户从未被验证时(即,token storage还没有token时),防火墙的入口点(entry point)将执行被称为“开启(start)”验证的进程。一个入口点需要实现AuthenticationEntryPointInterface接口,它只有一个方法:start()。这个方法接收当前的 Request对象和能够触发“异常监听”的异常。该方法返回一个Response对象。它可以是,比如,包含登录表单的页面,或者是Basic HTTP authentication的场合,一个带有WWW-Authenticate头的响应,它可以让用户在弹出窗口输入用户名和密码。

流程:防火墙,验证,授权 

希望你能了解一下security context的流程是如何工作的:

  1. 防火墙对kernel.request事件注册监听;

  2. 在request的最开始,防火墙要检查firewall map,以找出是否有任何一个firewall应该被这个URL激活;

  3. 如果通过URL在映射中找到了某个防火墙,它的监听会收到通知;

  4. 每个监听都要检查,看是否当前请求中,包含了任何关乎验证的信息——监听器可以: (a)验证一个用户, (b)抛出一个AuthenticationException异常, (c)什么也不做(因为当前请求中并没有验证相关信息);

  5. 一旦用户被验证,你可以使用授权来禁止其访问特定资源。

参阅后面章节以了解验证授权

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

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