dongzhan2461 2016-11-01 08:07
浏览 66
已采纳

在Symfony的FOSUserBundle中检测重复登录

i'm using the FOSUserBundle in my Symfony project and i need to remove duplicate logins. If a user logs in from a different system, I want his/her other session to be disconnected.

Can you please help me on my way?

  • 写回答

2条回答 默认 最新

  • doujuyang1764 2016-11-01 09:38
    关注

    The easy way:

    Map an activeSessionId field to your User class:

    /**
     * @ORM\Entity
     * @ORM\Table(name="fos_user")
     */
    class User extends BaseUser
    {
        /**
         * @ORM\Id
         * @ORM\Column(type="integer")
         * @ORM\GeneratedValue(strategy="AUTO")
         */
        protected $id;
    
        /**
         * @ORM\Column(type="string", length=255, nullable=true)
         */
        protected $activeSessionId;
    
        public function loginWithSessId($sessionId)
        {
            $this->activeSessionId = $sessionId;
        }
    
        public function logout()
        {
            $this->activeSessionId = null;
        }
    
        public function getActiveSessId()
        {
            return $this->activeSessionId;
        }
    }
    

    Then listen to the security.interactive_login event that will be fired every time the user log in, and save a reference of the session id together with the user:

    namespace AppBundle\Security;
    
    use Symfony\Component\EventDispatcher\EventSubscriberInterface;
    use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
    use Symfony\Component\Security\Http\SecurityEvents;
    use FOS\UserBundle\Model\UserManagerInterface;
    
    class LoginListener implements EventSubscriberInterface
    {
        private $userManager;
    
        public function __construct(UserManagerInterface $userManager)
        {
            $this->userManager = $userManager;
        }
    
        public static function getSubscribedEvents()
        {
            return array(
                SecurityEvents::INTERACTIVE_LOGIN => 'onSecurityInteractiveLogin',
            );
        }
    
        public function onSecurityInteractiveLogin(InteractiveLoginEvent $event)
        {
            $user = $event->getAuthenticationToken()->getUser();
            $session = $event->getRequest()->getSession();
    
            $user->loginWithSessId($session->getId());
            $this->userManager->updateUser($user);
        }
    }
    

    You can then register the listener with:

    <service id="app_bundle.security.login_listener" class="AppBundle\Security\LoginListener">
        <argument type="service" id="fos_user.user_manager"/>
        <tag name="kernel.event_subscriber" />
    </service>
    

    or

    # app/config/services.yml
    services:
        app_bundle.security.login_listener:
            class: AppBundle\Security\LoginListener
            arguments: ['@fos_user.user_manager']
            tags:
                - { name: kernel.event_subscriber }
    

    Now that your User entity know which session is the last one, you can creare a listener to the security.authentication.success event, and check if the current session id match with the last active one. If it doesn't, then it's not an active session anymore.

    namespace AppBundle\Security;
    
    use Symfony\Component\Security\Core\AuthenticationEvents;
    use Symfony\Component\EventDispatcher\EventSubscriberInterface;
    use Symfony\Component\Security\Core\Event\AuthenticationEvent;
    use Symfony\Component\HttpFoundation\RequestStack;
    use FOS\UserBundle\Model\UserManagerInterface;
    
    class AuthenticationListener implements EventSubscriberInterface
    {
        private $requestStack;
        private $userManager;
    
        public function __construct(RequestStack $requestStack, UserManagerInterface $userManager)
        {
            $this->requestStack = $requestStack;
            $this->userManager = $userManager;
        }
    
        public static function getSubscribedEvents()
        {
            return array(
                AuthenticationEvents::AUTHENTICATION_SUCCESS => 'onAuthenticationSuccess',
            );
        }
    
        public function onAuthenticationSuccess(AuthenticationEvent $event)
        {
            $token = $event->getAuthenticationToken();
            $sessionId = $this->requestStack->getMasterRequest()->getSession()->getId();
            $activeSessId = $token->getUser()->getActiveSessId();
    
            if ($activeSessId && $sessionId !== $activeSessId) {
                $token->setAuthenticated(false); // Sets the authenticated flag.
            }
        }
    }
    

    Finally:

    <service id="app_bundle.security.auth_listener" class="AppBundle\Security\AuthenticationListener">
        <argument type="service" id="request_stack"/>
        <argument type="service" id="fos_user.user_manager"/>
        <tag name="kernel.event_subscriber" />
    </service>
    

    or

    # app/config/services.yml
    services:
        app_bundle.security.auth_listener:
            class: AppBundle\Security\AuthenticationListener
            arguments: ['@request_stack', '@fos_user.user_manager']
            tags:
                - { name: kernel.event_subscriber }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 oracle集群安装出bug
  • ¥15 关于#python#的问题:自动化测试
  • ¥20 问题请教!vue项目关于Nginx配置nonce安全策略的问题
  • ¥15 教务系统账号被盗号如何追溯设备
  • ¥20 delta降尺度方法,未来数据怎么降尺度
  • ¥15 c# 使用NPOI快速将datatable数据导入excel中指定sheet,要求快速高效
  • ¥15 再不同版本的系统上,TCP传输速度不一致
  • ¥15 高德地图点聚合中Marker的位置无法实时更新
  • ¥15 DIFY API Endpoint 问题。
  • ¥20 sub地址DHCP问题