具有多个防火墙和多个共享保护身份验证器的 Symfony 3.4 防火墙配置

Symfony 3.4 firewall configuration with multiple firewalls and multiple shared guard authenticators

这是一个 Symfony 3.4 应用程序,具有网站前端和访问相同后端的移动应用程序。用户可以通过

登录
  1. 提交用户名和密码(表单登录)
  2. 使用 google 帐户进行身份验证
  3. 使用 Facebook 帐户进行身份验证

以前只有一种登录方式(用户名+密码)。身份验证和防火墙配置有效。添加社交网络身份验证需要更改防火墙配置,现在防护身份验证已部分损坏。

到目前为止我有信心的是我们需要为网络用户(主要)和移动应用程序用户(api)设置单独的防火墙。 Web 用户经过一次身份验证,然后登录的用户信息存储在会话 cookie 中,但移动应用程序用户在每次传入请求时都经过身份验证。当移动用户成功登录时,他们会收到一个 jwt 令牌作为响应,他们将在每个后续请求中发送该令牌。

最大的问题似乎是防火墙配置的其余部分。在当前配置中,"main" 防火墙按预期工作。但是针对移动用户的 "api" 防火墙有些问题。登录使用相同的保护身份验证器,以便它们 return jwt 令牌如预期的那样。但是随令牌发送的后续请求都会导致 403 访问被拒绝响应。我怀疑词法验证器永远不会从请求中获取 jwt 令牌,所以看起来用户从未登录过。

验证器已经过测试,它们似乎对网络用户和移动用户都能正常工作。 lexik jwt authenticator 的配置也是正确的 - 或者自从移动用户仍然拥有单个验证器以来它没有改变。这意味着密钥、密码短语、令牌 ttl。

一个可行的想法是为移动登录 url 和其余移动路由设置单独的防火墙,因为它们由不同的身份验证器处理。我试过了,情况没有任何改善:登录有效,但 jwt 身份验证无效。以下security.yml的相关部分:

security:
...
  providers:
    db_users:
      entity: { ... }
  firewalls:
    dev:
      pattern: ^/(_(profiler|wdt)|css|images|js|api)/(password/reset)
      security: false
    api:
      pattern: ^/api/
      stateless: true
      lexik_jwt: ~
      anonymous: false
      guard:
        provider: db_users
        authenticators:
          - main.form_login_authenticator
          - main.google_login_authenticator
          - main.fb_login_authenticator
          - lexik_jwt_authentication.jwt_token_authenticator
        entry_point: main.form_login_authenticator
    main:
      pattern: ^/
      guard:
        provider: db_users
        authenticators:
          - main.form_login_authenticator
          - main.google_login_authenticator
          - main.fb_login_authenticator
        entry_point: main.form_login_authenticator
      form_login:
        remember_me: true
        login_path: login
        check_path: login
        always_use_default_target_path: true
        default_target_path: /redirect
        target_path_parameter: _target_path
        use_referer: false
        require_previous_session: false
      anonymous: true
...

这里有什么问题?我应该如何调试这个问题? (除了使用邮递员模拟来自移动应用程序的 json 请求)

附加信息:所有三个自定义身份验证器都会创建一个 jwt 令牌,例如:

public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
  {
    // mobile users:
    if ( $request->getRequestFormat() == 'json') {
      return new JsonResponse(['token' => $this->jwtManager->create($token->getUser())]);
  // web users handled here

  }

然后关于访问模式(或请求如何处理以及由哪个身份验证器处理),这是来自 main.form_login_authenticator

的示例
public function supports(Request $request)
{
    return ('login' === $request->attributes->get('_route') || 'api_login_check' === $request->attributes->get('_route'))
        && $request->isMethod('POST');
}

不过,验证器似乎按预期工作。登录工作。不起作用的是使用 jwt 令牌保持登录状态。

首先,对于 api 防火墙,你应该只有 jwt 验证器,因为即使 auth.使用社交网络完成后,您必须生成自己的 jwt 令牌。

其次,你能把你的访问模式发给我们吗?这可以给我们更多的线索。

第三,我不确定 api 防火墙的入口点是否应该与第一个 (https://symfony.com/doc/current/components/security/firewall.html#entrypoint)

终于成功了。该解决方案的主要思想是分离防火墙:一个用于移动登录路由,另一个用于其余移动 (api) 路由。这是有效的配置,遗漏用三个点标记。 main 防火墙配置如问题所示。

security:
  ...
  firewalls:
    ...
    api_login:
      pattern: ^/api/(login|google-login|fb-login)
      stateless: true
      anonymous: true
      guard:
        provider: db_users
        authenticators:
          - main.form_login_authenticator
          - main.google_login_authenticator
          - main.fb_login_authenticator
        entry_point: main.form_login_authenticator
    api:
      pattern: ^/api
      stateless: true
      guard:
        provider: db_users
        authenticators:
          - lexik_jwt_authentication.jwt_token_authenticator
    main: 
      ...

身份验证器服务名称的前缀最初来自防火墙的名称main,这不再有意义,因为它们现在在多个防火墙之间共享。