如何在已登录主域的其它应用程序中,使用Symfony Security登录子域?

6

我已经登录到主域名。比如说 example.com(在遗留的kohana开发的应用程序)。我正在尝试登录到子域名,比如说foo.bar.example.com

foo.example.com是一个symfony应用程序。以下是我的配置。 开发人员工具条显示“匿名”用户。它不会从cookie中的会话ID中记录用户登录。

security.yml

# To get started with security, check out the documentation:
# http://symfony.com/doc/current/book/security.html
security:

    # http://symfony.com/doc/current/book/security.html#where-do-users-come-from-user-providers
    providers:
        in_memory:
            memory: ~

    firewalls:
        # disables authentication for assets and the profiler, adapt it according to your needs
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false

        main:
            anonymous: ~
            # activate different ways to authenticate

            # http_basic: ~
            # http://symfony.com/doc/current/book/security.html#a-configuring-how-your-users-will-authenticate

            # form_login: ~
            # http://symfony.com/doc/current/cookbook/security/form_login_setup.html

Config.yml

framework:
    #esi:             ~
    #translator:      { fallbacks: ["%locale%"] }
    secret:          "%secret%"
    router:
        resource: "%kernel.root_dir%/config/routing.yml"
        strict_requirements: ~
    form:            ~
    csrf_protection: ~
    validation:      { enable_annotations: true }
    #serializer:      { enable_annotations: true }
    default_locale:  "%locale%"
    trusted_hosts:   ~
    trusted_proxies:  %trusted_proxies%
    session:
        # handler_id set to null will use default session handler from php.ini
        handler_id:  'snc_redis.session.handler'
        name: 'MY_COOKIE_NAME'
        cookie_domain: '.example.com'
    fragments:       ~
    http_method_override: true
    request:
        formats:
            pdf: 'application/pdf'
            epub: 'application/epub+zip'


snc_redis:
    session:
        client: session
        prefix: ''
    clients:
        default:
            type: predis
            alias: default
            dsn: %redis_dsn%
            logging: false
            options:
#                profile: 2.6
                profile: 2.2
                connection_persistent: false
        slave:
            type: predis
            alias: slave
            logging: false
            dsn: %redis_dsn_slave%

        session:
            type: predis
            alias: session
            dsn: %redis_dsn%

我需要配置至少一个身份验证提供程序吗?或者我需要编写自定义身份验证提供程序,类似于记住我功能?
Symfony\Component\Security\Http\Firewall\AbstractAuthenticationListener->handle 有
  if ($this->options['require_previous_session'] && !$request->hasPreviousSession()) {
                throw new SessionUnavailableException('Your session has timed out, or you have disabled cookies.');
            }

Request对象具备以下功能

public function hasPreviousSession()
{
    // the check for $this->session avoids malicious users trying to fake a session cookie with proper name
    return $this->hasSession() && $this->cookies->has($this->session->getName());
}

默认情况下,会话 cookie 配置在子域上,为了使相同的 cookie 可访问,请确保检查 SESSIONID cookie 是否具有正确的 TLD 域。因此,应该使用 "example.com" 而不是 "sub.example.com"。 - Rvanlaak
我已经正确设置了域名,但仍然没有运气。 - vishal
Symfony不会自动使用SESSIONID,您应该创建自己的Provider,可以将该会话与(共享的)数据库中的会话进行匹配。之后,您可以手动设置UsernamePasswordToken - Rvanlaak
必须有默认实现来将cookie中的sessionId与存储在文件系统中的会话匹配。我已经配置了redis会话处理程序,并将其映射到session.handler_id。抱歉不理解为什么需要编写自定义提供程序。 - vishal
1个回答

1
在主域名和登录后,您可以将令牌保存到数据库和 cookie 中,在第二个应用程序中,您应该创建一个自定义的身份验证器,它可以从浏览器中恢复令牌并获取到数据库中。如果找到匹配项,则会对用户进行身份验证。
security:

# http://symfony.com/doc/current/book/security.html#where-do-users-come-from-

    firewalls:
    # disables authentication for assets and the profiler, adapt it according to your needs
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false

        main:
            stateless: true
            provider: customProvider
            guard:
                authenticators:
                    - App\Security\TokenAuthenticator

src/Security/TokenAuthenticator.php

namespace App\Security;

use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Guard\AbstractGuardAuthenticator;

class TokenAuthenticator extends AbstractGuardAuthenticator
{
private $em;

public function __construct(EntityManagerInterface $em)
{
    $this->em = $em;
}

/**
 * Called on every request to decide if this authenticator should be
 * used for the request. Returning false will cause this authenticator
 * to be skipped.
 */
public function supports(Request $request)
{
    return true;
}

/**
 * Called on every request. Return whatever credentials you want to
 * be passed to getUser() as $credentials.
 */
public function getCredentials(Request $request)
{
    return [
        'token' => // recover token from cookies,
    ];
}

public function getUser($credentials, UserProviderInterface $userProvider)
{
    $token = $credentials['token'];

    if (null === $token) {
        return;
    }

    // if a User object, checkCredentials() is called
    return $this->em->getRepository(User::class)
        ->findOneBy(['token' => $token]);
}

public function checkCredentials($credentials, UserInterface $user)
{
    // check credentials - e.g. make sure the password is valid
    // no credential check is needed in this case

    // return true to cause authentication success
    return true;
}

public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
    // on success, let the request continue
    return null;
}

public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
    $data = [
        'message' => strtr($exception->getMessageKey(), $exception->getMessageData())

        // or to translate this message
        // $this->translator->trans($exception->getMessageKey(), $exception->getMessageData())
    ];

    return new JsonResponse($data, Response::HTTP_FORBIDDEN);
}

/**
 * Called when authentication is needed, but it's not sent
 */
public function start(Request $request, AuthenticationException $authException = null)
{
    $data = [
        // you might translate this message
        'message' => 'Authentication Required'
    ];

    return new JsonResponse($data, Response::HTTP_UNAUTHORIZED);
}

public function supportsRememberMe()
{
    return false;
}
}

希望它能解决你的问题


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接