2011-12-15 23 views
8

Đối với một dự án trong tương lai, tôi đang tìm cách quản lý phát triển nhiều trang với Symfony2. Trên thực tế, mỗi trang web sẽ nằm trên một tên miền phụ khác nhưng sẽ hoạt động theo cùng một cách; chỉ có phong cách sẽ thay đổi một chút.Nhiều trang chỉ có một điểm xác thực

Điều này là: xác thực là phổ biến cho tất cả các trang con và được quản lý bởi trang web chính (www.mydomain.com). Sau đó, mỗi trang sẽ có cơ sở dữ liệu riêng.

Có thể làm như vậy với Symfony2 không? Tôi biết nó có thể sử dụng multidomains, nhưng tôi không làm thế nào về hệ thống xác thực. Bạn có ý tưởng về cách tiến hành không?

Cảm ơn!

Trả lời

8

Thực tế tôi đã quản lý để thực hiện điều này trong một trong các dự án mà tôi đang làm việc.

Đó là một chút khó khăn nhưng một khi bạn hiểu được khái niệm cơ bản đằng sau lớp bảo mật của symfony thì nó rất dễ tích hợp vào dự án hiện tại của bạn.

Trước hết, hãy nhớ đọc: http://symfony.com/doc/current/book/security.html. Tôi cũng khuyên bạn nên xem phần bảo mật của sách dạy nấu ăn.

Bạn sẽ không tìm thấy một anwer thẳng trong hướng dẫn sử dụng nhưng nó giúp hiểu được mã tôi sẽ dán ở đây.

Ý tưởng cơ bản là chia sẻ id phiên trên các tên miền phụ.

Lưu ý: vì mục đích không gian, tôi sẽ bỏ qua các thẻ usenamespace trong PHP. Đừng quên nhập và chỉ định các không gian tên thích hợp.

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> 

Và cuối cùng - các 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" 

Đây là đơn giản nhất và càng gọn gàng càng tốt g Tôi có thể nghĩ đến. Lớp "bị lạm dụng" duy nhất ở đây là SessionMatcher chỉ kiểm tra tính khả dụng của id phiên trong phiên.

Chúc may mắn và vui lòng đặt câu hỏi trong phần nhận xét. Tôi biết điều này có thể khá khó hiểu ngay từ đầu.

+0

Cảm ơn, tôi sẽ cố gắng nhanh chóng :) –

+0

Bạn có thể tư vấn cho các miền khác nhau như thế nào? Tôi có thể chia sẻ nhớ tôi beetwen họ, nhưng người dùng có thể đăng nhập mà không cần nhớ tôi tham số. Ngoài ra tôi có thể chia sẻ session id cookie betwen tên miền, nhưng đây không phải là cách tốt IMHO. – Koc

Các vấn đề liên quan