如何保护你应用程序中的服务和方法
在security文档中,你可以看到,如何通过从服务容器中请求 security.authorization_checker 服务并检查当前用户的角色(role),来 保护一个控制器:
1 2 3 4 5 6 7 8 9  | 
// ...
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
 
public function helloAction($name)
{
    $this->denyAccessUnlessGrAnted('ROLE_ADMIN');
 
    // ...
} | 
你也可以通过把 security.authorization_checker 服务注入到 任意 服务来保护它。把依赖注入到服务的基本知识,参考 服务容器 一文。例如,假设你有一个 NewsletterManager 类,用于发送邮件,你希望将它的使用限制在那些拥有 ROLE_NEWSLETTER_ADMIN role的用户。在添加security之前,类看起来是这个样子:
1 2 3 4 5 6 7 8 9 10 11 12  | 
// src/AppBundle/Newsletter/NewsletterManager.PHP
namespace AppBundle\Newsletter;
 
class NewsletterManager
{
    public function sendNewsletter()
    {
        // ... where you actually do the work / 你在此处真正做事
    }
 
    // ...
} | 
你的目标,是在 sendNewsletter() 方法被调用时,去检查用户的role。迈出的第一步,是向该对象注入 security.authorization_checker 服务。若 不 进行安全检查,此举将会失去意义,这是构造器注入(constructor injection)的理想场合,它确保authorization checker对象在 NewLetterManager 类中可用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16  | 
// src/AppBundle/Newsletter/NewsletterManager.php
 
// ...
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
 
class NewsletterManager
{
    protected $authorizationChecker;
 
    public function __construct(AuthorizationCheckerInterface $authorizationChecker)
    {
        $this->authorizationChecker = $authorizationChecker;
    }
 
    // ...
} | 
接下来,在你的服务定义中注入服务:
1 2 3 4 5  | 
# app/config/services.yml
services:
    newsletter_manager:
        class:     AppBundle\Newsletter\NewsletterManager
        arguments: ['@security.authorization_checker'] | 
1 2 3 4 5 6 7 8 9 10 11 12 13  | 
<!-- app/config/services.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="Http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services
        http://symfony.com/schema/dic/services/services-1.0.xsd">
 
    <services>
        <service id="newsletter_manager" class="AppBundle\Newsletter\NewsletterManager">
            <argument type="service" id="security.authorization_checker"/>
        </service>
    </services>
</container> | 
1 2 3 4 5 6 7 8  | 
// app/config/services.php
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
 
$container->setDefinition('newsletter_manager', new Definition(
    'AppBundle\Newsletter\NewsletterManager',
    array(new Reference('security.authorization_checker'))
)); | 
当 sendNewsletter() 方法被调用时,被注入服务将被用于去执行安全检查(security check):
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  | 
namespace AppBundle\Newsletter;
 
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
// ...
 
class NewsletterManager
{
    protected $authorizationChecker;
 
    public function __construct(AuthorizationCheckerInterface $authorizationChecker)
    {
        $this->authorizationChecker = $authorizationChecker;
    }
 
    public function sendNewsletter()
    {
        if (false === $this->authorizationChecker->isGranted('ROLE_NEWSLETTER_ADMIN')) {
            throw new AccessDeniedException();
        }
 
        // ...
    }
 
    // ...
} | 
如果当前用户没有 ROLE_NEWSLETTER_ADMIN,他们会被提示登录。
使用Annotations来保护Methods ¶
你也可以使用可选的 JMSSecurityExtraBundle bundle,在任意服务中,使用annotation注释来保护method calls(类的方法调用)。这个bundle没有包含在Symfony标准版中,但你可以选择安装它。
要开启annotation功能,使用 security.secure_service tag来对你想要保护的服务来 打标签您想保护的服务(你还可以为所有服务自动开启此功能,见本文底部“+”区块):
1 2 3 4 5 6  | 
# app/config/services.yml
services:
    newsletter_manager:
        class: AppBundle\Newsletter\NewsletterManager
        tags:
            -  { name: security.secure_service } | 
1 2 3 4 5 6 7 8 9 10 11 12 13  | 
<!-- app/config/services.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services
        http://symfony.com/schema/dic/services/services-1.0.xsd">
 
    <services>
        <service id="newsletter_manager" class="AppBundle\Newsletter\NewsletterManager">
            <tag name="security.secure_service" />
        </service>
    </services>
</container> | 
1 2 3 4 5 6 7 8 9 10  | 
// app/config/services.php
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
 
$definition = new Definition(
    'AppBundle\Newsletter\NewsletterManager',
    // ...
));
$definition->addTag('security.secure_service');
$container->setDefinition('newsletter_manager', $definition); | 
然后你就可以使用annotation来实现相同的结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18  | 
namespace AppBundle\Newsletter;
 
use JMS\SecurityExtraBundle\Annotation\Secure;
// ...
 
class NewsletterManager
{
 
    /**
     * @Secure(roles="ROLE_NEWSLETTER_ADMIN")
     */
    public function sendNewsletter()
    {
        // ...
    }
 
    // ...
} | 
annotations 注释能工作,是因为为你的“要执行安全检查的类”创建了一个代理类(proxy class)。这意味着,你只能在 public 和 protected 方法中使用annotations ,而不能在 private 或标记成 final 的方法中使用它们。
JMSSecurityExtraBundle 还允许你保护方法的参数和返回值。参考 JMSSecurityExtraBundle 文档以了解更多。