2011-12-15 9 views
8

Para un proyecto futuro, estoy buscando una forma de administrar el desarrollo de sitios múltiples con Symfony2. De hecho, cada sitio estará en un subdominio diferente, pero funcionará de la misma manera; solo el estilo cambiará un poco.Multisitios con solo un punto de autenticación

El hecho es que la autenticación es común para todos los subsitios y es administrada por el sitio principal (www.midominio.com). Cada multisitios tendrá su propia base de datos.

¿Es posible hacerlo con Symfony2? Sé que es posible usar multidominios, pero no sé qué tal el sistema de autenticación. ¿Tienes ideas sobre cómo proceder?

Gracias!

Respuesta

8

En realidad, he logrado hacer esto en uno de los proyectos en los que estoy trabajando.

Es un poco complicado pero una vez que comprenda el concepto básico detrás de la capa de seguridad de Symfony, es extremadamente fácil de integrar en su proyecto existente.

Primero, asegúrese de leer esto: http://symfony.com/doc/current/book/security.html. También recomendaría echar un vistazo a la sección de seguridad del libro de cocina.

No encontrará una respuesta directa en el manual, pero ayuda a comprender el código que voy a pegar aquí.

La idea básica es compartir el id de sesión en los subdominios.

Nota: por el bien de espacio, voy a estar omitiendo los use y namespace etiquetas en PHP. No olvide importar y especificar espacios de nombres apropiados.

class LoginListener 
{ 

    public function onLogin(InteractiveLoginEvent $event) 
    { 
     $token = $event->getAuthenticationToken(); 

     //multisite log-in 
     if ($token->getUser() instanceof User) 
     { 
      $_SESSION['_user_id'] = $token->getUser()->getId(); 
     } 
    } 

} 

class LogoutListener implements LogoutHandlerInterface 
{ 
    public function logout(Request $request, Response $response, TokenInterface $token) 
    { 
     if (isset($_SESSION['_user_id'])) 
     { 
      unset($_SESSION['_user_id']); 
     } 
    } 
} 

class SessionMatcher implements RequestMatcherInterface 
{ 
    public function matches(Request $request) 
    { 
     $request->getSession()->start(); 
     return isset($_SESSION['_user_id']); 
    } 
} 

class CrossLoginUserToken extends AbstractToken 
{ 

    private $id; 

    public function getId() 
    { 
     return $this->id; 
    } 

    public function __construct($id, array $roles = array()) 
    { 
     parent::__construct($roles); 

     $this->id = $id; 

     parent::setAuthenticated(count($roles) > 0); 
    } 

    public function getCredentials() 
    { 
     return ''; 
    } 

} 

class CrossLoginProvider implements AuthenticationProviderInterface 
{ 

    private $userProvider; 

    public function __construct(UserProviderInterface $userProvider) 
    { 
     $this->userProvider = $userProvider; 
    } 

    public function authenticate(TokenInterface $token) 
    { 
     $user = $this->userProvider->loadUserByUsername($token->getId()); 

     if ($user) 
     { 
      $authenticatedToken = new CrossLoginUserToken($token->getId(),$user->getRoles()); 
      $authenticatedToken->setUser($user); 

      return $authenticatedToken; 
     } 

     throw new AuthenticationException('The CrossSite authentication failed.'); 
    } 

    public function supports(TokenInterface $token) 
    { 
     return $token instanceof CrossLoginUserToken; 
    } 

} 

class CrossLoginListener implements ListenerInterface 
{ 

    protected $securityContext; 
    protected $authenticationManager; 
    protected $session; 

