簡體   English   中英

Symfony 4 登錄表單:身份驗證成功,但重定向后身份驗證立即丟失

[英]Symfony 4 login form : authenticating successfully, but authentication immediately lost after redirect

我按照此 表單登錄設置文檔構建了一個登錄表單。

這在 localhost工作正常,但在生產服務器無效

在 localhost 和 prod 上,身份驗證成功開始

  1. Guard 認證成功
  2. 保護身份驗證器設置成功響應
  3. 將安全令牌存儲在會話中
  4. 匹配路由“easyadmin

     ### var/log/prod.log output with info level [2019-07-05 10:28:46] request.INFO: Matched route "app_login". {"route":"app_login","route_parameters":{"_route":"app_login","_controller":"App\\\\Controller\\\\SecurityController::login"},"request_uri":"https://example.com/login","method":"POST"} [] [2019-07-05 10:28:46] security.DEBUG: Checking for guard authentication credentials. {"firewall_key":"main","authenticators":1} [] [2019-07-05 10:28:46] security.DEBUG: Checking support on guard authenticator. {"firewall_key":"main","authenticator":"App\\\\Security\\\\LoginFormAuthenticator"} [] [2019-07-05 10:28:46] security.DEBUG: Calling getCredentials() on guard authenticator. {"firewall_key":"main","authenticator":"App\\\\Security\\\\LoginFormAuthenticator"} [] [2019-07-05 10:28:46] security.DEBUG: Passing guard token information to the GuardAuthenticationProvider {"firewall_key":"main","authenticator":"App\\\\Security\\\\LoginFormAuthenticator"} [] [2019-07-05 10:28:46] php.INFO: User Deprecated: The "Symfony\\Component\\Security\\Core\\Encoder\\BCryptPasswordEncoder" class is deprecated since Symfony 4.3, use "Symfony\\Component\\Security\\Core\\Encoder\\NativePasswordEncoder" instead. {"exception":"[object] (ErrorException(code: 0): User Deprecated: The \\"Symfony\\\\Component\\\\Security\\\\Core\\\\Encoder\\\\BCryptPasswordEncoder\\" class is deprecated since Symfony 4.3, use \\"Symfony\\\\Component\\\\Security\\\\Core\\\\Encoder\\\\NativePasswordEncoder\\" instead. at /var/www/clients/client0/web4/web/vendor/symfony/security-core/Encoder/BCryptPasswordEncoder.php:14)"} [] [2019-07-05 10:28:46] security.INFO: Guard authentication successful! {"token":"[object] (Symfony\\\\Component\\\\Security\\\\Guard\\\\Token\\\\PostAuthenticationGuardToken: PostAuthenticationGuardToken(user=\\"myemail@gmail.com\\", authenticated=true, roles=\\"ROLE_EDITOR, ROLE_USER\\"))","authenticator":"App\\\\Security\\\\LoginFormAuthenticator"} [] [2019-07-05 10:28:46] security.DEBUG: Guard authenticator set success response. {"response":"[object] (Symfony\\\\Component\\\\HttpFoundation\\\\RedirectResponse: HTTP/1.0 302 Found\\r\\nCache-Control: no-cache, private\\r\\nDate: Fri, 05 Jul 2019 10:28:46 GMT\\r\\nLocation: /backoffice\\r\\n\\r\\n<!DOCTYPE html>\\n<html>\\n <head>\\n <meta charset=\\"UTF-8\\" />\\n <meta http-equiv=\\"refresh\\" content=\\"0;url=/backoffice\\" />\\n\\n <title>Redirecting to /backoffice</title>\\n </head>\\n <body>\\n Redirecting to <a href=\\"/backoffice\\">/backoffice</a>.\\n </body>\\n</html>)","authenticator":"App\\\\Security\\\\LoginFormAuthenticator"} [] [2019-07-05 10:28:46] security.DEBUG: Remember me skipped: it is not configured for the firewall. {"authenticator":"App\\\\Security\\\\LoginFormAuthenticator"} [] [2019-07-05 10:28:46] security.DEBUG: The "App\\Security\\LoginFormAuthenticator" authenticator set the response. Any later authenticator will not be called {"authenticator":"App\\\\Security\\\\LoginFormAuthenticator"} [] [2019-07-05 10:28:46] security.DEBUG: Stored the security token in the session. {"key":"_security_main"} [] [2019-07-05 10:28:46] request.INFO: Matched route "easyadmin". {"route":"easyadmin","route_parameters":{"_controller":"Symfony\\\\Bundle\\\\FrameworkBundle\\\\Controller\\\\RedirectController::urlRedirectAction","path":"/backoffice/","permanent":true,"scheme":null,"httpPort":80,"httpsPort":443,"_route":"easyadmin"},"request_uri":"https://example.com/backoffice","method":"GET"} []

