crocos/security-bundle

此包已被弃用且不再维护。未建议替代包。

此包提供了一种通过注解配置安全性的方法

安装数: 59,311

依赖者: 0

建议者: 0

安全: 0

星标: 26

关注者: 17

分支: 4

开放问题: 0

类型:symfony-bundle

2.1.0 2015-01-06 21:02 UTC

README

构建状态

  • master: Build Status
  • develop: Build Status

概要

CrocosSecurityBundle 是一个用于Symfony的Bundle,旨在更简单地管理认证状态,是为了替换复杂的 SecurityBundle 而开发的。SecurityBundle 与之相比,有以下不同。

  • 仅使用注解进行设置
  • 登录、注销状态的切换由开发者明确执行

Symfony 2.0 用户

请使用 CrocosSecurityBundle1.* 版本。

安装方法

使用Composer安装

composer.json 中添加 crocos/security-bundle

{
    "require": {
        "crocos/security-bundle": "dev-master"
    }
}

app/AppKernel.php

注册 CrocosSecurityBundle

public function registerBundles()
{
    $bundles = array(
        // ...
        new Crocos\SecurityBundle\CrocosSecurityBundle(),

    );
}

删除 Symfony\Bundle\SecurityBundle\SecurityBundle 的行。

app/config/config.yml

删除读取 security.yml 的行。

介绍

在控制器的方法或类上设置 Secure 注解和 SecureConfig 注解。

<?php

use Crocos\SecurityBundle\Annotation\Secure;
use Crocos\SecurityBundle\Annotation\SecureConfig;

/**
 * @SecureConfig(forward="CrocosAppBundle:Security:login")
 */
abstract class AppController
{
}

class SampleController extends AppController
{
    /**
     * @Secure
     */
    public function securedAction()
    {
        $user = $this->get('crocos_security.context')->getUser();
    }
}

/**
 * @Secure
 */
class SecurityController extends AppController
{
    public function login(Request $request)
    {
        $user = $this->findUser($request->request->get('username'), $request->request->get('password'));

        $this->get('crocos_security.context')->login($user);

        return $this->redirect('/');
    }

    public function logout()
    {
        $this->get('crocos_security.context')->logout();

        return $this->redirect('/login');
    }
}

注解

Secure 注解

添加了 Secure 注解的控制器需要认证。类上设置则应用于所有操作,方法上设置则仅针对指定的操作。

Secure 注解可设置以下属性。

disabled

  • 类型: boolean
  • 默认: false

设置为true表示无需认证。默认值是false,所以不传参数时 Secure 注解表示需要认证。

allow

  • 类型: array
  • 默认: []

以数组形式设置所需的权限。

SecureConfig 注解

SecureConfig 注解用于进行认证相关设置。Secure 注解类似,可以设置在控制器的类或方法上。

SecureConfig 注解可设置以下属性。

domain

  • 类型: string
  • 默认: "secured"

当需要在同一项目中执行不同的认证处理时(例如,用户专用页面、管理员专用页面等),可以指定认证状态应适用的区域。

默认情况下使用会话来保持认证状态,但domain用作会话的命名空间。

auth

  • 类型: string
  • 默认: "session"

指定认证状态的管理方法。默认值是 "session",使用会话来管理认证状态。也可以设置自定义的管理方法。

转发

  • 类型: string

在未登录状态下访问需要认证的控制器时,将调用此处指定的控制器。可以指定控制器的名称(类::方法),或者使用Symfony的缩写形式(包名:控制器名:动作名)。如果没有指定forward,则访问需要认证的控制器时会报错。

为了防止无限循环,即使认证是必需的,对通过forward指定的控制器的访问也不会进行控制。

基本认证

  • 类型: string|array

启用BASIC认证。值可以是“用户名:密码”格式的字符串,也可以是包含该字符串的数组(= 多个用户)。

@SecureConfig(domain="secured", basic="user:pass")

认证域(realm)将基于domain的值设置。"secured"的情况下,将设置为“Secured Area”。

注释的读取

Secure 注释设置在类上时,将应用于所有动作。会读取父类的值,后读取的值将覆盖前面的值。

  1. 父类
  2. 子类
  3. 方法

如果没有指定disabled属性,则会被覆盖为需要认证,但除非指定其他属性,否则不会被覆盖。只有当方法注释在读取时未指定时,才会设置默认值。

roleManager

  • 类型: string
  • 默认: "session"

指定权限的管理方法。默认值是"session",使用会话来管理认证状态。

指定"in_memory"则权限只会在设置权限的进程内保持,进程结束时将被销毁。

httpsRequired

  • 类型: boolean
  • 默认: false

如果指定了true,则在http下访问指定的控制器时,将强制重定向到https

@SecureConfig(httpsRequired=true)

此外,在未设置SSL通信设置的开发服务器等环境中,可以在app/config/config_dev.yml等文件中设置https_requiring: false来禁用强制重定向到https。 (默认情况下是启用的)

crocos_security:
  https_requiring: false

示例代码

以下代码是使用注释进行认证的示例。

当使用CrocosSecurityBundle时,推荐为每个应用程序创建一个共享的控制器类,以便于设置。

基本示例

AppController
├── AccountController
│   ├── indexAction()
│   └── loginAction()
└── ProductController
    ├── buyAction()
    └── showAction()

定义了继承自AppControllerProductControllerAccountController。由于ProductControllerbuyAction指定了Secure注释,因此需要认证。由于AccountController类指定了Secure注释,因此所有动作都需要认证。但是,由于AppControllerSecureConfig注释将loginAction指定为转发,因此loginAction始终不需要认证。

<?php

namespace Crocos\AppBundle\Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Crocos\SecurityBundle\Annotation\Secure;
use Crocos\SecurityBundle\Annotation\SecureConfig;

/**
 * @SecureConfig(forward="CrocosAppBundle:Accont:login")
 */
abstract class AppController extends Controller
{
    protected function getUser()
    {
        return $this->get('crocos_security.context')->getUser();
    }
}

/**
 * @Route("/product")
 */
class ProductController extends AppController
{
    /**
     * @Route("/{id}", requirements={"id" = "\d+"})
     */
    public function showAction($id)
    {
        // ...
    }

    /**
     * @Secure
     * @Route("/{id}/buy", requirements={"id" = "\d+"})
     */
    public function buyAction($id)
    {
        // ...
    }
}

/**
 * @Secure
 */
class AccountController extends AppController
{
    /**
     * @Route("/account")
     */
    public function indexAction()
    {
        // ...
    }

    /**
     * @Route("/login")
     * @Template
     */
    public function loginAction(Request $request)
    {
        if ($request->getMethod() === 'POST') {
            $username = $request->request->get('username');
            $password = $request->request->get('password');

            $user = $this->get('doctrine')->getRepository('CrocosAppBundle:User')
                ->findUser($username, $password);

            $this->get('crocos_security.context')->login($user);

            return $this->redirect('/');
        }

        return array();
    }
}

面向管理员页面的示例

创建管理员控制器时,可以这样指定domain属性,以便与另一个认证域分开。AppController指定了Secure注释,因此所有控制器都需要认证。

<?php

namespace Crocos\AppBundle\Controller\Admin;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Crocos\SecurityBundle\Annotation\Secure;
use Crocos\SecurityBundle\Annotation\SecureConfig;

/**
 * @Secure
 * @SecureConfig(domain="admin", forward="CrocosAppBundle:Admin\Accont:login")
 */
abstract class AppController extends Controller
{
    protected function getUser()
    {
        return $this->get('crocos_security.context')->getUser();
    }
}

/**
 * @Route("/admin")
 */
class AccountController extends AppController
{
    /**
     * @Route("/login")
     */
    public function loginAction(Request $request)
    {
        // ...
    }
}

设置Basic认证

要设置Basic认证,请将SecureConfig注释中的basic属性指定。在此示例中,用户名为"admin",密码为"password"

<?php

namespace Crocos\AppBundle\Controller\Admin;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Crocos\SecurityBundle\Annotation\Secure;
use Crocos\SecurityBundle\Annotation\SecureConfig;

/**
 * @SecureConfig(domain="admin", basic="admin:password")
 */
abstract class AppController extends Controller
{
}

此外,Basic认证的设置与Secure注释的设置无关。如果设置了basic属性,则认证域内的所有动作都将执行Basic认证。如果想要部分禁用Basic认证,可以将basic属性的值设置为false。当然,也可以通过设置如authforward属性等方式在PHP侧进行认证设置。

此外,也可以用参数形式指定用户名/密码。

@SecureConfig(domain="admin", basic="%app.basic_auth%")

app/config/parameters.yml:

parameters:
    app.basic_auth: admin:password

SecurityContext

与认证相关的状态由服务容器中注册的键为crocos_security.contextCrocos\SecurityBundle\Security\SecurityContext对象保持。有关实际处理内容的说明,请参阅下面的Auth Logic

登录

要执行登录,请将用户信息传递给login()方法。

$user = $userRepository->findUser('Katsuhiro Ogawa');
$this->get('crocos_security.context')->login($user);

登录状态的确认

可以使用isAuthenticated()方法来确认登录状态。

if ($this->get('crocos_security.context')->isAuthenticated()) {
    echo 'ログインしています';
}

获取登录用户

可以使用getUser()方法来获取登录用户。如果未登录,则返回null。

$user = $this->get('crocos_security.context')->getUser();

注销

可以通过logout()方法进行注销。

$this->get('crocos_security.context')->logout();

$this->get('crocos_security.context')->getUser();           // => null
$this->get('crocos_security.context')->isAuthenticated();   // => false

Auth Logic

Auth Logic 是切换认证状态管理方法的一种机制。Secure 注释的 auth 与之对应。标准情况下,使用会话来管理认证状态的是 SessionAuth (auth="session"),还有考虑到在会话中存储实体的情况的 SessionEntityAuth。还可以扩展现有的 Auth Logic 或创建自定义的 Auth Logic。

SessionAuth

@SecureConfig(auth="session")

SessionAuth 是使用会话来管理认证状态的机制。

SessionEntityAuth

@SecureConfig(auth="session.entity")

SessionEntityAuth 是为登录用户使用实体而设计的,基本上与 SessionAuth 相同。SessionAuth 使用时,登录用户信息会被序列化并存储在会话中。如果用户信息是对象,则对象会被序列化并保存。SessionEntityAuth 使用时,只会将类名和ID存储到会话中,每次访问时都会从存储库中获取实体。

在使用 SessionEntityAuth 时,登录目标实体必须实现 getId() 方法。这个值在从会话恢复实体时被传递给存储库的 find() 方法。此外,如果需要验证登录中用户的有效性,可以通过在实体中实现 isEnabled() 方法来在获取实体后进行有效性验证。如果 isEnabled()false,即使已登录也会被注销。

自定义Auth Logic

要创建自定义 Auth Logic,首先需要创建一个实现了 Crocos\SecurityBundle\Security\AuthLogic\AuthLogicInterface 接口的类。Auth Logic 需要定义以下 5 个方法。

  • setDomain($domain)
  • login($user)
  • logout()
  • isAuthenticated()
  • getUser()

除了 setDomain() 方法外,其他方法都是通过委派的形式从 SecurityContext 类中调用的。setDomain() 方法将使用注解读取的 domain 值传递。

自定义Auth Logic的注册

创建 Auth Logic 后,需要将其注册到 DI 容器中。此时,通过附加 crocos_security.auth_logic 标签,可以将其注册到 CrocosSecurityBundle 中。在注解中指定在 alias 中记录的值。

services:
    myapp.security.my_auth:
        class: Crocos\AppBundle\Security\MyAuth
        tags:
            - { name: crocos_security.auth_logic, alias: my_auth }

调用上述 Auth Logic 的方式如下。

/**
 * @SecureConfig(auth="my_auth")
 */
class AppController
{
}

AuthException

如果需要在任意位置转到登录页面,则抛出 Crocos\SecurityBundle\Exception\AuthException 对象。另外,可以在 AuthException 构造函数的第二个参数中指定 attributes 数组,并在转到登录页面时将其作为路由参数传递。

<?php

namespace Crocos\AppBundle\Controller;

use Crocos\SecurityBundle\Annotation\Secure;
use Crocos\SecurityBundle\Annotation\SecureConfig;
use Crocos\SecurityBundle\Exception\AuthException;

/**
 * @SecureConfig(forward="CrocosAppBundle:Demo:login")
 */
class AppController
{
}

class DemoController extends AppController
{
    /**
     * @Secure
     */
    public function someAction($id)
    {
        if ($this->hasSomeError()) {
            throw new AuthException('Login required', array('id' => $id));
        }
    }

    public function loginAction($id = null)
    {
        // do login
    }
}

Twig集成

读取 CrocosSecurityBundle 后,在 Twig 模板内会启用 _security 变量。_security 变量持有 SecurityContext 对象的引用。可以使用它在模板内进行条件分支等操作。

{% if _security.isAuthenticated %}
  <p>Logged in as {{ _security.user }}</p>
{% endif %}