[英]Symfony 4 login form : authenticating successfully, but authentication immediately lost after redirect
我按照此 表單登錄設置文檔構建了一個登錄表單。
這在 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: { }
AS @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,它設置了一個 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";}
我去登錄頁面。 與新 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? 易管理員? 我如何確保安全令牌存儲在會話中,即使它被記錄為已存儲? 我還嘗試將 access_control 更改為角色 ROLE_USER 以便任何經過身份驗證的用戶都可以訪問。 沒門。
任何幫助都非常感謝。
所以這里是我以更結構化的方式發表的評論,以便它可以幫助其他人在 Symfony 中遇到身份驗證問題。
默認情況下,每個會話都保存為<project_dir>/var/cache/<env>/sessions
名為sess_<id>
的文件,或者如果framework.session.handler
設置為php.ini
的save_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.ini
的session.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.