When are user roles refreshed and how to force it?

后端 未结 8 1029
梦谈多话
梦谈多话 2020-12-14 02:57

First off, I\'m not using FOSUserBundle and I can\'t because I\'m porting a legacy system which has its own Model layer (no Doctrine/Mongo/whatsoever here) and other very cu

相关标签:
8条回答
  • 2020-12-14 03:38

    I achieve this behaviour by implementing my own EntityUserProvider and overriding loadByUsername($username) method :

       /**
        * Load an user from its username
        * @param string $username
        * @return UserInterface
        */
       public function loadUserByUsername($username)
       {
          $user = $this->repository->findOneByEmailJoinedToCustomerAccount($username);
    
          if (null === $user)
          {
             throw new UsernameNotFoundException(sprintf('User "%s" not found.', $username));
          }
    
          //Custom function to definassigned roles to an user
          $roles = $this->loadRolesForUser($user);
    
          //Set roles to the user entity
          $user->setRoles($roles);
    
          return $user;
       }
    

    The trick is to call setRoles each time you call loadByUsername ... Hope it helps

    0 讨论(0)
  • 2020-12-14 03:38

    I've been battling this for Symfony4, and I think I've finally settled down to a solution.

    The thing is that in my case, the roles depend on the "company" the user is working with. It may be a CEO in one company, but an operator in another one, and the menus, permissions, etc. depend on the company. When switching companies, the user must not re-login.

    Finally I've done the following:

    • Set the firewall to stateless.
    • In the FormAuthentication class, I set an attribute in the session explicitely, with the username.
    • I set up another Guard, which essentially take this attribute and loads the user for it from the database, for every single request.
    class FormAuthenticator extends AbstractFormLoginAuthenticator
    {
        /** Constructor omitted */
    
        public function supports(Request $request)
        {
            return 'app_login' === $request->attributes->get('_route')
                && $request->isMethod('POST');
        }
    
        public function getCredentials(Request $request)
        {
            $credentials = [
                'nomusuari' => $request->request->get('nomusuari'),
                'password' => $request->request->get('password'),
                'csrf_token' => $request->request->get('_csrf_token'),
            ];
            $request->getSession()->set(
                Security::LAST_USERNAME,
                $credentials['nomusuari']
            );
    
            return $credentials;
        }
    
        public function getUser($credentials, UserProviderInterface $userProvider)
        {
            $token = new CsrfToken('authenticate', $credentials['csrf_token']);
            if (!$this->csrfTokenManager->isTokenValid($token)) {
                throw new InvalidCsrfTokenException();
            }
    
            $user = $userProvider->loadUserByUsername($credentials['nomusuari']);
    
            if (!$user) {
                // fail authentication with a custom error
                throw new CustomUserMessageAuthenticationException('Invalid user/password');
            }
    
            return $user;
        }
    
        public function checkCredentials($credentials, UserInterface $user)
        {
            $valid = $this->passwordEncoder->isPasswordValid($user, $credentials['password']);
            return $valid;
        }
    
        public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
        {
            $request->getSession()->set("user_username",$token->getUsername());
    
            return new RedirectResponse(
              $this->urlGenerator->generate("main")
            );
        }
    
        protected function getLoginUrl()
        {
            return $this->urlGenerator->generate('app_login');
        }
    }
    

    The SessionAuthenticator (returns JSON, you may have to adapt it):

    class SessionAuthenticator extends AbstractGuardAuthenticator
    {
        /**
         * Called on every request to decide if this authenticator should be
         * used for the request. Returning `false` will cause this authenticator
         * to be skipped.
         */
        public function supports(Request $request)
        {
            return $request->getSession()->has("user_username");
        }
    
        /**
         * Called on every request. Return whatever credentials you want to
         * be passed to getUser() as $credentials.
         */
        public function getCredentials(Request $request)
        {
            return $request->getSession()->get("user_username","");
        }
    
        public function getUser($credentials, UserProviderInterface $userProvider)
        {
            if (null === $credentials) {
                // The token header was empty, authentication fails with HTTP Status
                // Code 401 "Unauthorized"
                return null;
            }
    
            // if a User is returned, checkCredentials() is called
            /*return $this->em->getRepository(User::class)
                ->findOneBy(['apiToken' => $credentials])
            ;*/
            return $userProvider->loadUserByUsername($credentials);
        }
    
        public function checkCredentials($credentials, UserInterface $user)
        {
            // Check credentials - e.g. make sure the password is valid.
            // In case of an API token, no credential check is needed.
    
            // Return `true` to cause authentication success
            return true;
        }
    
        public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
        {
            // on success, let the request continue
            return null;
        }
    
        public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
        {
            $data = [
                // you may want to customize or obfuscate the message first
                'message' => strtr($exception->getMessageKey(), $exception->getMessageData())
    
                // or to translate this message
                // $this->translator->trans($exception->getMessageKey(), $exception->getMessageData())
            ];
    
            return new JsonResponse($data, Response::HTTP_UNAUTHORIZED);
        }
    
        /**
         * Called when authentication is needed, but it's not sent
         */
        public function start(Request $request, AuthenticationException $authException = null)
        {
            $data = [
                // you might translate this message
                'message' => 'Authentication Required'
            ];
    
            return new JsonResponse($data, Response::HTTP_UNAUTHORIZED);
        }
    
        public function supportsRememberMe()
        {
            return false;
        }
    }
    

    Finally, my security.yaml:

    main:
                anonymous:
                stateless: true
                guard:
                    entry_point: App\Security\FormAuthenticator
                    authenticators:
                        - App\Security\SessionAuthenticator
                        - App\Security\FormAuthenticator
    

    Working fine. I can see the changes in the toolbar, and the Roles are refreshed.

    HTH,

    Esteve

    0 讨论(0)
提交回复
热议问题