我对Symfony2安全系统存在“小问题”。这就是我需要实现的目标:
我已经设法为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表单。
有人能帮助我吗? :)文档在这一点上不是很好......
答案 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!
}
}
}
现在一切似乎都有效......