    public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, Session $session) 
    { 
     $this->securityContext = $securityContext; 
     $this->authenticationManager = $authenticationManager; 
     $this->session = $session; 
    } 

    public function handle(GetResponseEvent $event) 
    { 
     $this->session->start(); 
     if (!is_null($this->securityContext->getToken()) && $this->securityContext->getToken()->isAuthenticated()) 
     { 
      return; 
     } 
     if (isset($_SESSION['_user_id'])) 
     { 
      try 
      { 
       $token = $this->authenticationManager->authenticate(new CrossLoginUserToken($_SESSION['_user_id'])); 
       $this->securityContext->setToken($token); 
      } 
      catch (AuthenticationException $e) 
      { 
       throw $e; 
      } 
     } 
    } 

} 

class CrossLoginFactory implements SecurityFactoryInterface 
{ 
    public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint) 
    { 
     $providerId = 'security.authentication.provider.crosslogin.' . $id; 
     $container 
       ->setDefinition($providerId, new DefinitionDecorator('crosslogin.security.authentication.provider')) 
       ->replaceArgument(0, new Reference($userProvider)) 
     ; 

     $listenerId = 'security.authentication.listener.crosslogin.' . $id; 
     $listener = $container->setDefinition($listenerId, new DefinitionDecorator('crosslogin.security.authentication.listener')); 

     return array($providerId, $listenerId, $defaultEntryPoint); 
    } 

    public function getPosition() 
    { 
     return 'pre_auth'; 
    } 

    public function getKey() 
    { 
     return 'crosslogin'; 
    } 

    public function addConfiguration(NodeDefinition $node) 
    { 

    } 

} 

security_factories.yml:

<?xml version="1.0" encoding="UTF-8"?> 
    <container xmlns="http://symfony.com/schema/dic/services" 
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> 

     <services> 
      <service id="security.authentication.factory.crosslogin" class="MyBundle\Security\Factory\CrossLoginFactory"> 
       <tag name="security.listener.factory" /> 
      </service> 
     </services> 
    </container> 

config.xml:

<service id="crosslogin.security.authentication.provider" class="MyBundle\Security\Authentication\Provider\CrossLoginProvider"> 
    <argument /> 
</service> 

<service id="crosslogin.security.authentication.listener" class="MyBundle\Security\Firewall\CrossLoginListener"> 
    <argument type="service" id="security.context" /> 
    <argument type="service" id="security.authentication.manager" /> 
    <argument type="service" id="session" /> 
</service> 

<service id="crosslogin.session.matcher" class="MyBundle\Security\Matcher\SessionMatcher"> 

</service> 

<service id="crosslogin.handler.logout" class="MyBundle\Listener\LogoutListener"> 
    <service id="listener.login" class="Backend\CmsBundle\Listener\LoginListener"> 
     <tag name="kernel.event_listener" event="security.interactive_login" method="onLogin" /> 
</service> 

Y, por último - la security.yml:

firewalls: 

    ... 

    crosslogin: 
     crosslogin: true 
     provider: dao_provider_by_id 
     request_matcher: crosslogin.session.matcher 
     logout: 
      path: /secured/logout 
      target:/
      invalidate_session: true 
      handlers: [crosslogin.handler.logout] 

providers: 

    ... 

    dao_provider_by_id: 
     entity: { class: YOUR_SECURITY_CLASS_NAME, property: id } 

factories: 
    CrossLoginFactory: "%kernel.root_dir%/../src/MyBundle/Resources/config/security_factories.xml" 

Este es el más simple y lo más limpio posible g Podría pensar. La única clase "mal utilizada" aquí es SessionMatcher que solo comprueba la disponibilidad del ID de sesión en la sesión.

Buena suerte, y siéntase libre de hacer preguntas en la sección de comentarios. Sé que esto puede ser bastante confuso al principio.

+0

Gracias, lo intentaré rápidamente :) –

+0

¿Qué puedes aconsejar para diferentes dominios? Puedo compartir recordarme cookie antes que ellos, pero el usuario puede iniciar sesión sin recordarme el parámetro. También puedo compartir la cookie de ID de sesión entre dominios, pero esta no es una buena manera en mi humilde opinión. – Koc

Cuestiones relacionadas