【问题标题】:FOSUserbundle + Additional HTTP auth without user getting ROLE_USERFOSUserbundle + 没有用户获得 ROLE_USER 的附加 HTTP 身份验证
【发布时间】:2014-11-14 09:27:52
【问题描述】:

我的 Symfony2 安全系统有一个“小”问题。这是我需要实现的目标:

  • / 需要 HTTP 身份验证才能看到任何页面。我想用一个恒定的用户/通行证对来保护整个页面。用户输入正确的配对后,他应该是访客(而不是 ROLE_USER),并且能够通过 FOSUserBundle 表单登录
  • /api 需要通过 HTTP 身份验证单独登录,独立于 FOSUserBundle 和其他 HTTP 身份验证

我已经设法为 API 提供了一个单独的登录名。这是我完整的 security.yml:

security:
    encoders:
        FOS\UserBundle\Model\UserInterface: sha512
        Symfony\Component\Security\Core\User\User: plaintext

    role_hierarchy:
        ROLE_ADMIN:       ROLE_USER
        ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]

    providers:
        in_memory_http:
            memory:
                users:
                    User1: { password: PW1, roles: ROLE_HTTP }
        in_memory_api:
            memory:
                users:
                    User2: { password: PW2, roles: ROLE_API }
        fos_userbundle:
            id: fos_user.user_provider.username_email

    firewalls:
        api:
            pattern: ^/api
            http_basic:
                provider: in_memory_api
                realm: "API login"

        http:
            pattern: ^/
            provider: in_memory_http
            http_basic:
                realm: "Hello"
            context: primary_auth            

        main:
            pattern: ^/                      
            form_login:                 
                provider: fos_userbundle    
                login_path: fos_user_security_login                            
                csrf_provider: form.csrf_provider
                check_path: fos_user_security_check
            logout: 
                path: fos_user_security_logout
                target: home         
            anonymous: true                   
            context: primary_auth        

        dev:
            pattern:  ^/(_(profiler|wdt)|css|images|js)/
            security: false

        login:
            security: false

        secured_area: 
            anonymous: ~

    access_control:
        - { path: ^/api, roles: ROLE_API }
        - { path: ^/user/login.html, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/user/logout.html, roles: IS_AUTHENTICATED_REMEMBERED }

这几乎可以按预期工作,但不幸的是并不完全... /api 部分就像我希望它工作一样工作。我希望这里无事可做。

但是当我导航到 /,输入 User1/PW1 并发送凭据时,我可以访问该页面,就像预期的那样。唯一的问题是 User1 已登录!但我希望不要像普通用户一样处理 User1。应该只要求他访问正常的登录表单和/ api 之外的其余部分。我什至无法注销该用户。如果我在 User1 登录时导航到 /user/login.html(由于所需的 http 身份验证)并输入真实 fosuserbundle 用户的有效用户数据,我会得到:“您必须配置要由防火墙处理的检查路径在您的安全防火墙配置中使用 form_login。”

如果我想注销,我会收到:“您必须在安全防火墙配置中激活注销。”

我想要的是一种两步验证。 首先是 HTTP Auth,然后是 FOSUserBundle 表单。

有人可以帮助我吗? :) 文档在这一点上不是很好...

【问题讨论】:

  • 如果您使用的是 apache,您可以使用 .htpasswd 来实现您所需要的。对于 Nginx,你可以阅读 here
  • 是的,但我需要独立于 apache 或 nginx
  • 阅读this 了解您的第一点
  • 感谢您的评论,但这对我有什么帮助?我应该更改 FOSUserBundle 代码吗?我认为这不是推荐的方式......我需要的是:通过 HTTP auth 登录而不实际以 FOSUserBundle 用户身份登录,因为我想为此进行第二次登录

标签: security http symfony authentication


【解决方案1】:

所以.....

在经历了几个小时的痛苦之后,我放弃了尝试使用 Symfony2 安全组件。我也没有设法实现我想要的 Apache(尝试过 SetEnvIf 和 FilesMatch)。

所以我写了一个请求监听器来做我想做的事。如果有人有同样的问题,这是我的解决方案!

use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpFoundation\Response;

class RequestListener
{
    /**
    * Returns true iff the specified $password belongs to the $user and the $user has access to the specified $area
    */
    private function hasAccess($user, $password, $area) {
        $users = array("User1" => array("password" => "PW1", 
                                             "areas" => array("portal")),
                       "User2" => array("password" => "PW2", 
                                                     "areas" => array("API", "portal"))
                 );

        return $user
            && array_key_exists($user, $users)
            && $users[$user]["password"] == $password
            && in_array($area, $users[$user]["areas"]);
    }

    /**
    * Extracts the area out of the $path
    */
    public function getArea($path) {
        if (substr($path, 0, 4) == "/api") {
            return "API";    
        } else {
            return "portal";
        }
    }

    /**
    * Service handler
    */
    public function onKernelRequest(GetResponseEvent $event) {      
        $request = $event->getRequest();    

        # $path cointains the path to the virtual resource the user requested!
        # If the user calls app_dev.php/api/users, then $path is "/api/users"
        # If the user calls app.php/api/users,     then $path is "/api/users"
        # If the user calls /api/users,            then $path is "/api/users"
        # If the user calls /app_dev.php,          then $path is "/"
        # If the user calls /,                     then $path is "/"
        # and so on
        #
        # ==> $path abstracts the front controller away
        $path = $request->getPathInfo();
        $area = $this->getArea($path);

        # $user, $password are null if no AUTH data is sent
        $user = $request->server->get("PHP_AUTH_USER");
        $password = $request->server->get("PHP_AUTH_PW");   

        # If the user has no access, he must log in as another user
        if (!$this->hasAccess($user, $password, $area)) {   
            # In case the response already exists (in most cases not) we use it and modify it
            $response = $event->hasResponse() ? $event->getResponse() : new Response();             
            $response->setStatusCode(Response::HTTP_UNAUTHORIZED); # Code 401
            $response->headers->set("WWW-Authenticate", "Basic realm=\"".$area."\"");   
            $response->setContent("Please provide valid data");         
            $response->send();
            die(); # To make sure the page is not shown!
        }   
    }
}

现在似乎一切正常...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-18
    • 1970-01-01
    • 2019-10-06
    • 2019-01-25
    相关资源
    最近更新 更多