繁体   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