但是在本地主機中,我被正確重定向到后台:

  • 從會話中讀取現有的安全令牌
  • 用戶已從用戶提供程序重新加載

    ### var/log/prod.log (following lines, localhost) [2019-07-05 10:19:29] security.DEBUG: Read existing security token from the session. {"key":"_security_main","token_class":"Symfony\\\\Component\\\\Security\\\\Guard\\\\Token\\\\PostAuthenticationGuardToken"} [] [2019-07-05 10:19:29] security.DEBUG: User was reloaded from a user provider. {"provider":"Symfony\\\\Bridge\\\\Doctrine\\\\Security\\\\User\\\\EntityUserProvider","username":"raoux.thierry@free.fr"} [] [2019-07-05 10:19:29] security.DEBUG: Checking for guard authentication credentials. {"firewall_key":"main","authenticators":1} [] [2019-07-05 10:19:29] security.DEBUG: Checking support on guard authenticator. {"firewall_key":"main","authenticator":"App\\\\Security\\\\LoginFormAuthenticator"} [] [2019-07-05 10:19:29] security.DEBUG: Guard authenticator does not support the request. {"firewall_key":"main","authenticator":"App\\\\Security\\\\LoginFormAuthenticator"} [] [2019-07-05 10:19:29] cache.INFO: Lock acquired, now computing item "easyadmin.processed_config" {"key":"easyadmin.processed_config"} []

在生產環境中,改為:

  • 它跳過步驟:讀取現有的安全令牌
  • 沒有按預期刷新用戶
  • 相反,它使用匿名令牌填充 TokenStorage
  • 訪問被拒絕並返回登錄 url

     ### var/log/prod.log (same following lines, but from production server) [2019-07-05 10:28:46] security.DEBUG: Checking for guard authentication credentials. {"firewall_key":"main","authenticators":1} [] [2019-07-05 10:28:46] security.DEBUG: Checking support on guard authenticator. {"firewall_key":"main","authenticator":"App\\\\Security\\\\LoginFormAuthenticator"} [] [2019-07-05 10:28:46] security.DEBUG: Guard authenticator does not support the request. {"firewall_key":"main","authenticator":"App\\\\Security\\\\LoginFormAuthenticator"} [] [2019-07-05 10:28:46] security.INFO: Populated the TokenStorage with an anonymous Token. [] [] [2019-07-05 10:28:46] security.DEBUG: Access denied, the user is not fully authenticated; redirecting to authentication entry point. {"exception":"[object] (Symfony\\\\Component\\\\Security\\\\Core\\\\Exception\\\\AccessDeniedException(code: 403): Access Denied. at /var/www/clients/client0/web4/web/vendor/symfony/security-http/Firewall/AccessListener.php:72)"} [] [2019-07-05 10:28:46] security.DEBUG: Calling Authentication entry point. [] [] [2019-07-05 10:28:46] request.INFO: Matched route "app_login". {"route":"app_login","route_parameters":{"_route":"app_login","_controller":"App\\\\Controller\\\\SecurityController::login"},"request_uri":"https://example.com/login","method":"GET"} []

安全.yaml

security:
    encoders:
        App\Entity\User:
            algorithm: bcrypt
    providers:
        app_user_provider:
            entity:
                class: App\Entity\User
                property: email
    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            anonymous: true
            guard:
                authenticators:
                    - App\Security\LoginFormAuthenticator
            logout:
                path: app_logout
    access_control:
        - { path: ^/backoffice, roles: ROLE_EDITOR} # requires_channel: https

路線.yaml

admin:
  path: /backoffice
  controller: EasyCorp\Bundle\EasyAdminBundle\Controller\EasyAdminController

登錄表單驗證器

// use...

class LoginFormAuthenticator extends AbstractFormLoginAuthenticator
{
    use TargetPathTrait;

    private $entityManager;
    private $urlGenerator;
    private $csrfTokenManager;
    private $passwordEncoder;

