douao8353 2018-06-14 01:33
浏览 63
已采纳

Symfony自定义身份验证提供程序根据请求重叠注销

This problem has been reproduced with Symfony 3.3.17 and 3.4.9

I have a custom authenticaton provider that ties together a legacy application and a Symfony application:

app/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;
    }
}

What I find is that this system works fine. However a new feature in a legacy page is running 2 Symfony async application requests which are overlapping.

In this case what happens is the 1st request shows 2 Session 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

and the second request is being logged out:

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

I believe this must be some race condition in accessing the $_SESSION variable we use to hold together legacy system and Symfony application.

Any ideas how to resolve this issue?

  • 写回答

4条回答 默认 最新

  • dpq39825 2018-08-12 15:28
    关注

    make the following changes to your authenticators to keep the session management to 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);
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(3条)

报告相同问题?

悬赏问题

  • ¥15 自动转发微信群信息到另外一个微信群
  • ¥15 outlook无法配置成功
  • ¥30 这是哪个作者做的宝宝起名网站
  • ¥60 版本过低apk如何修改可以兼容新的安卓系统
  • ¥25 由IPR导致的DRIVER_POWER_STATE_FAILURE蓝屏
  • ¥50 有数据,怎么建立模型求影响全要素生产率的因素
  • ¥50 有数据,怎么用matlab求全要素生产率
  • ¥15 TI的insta-spin例程
  • ¥15 完成下列问题完成下列问题
  • ¥15 C#算法问题, 不知道怎么处理这个数据的转换