FOSUserBundle-如何在尝试访问login_path时重定向已登录的用户


FOSUserBundle - How to redirect already logged-in users when trying to access the login_path

是否可以为特定路由/login执行自动重定向到某个路由(即/),仅针对属于AUTHENTICATED的用户?以及如何?

我正在使用FOSUserBundle。

这是我的安全配置:

security:
    encoders:
        FOS'UserBundle'Model'UserInterface: sha512
role_hierarchy:
    ROLE_ADMIN:       ROLE_USER
    ROLE_SUPER_ADMIN: ROLE_ADMIN
providers:
    fos_userbundle:
        id: fos_user.user_provider.username_email
firewalls:
    main:
        pattern: ^/
        form_login:
            provider: fos_userbundle
            csrf_provider: form.csrf_provider
            login_path: /accedi
            check_path: /login_check
            default_target_path: /
        oauth:
            resource_owners:
                facebook:           "/login/check-facebook"
                google:             "/login/check-google"
            login_path:        /accedi
            failure_path:      /accedi
            default_target_path: /
            oauth_user_provider:
                service: my_user_provider
        logout:
            path: /logout
            target: /
            invalidate_session: false
        anonymous:  ~
    login:
        pattern:  ^/login$
        security: false
        remember_me:
            key: "%secret%"
            lifetime: 31536000 # 365 days in seconds
            path: /
            domain: ~ 
    oauth_authorize:
        pattern:    ^/oauth/v2/auth
        form_login:
            provider: fos_userbundle
            check_path: _security_check
            login_path: _demo_login
        anonymous: true
    
    oauth_token:
        pattern:    ^/oauth/v2/token
        security:   false
        
        
access_control:
    - { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/accedi$, role: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/registrati, role: IS_AUTHENTICATED_ANONYMOUSLY }

当您使用FOSUserBundle时,登录表单的呈现将在SecurityController::renderLogin()中进行。

解决方案基本上是:

  • 重写SecurityController
  • 添加角色IS_AUTHENTICATD_ANONYMOUSLY的检查
  • 如果找不到角色,则将用户重定向到另一个页面

我假设您已经创建了一个扩展FOSUserBundle的捆绑包,其中包含您的User实体。

我假设这个束被称为YourUserBundle,并且位于src/Your/Bundle/UserBundle

现在复制(不剪切)SecurityController

vendor/friendsofsymfony/user-bundle/src/FOS/UserBundle/Controller/SecurityController.php

到(以便覆盖FOSUserBundle提供的)

src/Your/Bundle/UserBundle/Controller/SecurityController.php

添加RedirectResponse的use语句并编辑renderLogin()方法,如下所示:

use Symfony'Component'HttpFoundation'RedirectResponse;
// ...
protected function renderLogin(array $data)
{
    if (false === $this->container->get('security.context')->isGranted('IS_AUTHENTICATED_ANONYMOUSLY')) {
        return new RedirectResponse('/', 403);
    }
    $template = sprintf('FOSUserBundle:Security:login.html.%s', $this->container->getParameter('fos_user.template.engine'));
    return $this->container->get('templating')->renderResponse($template, $data);
}

更新

现在使用security.authorization_checker代替security.context

http://symfony.com/blog/new-in-symfony-2-6-security-component-improvements

在我看来,覆盖登录表单的呈现是在错误的地方提供答案。登录表单的呈现不负责登录。这是登录请求的结果。它在未来的其他地方可能会有其他用途,在这些情况下,你会破坏功能。

对我来说,覆盖登录操作似乎更好。这是负责处理登录请求的实际组件。

要执行此操作,请覆盖Security Controller中的登录操作。假设您的MyProject项目中有一个MyUserBundle,它扩展了FOSUserBundle。

<?php
namespace MyProject'MyUserBundle'Controller;
use Symfony'Component'HttpFoundation'Request;
use Symfony'Component'HttpFoundation'RedirectResponse;
use FOS'UserBundle'Controller'SecurityController as BaseSecurityController;
class SecurityController extends BaseSecurityController
{
    /**
     * Overriding login to add custom logic.
     */
    public function loginAction(Request $request)
    {
        if( $this->container->get('security.context')->isGranted('IS_AUTHENTICATED_REMEMBERED') ){
            // IS_AUTHENTICATED_FULLY also implies IS_AUTHENTICATED_REMEMBERED, but IS_AUTHENTICATED_ANONYMOUSLY doesn't
            return new RedirectResponse($this->container->get('router')->generate('some_route_name_in_my_project', array())); 
            // of course you don't have to use the router to generate a route if you want to hard code a route
        }
        return parent::loginAction($request);
    }
}

注意:我正在使用Symfony3.0.4@dev

这个答案是基于@nifr@mirk提供的答案和@Ronan的评论。

为了防止用户访问登录页面,我覆盖SecurityController,如下所示:

<?php
namespace AppBundle'Controller;
use Symfony'Component'HttpFoundation'RedirectResponse;
use FOS'UserBundle'Controller'SecurityController as BaseController;
class SecurityController extends BaseController
{
    /**
     * Renders the login template with the given parameters. Overwrite this function in
     * an extended controller to provide additional data for the login template.
     *
     * @param array $data
     *
     * @return 'Symfony'Component'HttpFoundation'Response
     */
    protected function renderLogin (array $data)
    {
        // This little if do the trick
        if ($this->container->get('security.authorization_checker')->isGranted('ROLE_USER')) {
            return new RedirectResponse($this->container->get ('router')->generate ('app_generation_page'));
        }
        $template = sprintf ('FOSUserBundle:Security:login.html.twig');
        return $this->container->get ('templating')->renderResponse ($template, $data);
    }
}

我还将其添加到RegistrationController中以执行完全相同的操作。

希望它能帮助到你们中的一些人。

另一个基于@nifr答案的解决方案。仅覆盖捆绑包控制器中的renderLogin函数。另请参阅。如何使用Bundle继承来覆盖Bundle 的部分

namespace MyProject'UserBundle'Controller;
//namespace FOS'UserBundle'Controller;
 use Symfony'Component'HttpFoundation'RedirectResponse;
 use 'FOS'UserBundle'Controller'SecurityController as BaseController;
 class SecurityController extends BaseController {
/**
 * Renders the login template with the given parameters. Overwrite this function in
 * an extended controller to provide additional data for the login template.
 *
 * @param array $data
 *
 * @return 'Symfony'Component'HttpFoundation'Response
 */
  protected function renderLogin(array $data) {
    if (true === $this->container->get('security.context')->isGranted('ROLE_USER')) {
        return new RedirectResponse($this->container->get('router')-  >generate('homeroute'));
    }
    $template = sprintf('FOSUserBundle:Security:login.html.%s', $this->container->getParameter('fos_user.template.engine'));
    return $this->container->get('templating')->renderResponse($template, $data);
  }
}  

我正在使用路由和安全性来启用此功能。

#routing.yml
index:
    path: /
    defaults: { _controller: AppBundle:Base:index }
    methods: [GET]
login:
    path: /login
    defaults: { _controller: AppBundle:Security:login }
    methods: [GET]

如果用户已登录,则会重定向到仪表板。如果没有,他将看到登录路径。

#security.yml
    access_control:
        - { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/dashboard, role: ROLE_USER }

希望这对你有所帮助。:)