Я создал форму входа после этого документ настройки входа в форму.
Это отлично работает на локальном хосте но не на рабочем сервере.
Соответствующий маршрут "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=\"[email protected]\", 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":"[email protected]"} []
[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-адрес, он устанавливает файл cookie PHPSSID с тем же значением, что и первый файл 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";}
Я захожу на страницу входа. Новое значение PHPSSID, связанное с новым файлом sess_xyz:
_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:"[email protected]";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:"[email protected]";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 + Лак: Nginx обрабатывает 443 запроса, передает их в Varnish в качестве кеш-прокси, который доставляет кэшированные объекты или передает запросы на серверную часть nginx через порт 8083. Это работает как шарм для другого приложения с аналогичной логикой входа (единственное отличие состоит в том, что глючное перенаправляет на 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, чтобы любой пользователь, прошедший проверку подлинности, имел доступ. Ни за что.
Любая помощь очень ценится.
Вы чистили кеш на сервере после загрузки?
Только что опубликовал php bin/console debug:config security вывод. Да, я ищу проблему со вчерашнего вечера и очищал кеш около 100 раз, проверял проблемы с разрешениями...
Используете ли вы настройки по умолчанию для сохранения сеансов (настроенные в framework.session)? Если это так, сеансы сохраняются в виде файлов, и это определенно может быть проблемой с правами доступа. По умолчанию они должны быть сохранены в папке «sessions» в вашем каталоге кеша, но вы можете изменить это, как описано здесь: symfony.com/doc/3.4/session/sessions_directory.html. Может быть, вы можете попробовать дать каталогу права 777. Он должен создавать один файл на сеанс в формате «sess_<id>» с сериализованным пользователем внутри.
@ArnoHilke Большое спасибо за эту подсказку. Ну, я переключился на 4.3 документ, затем я изменил свою настройку, чтобы явно сохранять сеансы в var dir, и пока сеанс записан там, так что, к сожалению, это не точка блокировки :((
Как насчет того факта, что ssid получает новое значение каждый раз, когда я нажимаю URL-адрес (страница входа или нет) ?? Это нормальное поведение???
@Kojo Идентификатор сеанса должен меняться каждый раз, когда изменяется ваш уровень аутентификации, то есть при входе и выходе из системы, но не при обычных запросах. Одной из причин может быть то, что ваш пользователь изменился или был обнаружен как измененный, например. из-за ошибочной (де)сериализации. Вы также можете проверить 1) создается ли правильный сеанс при входе в систему (проверьте содержимое файла для пользователя с правильным адресом электронной почты и ролью) и 2) правильно ли установлен файл cookie и отправлен ли он с этим идентификатором сеанса на вашем клиенте. .
@ArnoHilke еще раз спасибо! Я отредактировал пост со всем поведением. Пожалуйста, посмотрите, да, файлы cookie установлены в отношении файлов ssid, но всякий раз, когда выполняется перенаправление, вернуться к началу ... Мне нужно посетить несколько собраний команды сегодня днем, я вернусь после ... Я действительно не знаю что думать :-((
мы можем увидеть вашу сущность пользователя, пожалуйста?
Также убедитесь, что в вашем брандмауэре нет опции stateless: true. Это заблокирует запуск сеанса для пользователя, и все останутся анонимными после успешной аутентификации.




Случилось со мной год назад, аутентификация прошла успешно, перенаправление и вход в систему как анонимный. Заставь меня хотеть биться головой о стены. Проблема, с которой я столкнулся тогда, заключалась в том, что я создал пользователя по старому курсу из 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;
}
}
Спасибо, Йоанн. Это не меняет поведения. :-(( Я собираюсь опубликовать User Entity
Вам может быть интересно: я сделал эту проверку, пытаясь реализовать EquatableInterface if ($this->email !== $user->getUsername()), потому что getUsername возвращает адрес электронной почты, установленный в провайдере security.yaml... это работает в localhost, а не в prod
это странно и может быть связано с вашей проблемой
Насколько я понимаю, по умолчанию пользователь обновляется, сравнивая последний сериализованный с текущим, используя getSalt(), getPassword() и getUsername() из объекта пользователя. getUsername — это нетgetName(), он должен возвращать уникальное значение, и вы определяете, какое именно, в свойстве объекта security.yaml -> provider. ты не согласен? Ваша реализация действительно отлично работает с моей конфигурацией, но не было причин добавлять эту проверку в мою столь базовую конфигурацию. В любом случае, благодаря вам я узнал отличный новый способ настройки этого шага!
Это заканчивается тем, что установка Varnish была ошибочной.
Я совсем забыл, что я должен указать любой паттерн backoffice типа /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, и как его отладить! Может быть, этот отладочный пост шаг за шагом поможет дальнейшим читателям?!
Итак, вот мои комментарии в более структурированном виде, чтобы они могли помочь кому-то еще, у кого проблемы с аутентификацией в 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%'
См. https://symfony.com/doc/current/session.html#configuration
При входе в систему должна быть создана сессия с новым идентификатором. Проверьте содержимое файла. Он должен содержать вашего сериализованного пользователя под именем брандмауэра (например, main), включая ваш идентификатор (например, адрес электронной почты) и ваши роли пользователя (например, ROLE_USER). Проблема здесь может быть вызвана ошибочной аутентификацией, конфигурацией безопасности или сериализацией.
_sf2_attributes|a:3:{s:18:"_csrf/authenticate";s:43:"n2oap401u4P4O7m_IhPODZ6Bz7EHl-DDsHxBEl-fhxc";s:23:"_security.last_username";s:10:"[email protected]";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:"[email protected]";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";}
Убедитесь, что файл cookie с таким же идентификатором установлен в вашем браузере при входе в систему. Имя файла cookie определяется session.name в вашем php.ini, по умолчанию это PHPSESSID. Его следует отправлять с каждым вашим запросом (например, Cookie: PHPSESSID=lpcf79ff8jdv2iigsgvepnr9bb). Если правильный сеанс существует, но в вашем браузере есть другой файл cookie, возможно, вы сразу же вышли из системы после успешного перенаправления.
Идентификатор сеанса должен меняться только при изменении вашего пользователя (например, при входе в систему и выходе из нее). Если он меняется после обычных запросов (например, вы немедленно выходите из системы) или ваша сессия кажется проигнорированной, проблема может заключаться в том, что Symfony считает, что ваш пользователь изменился. Это может быть вызвано ошибочной (де)сериализацией или сравнением.
По умолчанию Symfony использует сериализованные результаты getPassword(), getUsername() и getSalt() из сеанса для сравнения с пользователем, предоставленным провайдером пользователя (например, базой данных). Если какое-либо из этих значений изменится, вы выйдете из системы (см. https://symfony.com/doc/current/security/user_provider.html#understanding-how-users-refreshed-from-the-session).
Таким образом, вы должны убедиться, что пользователь, предоставленный, например. ваша база данных верна и соответствует десериализованному пользователю из сеанса. Убедитесь, что соответствующие поля правильно сериализованы. Если вы реализуете интерфейс Serializable, убедитесь, что ваш метод serialize() соответствует вашему unserialize(). Если вы реализуете EquatableInterface, убедитесь, что ваш метод isEqualTo() работает правильно. Оба эти интерфейса являются необязательными, поэтому вы можете удалить их в целях отладки.
У меня сработали "isEqualTo()" и методы сериализации, спасибо!
Меня удивило, что hasUserChanged in Symfony\Component\Security\Core\Authentication\Token\AbstractToken вызывается во время обновления. Это сработало для меня, внедрив EquatableInterface в мою сущность User и определив isEqualTo.
@Julian Как упоминалось в документы, на который я также ссылался выше, они говорят, что это из соображений безопасности, чтобы убедиться, что десериализованный пользователь по-прежнему соответствует только что запрошенному пользователю. В некоторых более старых документы они также заявляют, что теоретически было бы достаточно просто сохранить идентификатор в сеансе и использовать его для запроса пользователя, поэтому я не совсем уверен, почему это было сделано именно так.
У меня был тот же симптом, который был описан OP, но в приложении Symfony 5.1 и с использованием моего собственного существующего объекта User. Оказывается, проблема в том, что я не реализовал \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));
}
Как только это было исправлено, приложение могло оставаться в системе как в среде разработки, так и в рабочей среде, а также как с использованием параметра «Запомнить меня», так и без него. Логика этого объясняется в официальный сайт.
Моя проблема была очень похожа. Я настроил безопасность в Symfony 6.0 в соответствии с Безопасность (Документация Symfony). После отправки формы входа и успешной аутентификации я был перенаправлен без сохранения сеанса как анонимный пользователь.
На следующий день я, наконец, попытался зайти на свой сайт с помощью https и вуаля, я вошел в систему. Поэтому попробуйте использовать https при входе в систему.
Можете ли вы показать, как использовать вашу конфигурацию с помощью php bin/console debug:config security