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ẻ use
và namespace
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.
Cảm ơn, tôi sẽ cố gắng nhanh chóng :) –
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