FOSUserbundle +额外的HTTP身份验证,无需用户获取ROLE_USER

时间:2014-11-13 01:23:27

标签: security http symfony authentication

我对Symfony2安全系统存在“小问题”。这就是我需要实现的目标:

  • /需要HTTP身份验证才能看到任何页面。我想用一个常量的用户/传递对来保护整个页面。在用户输入正确的对后,他应该是访客(而不是ROLE_USER)并且能够通过FOSUserBundle表单登录
  • / api需要通过HTTP身份验证单独登录,独立于FOSU​​serBundle和其他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表单。

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

1 个答案:

答案 0 :(得分:0)

所以.....

经过几个小时的痛苦,我放弃了使用Symfony2安全组件进行尝试。我也没有设法通过Apache(Tried 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!
        }   
    }
}

现在一切似乎都有效......