    public function __construct(EntityManagerInterface $entityManager, UrlGeneratorInterface $urlGenerator, CsrfTokenManagerInterface $csrfTokenManager, UserPasswordEncoderInterface $passwordEncoder)
    {
        $this->entityManager = $entityManager;
        $this->urlGenerator = $urlGenerator;
        $this->csrfTokenManager = $csrfTokenManager;
        $this->passwordEncoder = $passwordEncoder;
    }

    public function supports(Request $request)
    {
        return 'app_login' === $request->attributes->get('_route')
            && $request->isMethod('POST');
    }

    public function getCredentials(Request $request)
    {
        $credentials = [
            'email' => $request->request->get('email'),
            'password' => $request->request->get('password'),
            'csrf_token' => $request->request->get('_csrf_token'),
        ];
        $request->getSession()->set(
            Security::LAST_USERNAME,
            $credentials['email']
        );

        return $credentials;
    }

    public function getUser($credentials, UserProviderInterface $userProvider)
    {
        $token = new CsrfToken('authenticate', $credentials['csrf_token']);
        if (!$this->csrfTokenManager->isTokenValid($token)) {
            throw new InvalidCsrfTokenException();
        }

        $user = $this->entityManager->getRepository(User::class)->findOneBy(['email' => $credentials['email']]);

        if (!$user) {
            // fail authentication with a custom error
            throw new CustomUserMessageAuthenticationException('Email could not be found.');
        }

        return $user;
    }

    public function checkCredentials($credentials, UserInterface $user)
    {
        return $this->passwordEncoder->isPasswordValid($user, $credentials['password']);
    }

    public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
    {
        if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) {
            return new RedirectResponse($targetPath);
        }
        return new RedirectResponse($this->urlGenerator->generate('admin'));
    }

    protected function getLoginUrl()
    {
        return $this->urlGenerator->generate('app_login');
    }
}

安全控制器

// use...

class SecurityController extends AbstractController
{
    /**
     * @Route("/login", name="app_login")
     */
    public function login(AuthenticationUtils $authenticationUtils): Response
    {
        // get the login error if there is one
        $error = $authenticationUtils->getLastAuthenticationError();
        // last username entered by the user
        $lastUsername = $authenticationUtils->getLastUsername();

        return $this->render(
          'security/login.html.twig',
          [
            'last_username' => $lastUsername,
            'error' => $error,
          ]
        );
    }

    /**
     * @Route("/logout", name="app_logout")
     * @return \Symfony\Component\HttpFoundation\RedirectResponse
     */
    public function logout()
    {
        return $this->redirectToRoute('home');
    }
}
//... skipped forgottenPassword and resetPassword methods

編輯:

php bin/console debug:config security輸出

Current configuration for extension with alias "security"
=========================================================

security:
encoders:
    App\Entity\User:
        algorithm: bcrypt
        hash_algorithm: sha512
        key_length: 40
        ignore_case: false
        encode_as_base64: true
        iterations: 5000
        cost: null
        memory_cost: null
        time_cost: null
        threads: null
providers:
    app_user_provider:
        entity:
            class: App\Entity\User
            property: email
            manager_name: null
firewalls:
    dev:
        pattern: ^/(_(profiler|wdt)|css|images|js)/
        security: false
        methods: {  }
        user_checker: security.user_checker
        stateless: false
        logout_on_user_change: true
    main:
        anonymous:
            secret: null
        guard:
            authenticators:
                - App\Security\LoginFormAuthenticator
            entry_point: null
        logout:
            path: app_logout
            csrf_parameter: _csrf_token
            csrf_token_id: logout
            target: /
            invalidate_session: true
            delete_cookies: {  }
            handlers: {  }
        methods: {  }
        security: true
        user_checker: security.user_checker
        stateless: false
        logout_on_user_change: true
access_control:
    -
        path: ^/backoffice
        roles:
            - ROLE_EDITOR
        requires_channel: null
        host: null
        port: null
        ips: {  }
        methods: {  }
        allow_if: null
access_decision_manager:
    strategy: affirmative
    allow_if_all_abstain: false
    allow_if_equal_granted_denied: true
access_denied_url: null
session_fixation_strategy: migrate
hide_user_not_found: true
always_authenticate_before_granting: false
erase_credentials: true
role_hierarchy: {  }

編輯 2

AS @Arno 評論說,我編輯了framework.yaml以將會話保存在var/目錄中,我可以檢查此步驟是否在沒有權限問題的情況下工作,每次點擊登錄表單時,都會寫入一個 sess_ 文件。

