symfony set the cookie_domain dynamically

不羁的心 提交于 2019-12-22 04:16:21

问题


I have a single app that ca serve multiple domains. I'm having a problem with the framework.session.cookie_domain

  • I'd like the session to be kept between subdomain, so far so good with cookie_domain set right
  • Where i have a problem is that i'd like the cookie_domain parameter set dynamically as i don't know in advance which domain the request is coming from.

    • I tried in the AppKernel.php to do something like :

    $domain = substr($_SERVER['HTTP_HOST'], strpos($_SERVER['HTTP_HOST'], '.')); ini_set('session.cookie_domain', $domain);

    • But it seems to break my sessions
  • I could have multiple config.yml one for each domain but i'd like to avoid that.

Do you know a way?

Thanks


回答1:


I have a similar situation. It's a multi-tenant site with school districts and schools. Each district and school has its own URL as follows:

  • school-1.district-1.example.com
  • school-2.district-1.example.com
  • school-1.district-2.example.com

I want users to be able to access all schools in one district with a single login. I therefore need the cookie to be at the district level.

This is my session storage service.

namespace AppBundle\Services;

use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Session\Storage\MetadataBag;
use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;

class MySessionStorage extends NativeSessionStorage
{
    public function __construct(array $options = array(), $handler = null, MetadataBag $metaBag = null, RequestStack $requestStack)
    {
        $host = $requestStack->getMasterRequest()->getHost();
        $options['cookie_domain'] = substr($host, strpos($host, '.') + 1);

        parent::__construct($options, $handler, $metaBag);
    }
}

In services.yml

mySessionStorage:
    class: AppBundle\Services\MySessionStorage
    arguments: [%session.storage.options%, @session.handler, @session.storage.metadata_bag, @request_stack]

In config.yml under framework:

session:
    handler_id: session.handler.native_file
    storage_id: mySessionStorage

Note that handler_id is null (~) by default in a standard Symfony installation. It needs to be set to something for the service to receive a non-null @session.handler.

That does it for the session cookie but the other one I needed to change is the remember_me cookie. You can set the domain to a constant in config.yml but I need it to depend on host. Maybe I'm missing something but I couldn't see a way to do it dynamically within the security system. RememberMeFactory is directly instantiated, not via configuration. My solution is to listen for kernel.response and replace the cookie before it is sent.

namespace AppBundle\Listeners;

use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;

class CookieFix
{
    private $requestStack;

    public function __construct(RequestStack $requestStack)
    {
        $this->requestStack = $requestStack;
    }

    public function onKernelResponse(FilterResponseEvent $event)
    {
        $response = $event->getResponse();
        $cookies = $response->headers->getCookies();
        $rMe = null;

        foreach($cookies as $cookie) {
            /** @var \Symfony\Component\HttpFoundation\Cookie $cookie */
            if ($cookie->getName() == 'REMEMBERME') {
                $rMe = $cookie;
                break;
            }
        }

        if ($rMe !== null) {
            $host = $this->requestStack->getMasterRequest()->getHost();
            $newDomain = substr($host, strpos($host, '.') + 1);

            $response->headers->removeCookie($rMe->getName());
            $response->headers->setCookie(new Cookie($rMe->getName(), $rMe->getValue(), $rMe->getExpiresTime(), $rMe->getPath(), $newDomain));
        }
    }
}

I should probably try to get the cookie name from the config.

In services.yml

cookieFix:
    class: AppBundle\Listeners\CookieFix
    arguments: [@request_stack]
    tags:
        - { name: kernel.event_listener, event: kernel.response, method: onKernelResponse, priority: -100 }

The -100 priority ensures that it runs after the listener that creates the cookie.




回答2:


Ok, i've figured this out.

It was not that difficult.

I created a custom sessionStorage, extending the default one and i did a simple override where the options were being dealt with: there i calculated my cookie_domain and passed it to the parent::function :

use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;

/**
 * DynamicDomainSessionStorage.
 *
 * @author Julien Devouassoud
 */
class DynamicDomainSessionStorage extends NativeSessionStorage
{
     /**
     * setOptions.
     *
     * {@inheritDoc}
     */
    public function setOptions(array $options)
    {   
        if(isset($_SERVER['HTTP_HOST'])){
            $domain = substr($_SERVER['HTTP_HOST'], strpos($_SERVER['HTTP_HOST'], '.'));

            $options["cookie_domain"] = $domain;
        } 



        return parent::setOptions($options);
    }
}

Don't forget:

• to declare your class as a service

• set this service as storage

• set the save_path otherwise cookie_domain seems not to work (breaks the session)

• i set a 'name' as well but i don't think it's essential

• code config.yml :

#...
framework:
    #...
    session:
         storage_id: v3d.session.storage.dynamic_domain
         save_path: %kernel.root_dir%/cache/var/sessions
         name: SFSESSID

services
    v3d.session.storage.dynamic_domain:
        class: V3d\Bundle\ApplicationBundle\Services\DynamicDomainSessionStorage


来源:https://stackoverflow.com/questions/24766260/symfony-set-the-cookie-domain-dynamically

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!