2011-12-15 58 views
8

對於未來的項目,我正在尋找一種使用Symfony2來管理多站點開發的方法。實際上,每個網站都將位於不同的子域上,但它們的工作方式是相同的。只有風格會改變一點。只有一個認證點的多站點

問題是:身份驗證對所有子網站都是通用的,並由主網站(www.mydomain.com)管理。每個多站點將擁有自己的數據庫。

Symfony2可以這麼做嗎?我知道有可能使用多域名,但我不知道如何認證系統。你有什麼想法如何進行?

謝謝!

回答

8

其實我已經設法在我正在進行的其中一個項目中做到這一點。

這有點棘手,但是一旦您瞭解了symfony安全層的基本概念,就很容易將其集成到您現有的項目中。

首先,請務必閱讀:http://symfony.com/doc/current/book/security.html。我還建議看看食譜的安全部分。

你不會在手冊中找到一個直接的問題,但它有助於理解我要粘貼在這裏的代碼。

基本思想是在子域之間共享會話ID。

注意:爲了節省空間,我將省略PHP中的usenamespace標記。不要忘記導入並指定適當的名稱空間。

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> 

最後 - 在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" 

這是simpliest和儘可能薄整潔我可以想到的。 這裏唯一的「誤用」類是SessionMatcher,它只檢查會話中會話ID的可用性。

祝你好運,並隨時在評論部分提問。我知道這一開始可能會讓人感到困惑。

+0

謝謝,我會盡快嘗試:) – 2012-01-03 08:11:23