對於未來的項目,我正在尋找一種使用Symfony2來管理多站點開發的方法。實際上,每個網站都將位於不同的子域上,但它們的工作方式是相同的。只有風格會改變一點。只有一個認證點的多站點
問題是:身份驗證對所有子網站都是通用的,並由主網站(www.mydomain.com)管理。每個多站點將擁有自己的數據庫。
Symfony2可以這麼做嗎?我知道有可能使用多域名,但我不知道如何認證系統。你有什麼想法如何進行?
謝謝!
對於未來的項目,我正在尋找一種使用Symfony2來管理多站點開發的方法。實際上,每個網站都將位於不同的子域上,但它們的工作方式是相同的。只有風格會改變一點。只有一個認證點的多站點
問題是:身份驗證對所有子網站都是通用的,並由主網站(www.mydomain.com)管理。每個多站點將擁有自己的數據庫。
Symfony2可以這麼做嗎?我知道有可能使用多域名,但我不知道如何認證系統。你有什麼想法如何進行?
謝謝!
其實我已經設法在我正在進行的其中一個項目中做到這一點。
這有點棘手,但是一旦您瞭解了symfony安全層的基本概念,就很容易將其集成到您現有的項目中。
首先,請務必閱讀:http://symfony.com/doc/current/book/security.html。我還建議看看食譜的安全部分。
你不會在手冊中找到一個直接的問題,但它有助於理解我要粘貼在這裏的代碼。
基本思想是在子域之間共享會話ID。
注意:爲了節省空間,我將省略PHP中的use
和namespace
標記。不要忘記導入並指定適當的名稱空間。
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的可用性。
祝你好運,並隨時在評論部分提問。我知道這一開始可能會讓人感到困惑。
謝謝,我會盡快嘗試:) – 2012-01-03 08:11:23