Эта проблема была воспроизведена в Symfony 3.3.17 и 3.4.9.
У меня есть собственный провайдер аутентификации, который связывает устаревшее приложение и приложение Symfony:
приложение / config / security.yml
security:
providers:
zog:
id: app.zog_user_provider
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: ~
logout:
path: /logout
target: /
guard:
authenticators:
- app.legacy_token_authenticator
- app.token_authenticator
entry_point: app.legacy_token_authenticator
src / AppBundle / Security / LegacyTokenAuthenticator:
class LegacyTokenAuthenticator extends AbstractGuardAuthenticator
{
private $session;
private $router;
public function __construct(
RouterInterface $router,
SessionInterface $session,
$environment
) {
if (session_status() != PHP_SESSION_ACTIVE) {
if ($environment != 'test'){
session_start();
}
$session->start();
$this->setSession($session);
}
//if (!$session->isStarted()) {
//}
$this->router = $router;
}
/**
* @return mixed
*/
public function getSession()
{
return $this->session;
}
/**
* @param mixed $session
*/
public function setSession($session)
{
$this->session = $session;
}
/**
* Called on every request. Return whatever credentials you want,
* or null to stop authentication.
*/
public function getCredentials(Request $request)
{
$session = $this->getSession();
if (isset($_SESSION['ADMIN_logged_in']) && intval($_SESSION['ADMIN_logged_in'])){
return $_SESSION['ADMIN_logged_in'];
}
return;
}
public function getUser($credentials, UserProviderInterface $userProvider)
{
return $userProvider->loadUserByUserId($credentials);
}
public function checkCredentials($credentials, UserInterface $user)
{
return $user->getUsername() == $credentials;
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
return null;
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
return null;
}
/**
* Called when authentication is needed, but it's not sent
*/
public function start(Request $request, AuthenticationException $authException = null)
{
$url = $this->router->generate('app_security_login');
return new RedirectResponse($url);
}
public function supportsRememberMe()
{
return false;
}
}
src / AppBundle / Security / TokenAuthenticator:
class TokenAuthenticator extends AbstractGuardAuthenticator
{
/**
* @var \Symfony\Component\Routing\RouterInterface
*/
private $router;
/**
* Default message for authentication failure.
*
* @var string
*/
private $failMessage = 'Invalid credentials';
/**
* @var UserPasswordEncoderInterface
*/
private $passwordEncoder;
/**
* Creates a new instance of FormAuthenticator
*/
public function __construct(
RouterInterface $router,
SessionInterface $session,
$environment,
UserPasswordEncoderInterface $passwordEncoder
) {
$this->passwordEncoder = $passwordEncoder;
$this->router = $router;
if (session_status() != PHP_SESSION_ACTIVE) {
if ($environment != 'test') {
session_start();
}
$session->start();
}
}
/**
* {@inheritdoc}
*/
public function getCredentials(Request $request)
{
if ($request->getPathInfo() != '/security/login' || !$request->isMethod('POST')) {
return;
}
return ['username' => $request->request->get('username'), 'password' => $request->request->get('password')];
}
/**
* {@inheritdoc}
*/
public function getUser($credentials, UserProviderInterface $userProvider)
{
try {
return $userProvider->loadUserByUsername($credentials['username']);
} catch (UsernameNotFoundException $e) {
throw new CustomUserMessageAuthenticationException(
$e->getMessage() != '' ?$e->getMessage():$this->failMessage
);
}
}
/**
* {@inheritdoc}
*/
public function checkCredentials($credentials, UserInterface $user)
{
if ($this->passwordEncoder->isPasswordValid($user, $credentials['password'])) {
return true;
}
throw new CustomUserMessageAuthenticationException($this->failMessage);
}
/**
* {@inheritdoc}
*/
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
$_SESSION['ADMIN_logged_in'] = $token->getUser()->getUsername();
if ($_SESSION['legacy_page_requested'] ?? '/'){
$url = $_SESSION['legacy_page_requested'] ?? '/';
}else{
$url = '/workflow_detailv2view.php';
}
unset($_SESSION['legacy_page_requested']);
return new RedirectResponse($url);
}
/**
* {@inheritdoc}
*/
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
$request->getSession()->set(Security::AUTHENTICATION_ERROR, $exception);
$url = $this->router->generate('app_security_login');
return new RedirectResponse($url);
}
/**
* {@inheritdoc}
*/
public function start(Request $request, AuthenticationException $authException = null)
{
$url = $this->router->generate('app_security_login');
return new RedirectResponse($url);
}
/**
* {@inheritdoc}
*/
public function supportsRememberMe()
{
return false;
}
}
Я обнаружил, что эта система работает нормально. Однако новая функция на устаревшей странице запускает 2 запроса асинхронного приложения Symfony, которые перекрываются.
В этом случае происходит следующее: первый запрос показывает 2 файла cookie сеанса.
Request URL: https://somedomain.com/system/staff_meeting/edit/1
Request Method: GET
Status Code: 200 OK
Remote Address: 222.154.225.22:443
Referrer Policy: no-referrer-when-downgrade
Cache-Control: max-age=0, must-revalidate, private
Cache-Control: no-store, no-cache, must-revalidate
Connection: Keep-Alive
Content-Type: text/html; charset=UTF-8
Date: Wed, 13 Jun 2018 21:57:19 GMT
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Keep-Alive: timeout=15, max=90
Server: Apache/2.4.6 (CentOS) mpm-itk/2.4.7-04 OpenSSL/1.0.2k-fips PHP/7.0.20
Set-Cookie: PHPSESSID=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0; path=/
Set-Cookie: PHPSESSID=tn7jhi5n2iu16le1os971vn024; path=/
Transfer-Encoding: chunked
X-Powered-By: PHP/7.0.20
и второй запрос выходит из системы:
Request URL: https://somedomain.com/system/staff_meeting/edit/1
Request Method: GET
Status Code: 302 Found
Remote Address: 222.154.225.22:443
Referrer Policy: no-referrer-when-downgrade
Cache-Control: no-store, no-cache, must-revalidate
Cache-Control: max-age=0, must-revalidate, private
Connection: Keep-Alive
Content-Length: 332
Content-Type: text/html; charset=UTF-8
Date: Thu, 14 Jun 2018 01:26:43 GMT
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Keep-Alive: timeout=15, max=90
Location: /system/security/login
Server: Apache/2.4.6 (CentOS) mpm-itk/2.4.7-04 OpenSSL/1.0.2k-fips PHP/7.0.20
Set-Cookie: PHPSESSID=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0; path=/
X-Powered-By: PHP/7.0.20
Я считаю, что это должно быть какое-то состояние гонки при доступе к переменной $ _SESSION, которую мы используем для удержания вместе унаследованной системы и приложения Symfony.
Есть идеи, как решить эту проблему?