值得說的是,如果我評論:

access_control:
    - { path: ^/odelices_admin, roles: ROLE_USER}

我可以訪問后台。

編輯 3:會話行為

所以現在會話被保存到 var/sessions/prod 中。

  1. 我清理目錄: sudo rm -r var/sessions/prod/sess_*
  2. 我打開 Chrome 和 url,它設置了一個 PHPSSID cookie,其值與第一個 sess_xyz 文件相同:

     _sf2_attributes|a:2:{s:19:"_csrf/https-contact";s:43:"Oq-QpN21bI_BUDcVbv0ocyrYsTzQo3aJr80QAk2AR7w";s:19:"_csrf/https-booking";s:43:"z_L4TG7Wg0jydwl5VabfJMx0NBhQgeasuAiqxksLvD8";}_sf2_meta|a:3:{s:1:"u";i:1562668584;s:1:"c";i:1562668584;s:1:"l";s:1:"0";}
  3. 我去登錄頁面。 與新 sess_xyz 文件關聯的新 PHPSSID 值:

     _sf2_attributes|a:1:{s:24:"_csrf/https-authenticate";s:43:"erWMU-irtptcZodr8UOjFtxiuyE23LbAeFHRnXgcNdc";}_sf2_meta|a:3:{s:1:"u";i:1562668662;s:1:"c";i:1562668662;s:1:"l";s:1:"0";}
  4. 我使用正確的值登錄。 這將創建 3 個新的 ssid_xyz 文件。

     # 1st one shows user logged in with correct roles and so on _sf2_attributes|a:3:{s:24:"_csrf/https-authenticate";s:43:"erWMU-irtptcZodr8UOjFtxiuyE23LbAeFHRnXgcNdc";s:23:"_security.last_username";s:21:"user_email@gmail.com";s:14:"_security_main";s:799:"C:67:"Symfony\\Component\\Security\\Guard\\Token\\PostAuthenticationGuardToken":718:{a:2:{i:0;s:4:"main";i:1;a:5:{i:0;O:15:"App\\Entity\\User":6:{s:19:"^@App\\Entity\\User^@id";i:1;s:22:"^@App\\Entity\\User^@email";s:21:"user_email@gmail.com";s:22:"^@App\\Entity\\User^@roles";a:1:{i:0;s:11:"ROLE_EDITOR";}s:25:"^@App\\Entity\\User^@password";s:60:"$2y$13$cXaR7Ss.kTH1U.T/Rzi6m.ALsKwWCLDcO5/OIeRDAq02iylmf4us6";s:21:"^@App\\Entity\\User^@name";s:7:"Thierry";s:13:"^@*^@resetToken";N;}i:1;b:1;i:2;a:2:{i:0;O:41:"Symfony\\Component\\Security\\Core\\Role\\Role":1:{s:47:"^@Symfony\\Component\\Security\\Core\\Role\\Role^@role";s:11:"ROLE_EDITOR";}i:1;O:41:"Symfony\\Component\\Security\\Core\\Role\\Role":1:{s:47:"^@Symfony\\Component\\Security\\Core\\Role\\Role^@role";s:9:"ROLE_USER";}}i:3;a:0:{}i:4;a:2:{i:0;s:11:"ROLE_EDITOR";i:1;s:9:"ROLE_USER";}}}}";}_sf2_meta|a:3:{s:1:"u";i:1562668713;s:1:"c";i:1562668713;s:1:"l";s:1:"0";} # 2nd one ...is empty # 3rd one refers to backoffice url _sf2_attributes|a:1:{s:26:"_security.main.target_path";s:42:"https://mywebsite.com/backoffice";}_sf2_meta|a:3:{s:1:"u";i:1562668713;s:1:"c";i:1562668713;s:1:"l";s:1:"0";} # last one is similar to point 3, before logging, only ssid value differs, and a corresponding cookie is set on Chrome _sf2_attributes|a:1:{s:24:"_csrf/https-authenticate";s:43:"3UC5dCRrahc2qhdZ167Jg4HKTJCexf8PFlefibTVpYk";}_sf2_meta|a:3:{s:1:"u";i:1562668713;s:1:"c";i:1562668713;s:1:"l";s:1:"0";}

編輯 4:用戶實體

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
// use Symfony\Component\Security\Core\User\EquatableInterface;
use Symfony\Component\Security\Core\User\UserInterface;

