【发布时间】:2018-01-04 15:30:35
【问题描述】:
我在使用 Symfony 3 执行子请求时遇到了一种奇怪的行为。
上下文
我认为最好解释一下我为什么要做导致问题的原因,这可能有助于找到解决方案。 但是如果阅读量太大,你可以跳过这部分。
我开发了一种Api Platform 的克隆,不是一个分支,而是一个受启发的重写。 我想添加的功能之一是能够从服务器端调用 Api。
Api 自己做了很多工作(例如在关系上工作得很好),所以让它来做是非常有用的 持久化/过滤等工作,即使对于经典的服务器端应用程序(无 ajax 调用)也是如此。
我发现的最简单的方法是执行子请求。我当前的实现在使用时看起来像这样:
// To get a collection of articles (output as array)
$this->proxy->get(Article::class)->collection($page, $itemsPerPages, $filters, $ordering)->asArray();
// To get a single article (output as entities)
$this->proxy->get(Article::class)->item('a2Ck2')->asObject();
// To persist an article (output as entities)
$this->proxy->persist(Article::class)->item(['title' => 'Super article'])->asObject();
你明白了。
当您调用as* 方法时,请求被执行,执行此操作的代码非常简单:
// Proxy.php
public function execute(Request $request, Response &$response = null)
{
$response = $this->kernel->handle($request);
[...]
// Handle the response status code, the content etc. Not important here.
}
execute 方法的参数中给出的请求是这样构建的(这里显示了 GET 构建器):
// GetRequestBuilder.php
protected function createRequest(): Request
{
$currentRequest = $this->getCurrentRequest();
$request = Request::create(
$this->generateUri([...]),
Request::METHOD_GET,
[],
$currentRequest->cookies->all()
);
return $request;
}
问题
当我有多个防火墙并且当我执行一个子请求时,会出现一个问题,该子请求与原始请求的防火墙不同。
对于以下security的配置:
# app/config/security.yml
security:
encoders:
AppBundle\Entity\User:
algorithm: bcrypt
providers:
main:
id: app.main_user_provider
doctrine:
entity:
class: AppBundle:User
property: username
firewalls:
backend:
pattern: ^/admin
anonymous: ~
logout:
path: /admin/logout
target: /admin/login
invalidate_session: true
stateless: false
guard:
entry_point: app.backend.login_form_authenticator
authenticators: [app.backend.login_form_authenticator]
frontend:
pattern: ^/
anonymous: ~
logout:
path: /logout
target: /login
invalidate_session: true
stateless: false
guard:
entry_point: app.frontend.login_form_authenticator
authenticators: [app.frontend.login_form_authenticator]
如果我从 admin 路由向匹配frontend 防火墙的路由发出子请求,我将丢失我的管理员会话。
如果我在 Api 调用之前和之后转储会话,它会给我以下信息:
array (size=3)
'_security.backend.target_path' => string 'http://localhost/admin/api/formation/categories' (length=51)
'_csrf/form' => string 'wt9Js9b8deT00XanUgEq23qXFqY8uHrt_j5i6D9Btj8' (length=43)
'_security_backend' => string 'C:67:"Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken":1786:{a:2:{i:0;s:7:"backend";i:1;s:174'... (length=1868)
在子请求之后:
array (size=2)
'_security.backend.target_path' => string 'http://localhost/admin/api/formation/categories' (length=51)
'_csrf/form' => string 'wt9Js9b8deT00XanUgEq23qXFqY8uHrt_j5i6D9Btj8' (length=43)
_security_backend 键不见了,我的会话也不见了。
如果我不将原始请求的cookies复制到子请求中,会话不会丢失,但是如果Api路由是安全的,我有一个问题:
// GetRequestBuilder.php
protected function createRequest(): Request
{
$currentRequest = $this->getCurrentRequest();
$request = Request::create(
$this->generateUri([...]),
Request::METHOD_GET,
[],
// Removing this line and replace it with an empty array solves the problem of loosing the session.
// But if the target route is behind the "backend" firewall, the sub request will be redirected to the login page.
$currentRequest->cookies->all()
);
return $request;
}
我的问题
1) 您认为这种执行子请求的方法是否足够健壮,或者您知道获得相同结果的更好方法吗?
附带说明:我不使用 CURL 或任何其他方式来执行 真实 http 请求,因为我希望能够获得 entities(实际对象)作为 Api 调用的结果。
2) 如果子请求与具有不同防火墙的路由匹配,您是否知道防止原始会话丢失的方法? 据我现在了解,我看到的唯一方法是检测(不知道如何......)目标路由是否在同一个防火墙上,如果是,则只复制 cookie.. 似乎很难做到,而且很难稳定。它看起来像**一样脏。
任何想法将不胜感激。
感谢您的帮助。
【问题讨论】:
标签: php api symfony session request