我在此form login setup doc之后建立了一个登录表单。
这在localhost 上工作正常,但在生产服务器上上却没有。
匹配的路线“ 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"} []
访问被拒绝并返回登录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"} []
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
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: { }
@Arno进行了评论,我编辑了framework.yaml
以将会话保存在var/
目录中,并且每次单击登录表单时,都可以检查是否写入了sess_文件,从而可以检查此步骤是否存在权限问题。
值得一提的是,如果我发表评论:
access_control:
- { path: ^/odelices_admin, roles: ROLE_USER}
我可以访问后台。
因此现在将会话保存到var / sessions / prod中。
sudo rm -r var/sessions/prod/sess_*
我打开Chrome和URL,它设置了一个与第一个sess_xyz文件相同的值的PHPSSID cookie:
_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";}
我进入登录页面。与新的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";}
我以正确的值登录。这将创建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";}
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? EasyAdmin?我如何确保安全令牌已存储在会话中,即使它已记录为已存储?我还尝试将access_control更改为ROLE_USER角色,以便任何经过身份验证的用户都可以访问。没办法。
我们非常感谢您的帮助。
答案 0 :(得分:2)
一年前发生在我身上,身份验证成功,重定向并以匿名身份登录。让我想把头砸在墙上。我当时遇到的问题是,我是根据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;
}
}
答案 1 :(得分:2)
结束Varnish设置错误。
我完全忘记了必须指定任何后台模式,例如 /admin/ , /backoffice/
和更多的不要由代理缓存,而是直接传递给后端。
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用户认证过程,以及如何调试它!也许这篇分步调试帖子将帮助更多的读者?
答案 2 :(得分:2)
因此,这里以结构化的方式提供了我的评论,以便它可以帮助其他在Symfony中遇到身份验证问题的人。
默认情况下,每个会话均保存为sess_<id>
中名称为<project_dir>/var/cache/<env>/sessions
或save_path
中php.ini
定义的文件,如果framework.session.handler
为设置为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%'
Cf。 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的名称由session.name
中的php.ini
定义,默认情况下为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()
方法与deserialize()
相匹配。如果实现EquatableInterface
,请确保您的isEqualTo()
方法正确运行。不过,这两个接口都是可选的,因此您可以考虑删除它们以进行调试。
答案 3 :(得分:0)
我有与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));
}
更正后,无论是否使用“记住我”选项,该应用程序都可以在开发环境和产品环境中保持登录状态。 official site解释了其背后的逻辑。