/**
 * @ORM\Entity(repositoryClass="App\Repository\UserRepository")
 */
class User implements UserInterface # , EquatableInterface
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=180, unique=true)
     */
    private $email;

    /**
     * @ORM\Column(type="json")
     */
    private $roles = [];

    /**
     * @var string The hashed password
     * @ORM\Column(type="string")
     */
    private $password;

    /**
     * @ORM\Column(type="string", length=255)
     */
    private $name;

    /**
     * @var string le token qui servira lors de l'oubli de mot de passe
     * @ORM\Column(type="string", length=255, nullable=true)
     */
    protected $resetToken;

  /*public function __construct($username, $password, array $roles)
  {
    $this->username = $username;
    $this->password = $password;
    $this->roles = $roles;
  }*/


    public function getId(): ?int
    {
        return $this->id;
    }

    public function getEmail(): ?string
    {
        return $this->email;
    }

    public function setEmail(string $email): self
    {
        $this->email = $email;

        return $this;
    }

    /**
     * A visual identifier that represents this user.
     *
     * @see UserInterface
     */
    public function getUsername(): string
    {
        return (string) $this->email;
    }

    /**
     * @see UserInterface
     */
    public function getRoles(): array
    {
        $roles = $this->roles;
        // guarantee every user at least has ROLE_USER
        $roles[] = 'ROLE_USER';

        return array_unique($roles);
    }

    public function setRoles(array $roles): self
    {
        $this->roles = $roles;

        return $this;
    }

    /**
     * @see UserInterface
     */
    public function getPassword(): string
    {
        return (string) $this->password;
    }

    public function setPassword(string $password): self
    {
        $this->password = $password;

        return $this;
    }

    /**
     * @see UserInterface
     */
    public function getSalt()
    {
        // not needed when using the "bcrypt" algorithm in security.yaml
    }

    /**
     * @see UserInterface
     */
    public function eraseCredentials()
    {
        // If you store any temporary, sensitive data on the user, clear it here
        // $this->plainPassword = null;
    }

    public function getName(): ?string
    {
        return $this->name;
    }

    public function setName(string $name): self
    {
        $this->name = $name;

        return $this;
    }

    /**
     * @return string
     */
    public function getResetToken(): string
    {
      return $this->resetToken;
    }

    /**
     * @param string $resetToken
     */
    public function setResetToken(?string $resetToken): void
    {
      $this->resetToken = $resetToken;
    }

    public function __toString() {
      return $this->getName() ;
    }

/*    public function isEqualTo(UserInterface $user)
    {

      if ($this->password !== $user->getPassword()) {
        return false;
      }


      if ($this->email !== $user->getUsername()) {
        return false;
      }

      return true;
    }*/
}

Debian Stretch、 Nginx + Varnish :Nginx 處理 443 個請求,將它們作為緩存代理傳遞給 Varnish,后者將緩存的對象或通過 8083 端口將請求傳遞給 nginx 后端。 這對於具有類似登錄邏輯的另一個應用程序來說就像一個魅力(唯一的區別是有問題的應用程序重定向到 easyadmin 而不是自定義管理員),所以我認為它與堆棧無關。

虛擬主機

server { # this block only redirects www to non www
        listen aaa.bbb.ccc.ddd:443 ssl;
        server_name www.somewebsite.com;

        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_certificate /var/www/clients/client0/web4/ssl/somewebsite.com-le.crt;
        ssl_certificate_key /var/www/clients/client0/web4/ssl/somewebsite.com-le.key;

        return 301 https://somewebsite.com$request_uri;
}

server { # this block redirects ssl requests to Varnish
        listen aaa.bbb.ccc.ddd:443 ssl;
        server_name somewebsite.com;

        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_certificate /var/www/clients/client0/web4/ssl/somewebsite.com-le.crt;
        ssl_certificate_key /var/www/clients/client0/web4/ssl/somewebsite.com-le.key;

        location / {
            # Pass the request on to Varnish.
            proxy_pass  http://127.0.0.1;

            # Pass some headers to the downstream server, so it can identify the host.
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

            # Tell any web apps that the session is HTTPS.
            proxy_set_header X-Forwarded-Proto https;
            proxy_redirect     off;
        }
}