Избегайте вызова session_start () или $ session-> start () ;! Symfony сделает это сама.
Это ответ? Сессия перезапускается при успешной аутентификации.
Дополнительные session_starts могут вызвать проблему. Я никогда не забочусь о начале сеанса, его стоит попробовать.
Может быть. Но поскольку он работает с guardAuthenticator, сеанс управляется автоматически. У меня была проблема, когда переменные сеанса, которые я установил ранее при успешной аутентификации, терялись здесь.
Мне удалось обойти проблему, установив
security.yml
security:
session_fixation_strategy: none
Однако я не уверен, как можно исправить регенерацию сеанса и соответствующее переименование значения Cookie.
Я все еще хочу услышать другие мысли по этому поводу.
внесите следующие изменения в ваши аутентификаторы, чтобы сохранить управление сеансом в symfony:
src / AppBundle / Security / LegacyTokenAuthenticator:
public function __construct(RouterInterface $router) {
$this->router = $router;
}
public function getUser($credentials, UserProviderInterface $userProvider)
{
return $userProvider->loadUserByUsername($credentials);
}
public function getCredentials(Request $request)
{
$session = $request->getSession();
if ($session->has('ADMIN_logged_in') && intval($session->get('ADMIN_logged_in'))){
return $session->get('ADMIN_logged_in');
}
return null;
}
src / AppBundle / Security / TokenAuthenticator:
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
$session = $request->getSession();
$session->set('ADMIN_logged_in', $token->getUser()->getUsername());
if ($session->has('legacy_page_requested')) {
$url = $session->get('legacy_page_requested') ?? '/';
$session->remove('legacy_page_requested');
} else {
$url = '/workflow_detailv2view.php';
}
return new RedirectResponse($url);
}
Таким образом, с помощью этого решения я не вижу, как устаревшее приложение проверяет, что кто-то вошел в систему на стороне Symfony, поскольку в $ _SESSION ничего нет. Страница входа присутствует только в Symfony
проверьте прослушиватель, предложенный в этом решении для совместного использования переменных сеанса между платформами: stackoverflow.com/questions/35601629/…
Я не совсем понимаю вашу архитектуру, но я думаю, что лучше всего, если у вас нет двух аутентификаторов Guard на одном брандмауэре.
Вы можете сделать свой сеанс Symfony устаревшим сеансом следующим образом:
# config.yml
framework:
session:
handler_id: session.handler.native_file
save_path: "/tmp/sessions/%kernel.environment%"
Отсюда любой устаревший сеанс, который у вас есть, должен управляться с помощью суперглобального $_SESSION (который часто используется в устаревшем коде).
Если вы хотите иметь разные правила входа для старой и новой системы, я рекомендую установить два разных брандмауэра. Вы можете назначить общий контекст, чтобы пользователю не приходилось повторно проходить аутентификацию, и просто добавить некоторые дополнительные правила в дочерний класс.
Для управления сеансом в Symfony лучше использовать $ request-> getSession () или использовать внедрение зависимостей и внедрить SessionInterface.