0

我将 Symfony2 应用程序分成 2 个包:BackendBundle用于 API 和FrontendBundle用于 AngularJS“客户端”。一切都在防火墙下工作。

BackendBundle有实体,处理 API 路由;FrontendBundle具有 Angular 视图、路由等,并且只有一个带有通配符的控制器:

class AngularController extends Controller {
    /**
     * @Route("/{route}", name="angular_index_all_unmatched_routes", requirements={"route" = ".*"})
     * @Template("FrontendBundle::index.html.twig")
     */
    public function angularIndexAction($route) {
        return ['route' => $route];
    }
}

FrontendBundle路由被定义为 中的最后一个资源app/config/routing.yml,仅在任何其他路由不匹配时才被调用。多亏了这一点,如果直接访问它们(例如复制粘贴),它可以处理 Angular HTML5 模式的路由——而且它工作正常。

我想要做AngularController::angularIndexAction()的是定义防火墙和/或访问控制,以使匿名用户可以访问所有那些不匹配的路由(由 处理)。

为什么?我想打开一些 API 路由(通过前端代理)以供非用户访问(例如通过电子邮件发送的确认 URL,向用户发送一些消息)。

我不想为每个匿名“Angular”路由硬编码访问控制列表,我只想对 API 路由执行此操作。最后,那些不匹配的路由应该打开 Angular 的索引,它应该知道用户是否登录(用于显示完整或简化的布局)并且应该处理 Angular 路由并在请求失败时显示某种“访问被拒绝”消息(有 Symfony 监听器和 Angular 的$provide拦截器)。

有什么建议么?


编辑@Security注释AngularController::angularIndexAction()不起作用,它仍然重定向到防火墙入口点。


Edit2:这是片段security.yml

firewalls:
    unsecured:
        pattern: ^/(_(profiler|wdt)|css|images|js)/
        security: false
        anonymous: true

    secured:
        pattern: '^.*$'
        form_login:
            login_path: /our-provider/login
            check_path: /our-provider/callback/
        anonymous: true
        entry_point: our_provider.entry_point

access_control:
    - { path: '^/our-provider/(login(/[a-zA-Z]+)?|logout|redirect|callback)', roles: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: '^/', roles: ROLE_USER }

我知道{ path: '^/', roles: ROLE_USER }如果用户未登录,这会将所有路由重定向到登录页面。我认为这很明显并且没有提到它。我想要的是强制ROLE_USER匹配路由并让那些不匹配IS_AUTHENTICATED_ANONYMOUSLY的路由,而无需明确定义每个前端“代理路由”。在我的例子中,没有 404 Symfony 页面,因为一切都进行了angular_index_all_unmatched_routes路由,并且 Angular 路由定义决定是否有需要处理的东西。

4

1 回答 1

0

I haven't tried this, and I cannot begin to guess your existing security/route setup in security.yml but I guess you could whitelist the method with IS_AUTHENTICATED_ANONYMOUSLY. From the Symfony docs:

All users (even anonymous ones) have this - this is useful when whitelisting URLs to guarantee access - some details are in How Does the Security access_control Work?.

So, for example, if you were using the @Security annotation you could do something like (not tested):

class AngularController extends Controller {
    /**
     * @Route("/{route}", name="route", requirements={"route" = ".*"})
     * @Template("FrontendBundle::index.html.twig")
     * @Security("has_role('IS_AUTHENTICATED_ANONYMOUSLY')")
     */
    public function angularIndexAction($route) {
        return ['route' => $route];
    }
}

More on the @Security annotation here.

Hope this helps :)

Edit

All that said, when you define/restrict your routes under access_control in security.yml, the matching process stops on the first match. I assume that you have some role-restricted paths, which you should define explicitly - and put them first, so if they match the process stops.

Otherwise, you should be able to add a catch-all route, enforced by role IS_AUTHENTICATED_ANONYMOUSLY. Since the path definition of a route is a regex, something like ^/ should catch anything that is not explicitly defined. Just make sure and place it after your restricted route definitions.

You would not need for the @Security annotation in this case.

Edit 2

I tried mocking this out using a clean instance and HTTP BasicAuth but what I was trying to achieve was the following, which I understand as similar to your use case:

  • Create a backend controller with routes / and /api/ and trigger a HTTP BasicAuth authentication popup
  • Create a frontend controller with route /{route} that would match everything else and authenticate anonymously.

My firewall and access_control configuration looks like this:

security:
    encoders:
        # encoder config here
    providers:
        # provider config here
firewalls:
    dev:
        pattern:  ^/(_(profiler|wdt)|css|images|js)/
        security: false
    secured:
        anonymous: ~
        http_basic: ~

access_control:
    - { path: ^/$,    roles: ROLE_USER }
    - { path: ^/api/, roles: ROLE_USER }
    - { path: ^/,     roles: IS_AUTHENTICATED_ANONYMOUSLY }

Access control paths are regexes, so ^/$ and ^/ are not the same. The former will only match exactly to route /. The latter will match any route that begins with /; e.g: /home, /products, /contact etc.

Indeed, the latter will match and anonymously authenticate /api, but it will not match /api/, or /api/1 etc. as these are explicitly defined and restricted to ROLE_USER.

So the general idea is to explicitly and (if possible) exactly match the routes you want to restrict, and declare those first. The last declaration ^/ should openly catch any other route that falls through.

于 2015-03-13T14:25:27.450 回答