server { # now sent to backend 
        listen aaa.bbb.ccc.ddd:8083;
        server_name somewebsite.com;
        root   /var/www/somewebsite.com/web/public;

        location / {
            try_files $uri /index.php$is_args$args;
       }
       location ~ ^/index\.php(/|$) {
            fastcgi_pass 127.0.0.1:8998;

            fastcgi_split_path_info ^(.+\.php)(/.*)$;
            include fastcgi_params;

            fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
            fastcgi_param DOCUMENT_ROOT $realpath_root;

            internal;
        }
        location ~ \.php$ {
            return 404;
        }

        error_log /var/log/ispconfig/httpd/somewebsite.com/error.log;
        access_log /var/log/ispconfig/httpd/somewebsite.com/access.log combined;

        location ~ /\. {
                        deny all;
        }
        location ^~ /.well-known/acme-challenge/ {
                        access_log off;
                        log_not_found off;
                        root /usr/local/ispconfig/interface/acme/;
                        autoindex off;
                        try_files $uri $uri/ =404;
        }
        location = /favicon.ico {
            log_not_found off;
            access_log off;
            expires max;
        }
        location = /robots.txt {
            allow all;
            log_not_found off;
            access_log off;
        }
}

這可能與某些目錄的權限有關嗎? HTTPS? 易管理員? 我如何確保安全令牌存儲在會話中,即使它被記錄為已存儲? 我還嘗試將 access_control 更改為角色 ROLE_USER 以便任何經過身份驗證的用戶都可以訪問。 沒門。

任何幫助都非常感謝。

所以這里是我以更結構化的方式發表的評論,以便它可以幫助其他人在 Symfony 中遇到身份驗證問題。

確保會話已保存

默認情況下,每個會話都保存為<project_dir>/var/cache/<env>/sessions名為sess_<id>的文件,或者如果framework.session.handler設置為php.inisave_path定義null 明確配置您的會話目錄並確保在您登錄時創建了一個會話文件。如果沒有,請檢查該文件夾的權限。

# app/config/config.yml (Symfony 3)
# config/packages/framework.yaml (Symfony 4)
framework:
    session:
        handler_id: 'session.handler.native_file'
        save_path: '%kernel.project_dir%/var/sessions/%kernel.environment%'

參見 https://symfony.com/doc/current/session.html#configuration

確保會話正確並使用

當您登錄時,應創建一個具有新 ID 的會話。 檢查文件的內容。 它應該在防火牆名稱(例如 main)下包含您的序列化用戶,包括您的標識符(例如電子郵件)和您的用戶角色(例如 ROLE_USER)。 這里的問題可能是由錯誤的身份驗證、安全配置或序列化引起的。

_sf2_attributes|a:3:{s:18:"_csrf/authenticate";s:43:"n2oap401u4P4O7m_IhPODZ6Bz7EHl-DDsHxBEl-fhxc";s:23:"_security.last_username";s:10:"foo@bar.de";s:14:"_security_main";s:545:"C:67:"Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken":464:{a:2:{i:0;s:4:"main";i:1;a:5:{i:0;O:15:"App\Entity\User":4:{s:19:"App\Entity\Userid";i:1;s:22:"App\Entity\Useremail";s:10:"foo@bar.de";s:22:"App\Entity\Userroles";a:0:{}s:25:"App\Entity\Userpassword";s:60:"$2y$13$qwbtasafa58lPonX6B5a9eV4lziF7EZWP8NFLAe3blpCJVhQgPVOS";}i:1;b:1;i:2;a:1:{i:0;O:41:"Symfony\Component\Security\Core\Role\Role":1:{s:47:"Symfony\Component\Security\Core\Role\Rolerole";s:9:"ROLE_USER";}}i:3;a:0:{}i:4;a:1:{i:0;s:9:"ROLE_USER";}}}}";}_sf2_meta|a:3:{s:1:"u";i:1563015142;s:1:"c";i:1563015142;s:1:"l";s:1:"0";}

登錄時檢查瀏覽器中是否設置了具有相同 ID 的 cookie。 cookie 的名稱由php.inisession.name定義,默認情況下它是PHPSESSID 它應該與您提出的每個請求一起發送(例如Cookie: PHPSESSID=lpcf79ff8jdv2iigsgvepnr9bb )。 如果存在正確的會話,但您的瀏覽器中有不同的 cookie,則您可能會在成功重定向后立即注銷。

確保用戶正確刷新

會話 ID 應僅在您的用戶更改時(例如,登錄和注銷時)更改。 如果它在正常請求后發生變化(例如,您立即退出)或者您的會話似乎被忽略,則問題可能是 Symfony 認為您的用戶已更改。 這可能是由錯誤的(反)序列化或比較引起的。

默認情況下,Symfony 使用會話中getPassword()getUsername()getSalt()的序列化結果來與用戶提供者(例如數據庫)提供的用戶進行比較。 如果這些值中的任何一個發生變化,您將被注銷(參見https://symfony.com/doc/current/security/user_provider.html#understanding-how-users-are-refreshed-from-the-session )。

因此,您應該確保例如您的數據庫提供的用戶是正確的,並且與會話中的反序列化用戶相匹配。 確保相關字段正確序列化。 如果您實現了Serializable接口,請確保您的serialize()方法與您的unserialize()匹配。 如果您實現EquatableInterface ,請確保您的isEqualTo()方法正常工作。 不過,這兩個接口都是可選的,因此您可以考慮刪除它們以進行調試。

一年前發生在我身上,身份驗證成功,重定向並以匿名身份登錄。 讓我想用頭撞牆。 我當時遇到的問題是我根據 KnpUniversity 的舊課程創建了用戶,但它沒有實現 EquatableInterface 和 isEqualTo 方法。 希望它對你有用。

確保您的用戶實體實現 EquatableInterface

namespace App\Entity;

use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\EquatableInterface;

    class User implements UserInterface, EquatableInterface
{
    private $username;
    private $password;
    private $salt;
    private $roles;

    public function __construct($username, $password, $salt, array $roles)
    {
        $this->username = $username;
        $this->password = $password;
        $this->salt = $salt;
        $this->roles = $roles;
    }

    // your setters, getters and whatever ...

    public function isEqualTo(UserInterface $user)
    {
        if (!$user instanceof WebserviceUser) {
            return false;
        }

        if ($this->password !== $user->getPassword()) {
            return false;
        }

        if ($this->salt !== $user->getSalt()) {
            return false;
        }

        if ($this->username !== $user->getUsername()) {
            return false;
        }

        return true;
    }
}

它結束了清漆設置是錯誤的。

我完全忘記了我必須指定任何后台模式,例如/admin/ , /backoffice/不要被代理緩存,而是直接傳遞給 backend

sub vcl_recv {
    # ...
    if (req.url ~ "^/status\.php$" ||
        req.url ~ "^/update\.php$" ||
        req.url ~ "^/install\.php" ||
        req.url ~ "^/admin$" ||
        req.url ~ "^/admin/.*$" ||
        req.url ~ "^/flag/.*$" ||
        req.url ~ "^.*/ajax/.*$" ||
        req.url ~ "^.*/ahah/.*$" ||
        req.url ~ "^/info/.*$" ||
        req.url ~ "^/system/files/.*$" ||
        req.url ~ "^/user" ||
        req.url ~ "^/users/.*$" ||
        req.url ~ "^/user/.*$" ) {

       return (pass);
    }
    # ...

這已經為我在問題中提到的另一個 symfony 應用程序和幾個 Drupal 網站設置了。 至少它迫使我深入挖掘Symfony 4 用戶身份驗證過程,以及如何調試它! 也許這一步一步的調試帖子會幫助更多的讀者?!

我有與 OP 描述的相同的症狀,但在 Symfony 5.1 應用程序中並使用我自己現有的用戶實體。 原來問題是我沒有在我的 User 實體和 2 個必需的方法上實現 \\Serializable :

public function serialize()
{
    return serialize(array(
        $this->id,
        $this->username,
        $this->password,
        // see section on salt below
        // $this->salt,
    ));
}

/** @see \Serializable::unserialize() */
public function unserialize($serialized)
{
    list (
        $this->id,
        $this->username,
        $this->password,
        // see section on salt below
        // $this->salt
    ) = unserialize($serialized, array('allowed_classes' => false));
}

一旦糾正,該應用程序能夠在開發和生產環境中保持登錄狀態,無論是否使用“記住我”選項。 其背后的邏輯在官方網站上有解釋。

我的問題非常相似。 我根據Security (Symfony Docs)在 Symfony 6.0 中設置了安全性。 提交登錄表單並成功驗證后,我被重定向而沒有將會話保存為匿名用戶。

第二天,我終於嘗試使用https訪問我的網站,瞧,我登錄了。 所以登錄時嘗試使用https

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM