问题
Most likely I am missing some silly stuff, but I spent quite some time on this, so any help is appreciated.
The authentication is based upon this tutorial
I am using bcrypt to encode the password and it seems that is working properly on user signup.
But when logging in it throws error below, even though the password entered is correct:
Invalid credentials.
I verified that the email and password arrive properly at the login authenticator ( login form is submitted via Ajax ).
Also the getUser() method seems to be doing its job of retrieving $user object and the corresponding password from the db.
The security.yml is set as follows:
security:
encoders:
UsedBundle\Entity\User:
algorithm: bcrypt
This is the registration controller:
namespace UsedBundle\Controller;
use UsedBundle\Form\UserType;
use UsedBundle\Entity\User;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use UsedBundle\Service\sendEmail;
class RegistrationController extends Controller
{
/**
* @Route("/inscription", name="inscription")
*/
public function registerAction(Request $request)
{
$user = new User();
$form = $this->createForm(UserType::class, $user);
if ($request->isMethod('POST')) {
$form->submit($request->request->get($form->getName('user')));
if(!$form->isValid()){
// handle invalid form
}
if ($form->isSubmitted() && $form->isValid()) {
$password = $this->get('security.password_encoder')
->encodePassword($user, $user->getPlainPassword());
$user->setPassword($password);
$user->setUserKey( $user->getEmail() );
$user->setUserKeyTime();
$user->setDateReg();
$em = $this->getDoctrine()->getManager('used');
$em->persist($user);
$em->flush();
return new JsonResponse(array(
'status' => 'ok',
'message' => 'Success!')
);
}
}else{
return $this->render(
'common/register.html.twig',
array('form' => $form->createView())
);
}
}
}
The login form authenticator ( setup as a service ):
namespace UsedBundle\Security;
use Symfony\Component\Security\Guard\Authenticator\AbstractFormLoginAuthenticator;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Guard\AbstractGuardAuthenticator;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Security;
use UsedBundle\Entity\User;
use UsedBundle\Repository\UserRepository;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
class FormLoginAuthenticator extends AbstractFormLoginAuthenticator
{
private $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
public function getCredentials(Request $request)
{
if ($request->getPathInfo() != '/login_check') {
return;
}
$username = $request->request->get('_email');
$request->getSession()->set(Security::LAST_USERNAME, $username);
$password = $request->request->get('_password');
return array(
'username' => $username,
'password' => $password
);
}
public function getUser($credentials, UserProviderInterface $userProvider)
{
$username = $credentials['username'];
$user = $this->container
->get('doctrine')
->getRepository('UsedBundle:User', 'used')
->findOneByemail( $username );
return $user;
}
public function checkCredentials($credentials, UserInterface $user)
{
$plainPassword = $credentials['password'];
$encoder = $this->container->get('security.password_encoder');
if (!$encoder->isPasswordValid($user, $plainPassword)){
throw new BadCredentialsException();
}else{
$this->pass_error = 'no error';
}
}
protected function getLoginUrl()
{
return $this->container->get('router')
->generate('homepage');
}
protected function getDefaultSuccessRedirectUrl()
{
return $this->container->get('router')
->generate('homepage');
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
// AJAX! Return some JSON
if ($request->isXmlHttpRequest()) {
return new JsonResponse(
array('userId' => $token->getUser()->getId(),
'statut' => 'ok'
)
);
}
// for non-AJAX requests, return the normal redirect
return parent::onAuthenticationSuccess($request, $token, $providerKey);
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
return new JsonResponse(
array('message' => $exception->getMessageKey(),
'statut' => 'error',
'passerror' => $this->pass_error )
);
}
Excerpt of var_dump($user) on checkCredentials() as requested
object(UsedBundle\Entity\User)#329 (15) {
["id":"UsedBundle\Entity\User":private]=>
int(7)
["avatar":"UsedBundle\Entity\User":private]=>
string(11) "dsfdfafadfa"
["name":"UsedBundle\Entity\User":private]=>
string(9) "dfdffadfa"
["password":"UsedBundle\Entity\User":private]=>
string(64) "jjuewij/sc9Af17i+ZXAUcrdiZX83HHMLjTNVSnJ34qGCp6BAxisVtjiG3Nm+uH5"
["plainPassword":"UsedBundle\Entity\User":private]=>
NULL
["email":"UsedBundle\Entity\User":private]=>
string(22) "myemail@gmail.com"
["phone":"UsedBundle\Entity\User":private]=>
string(12) "445454545454"
["roles":"UsedBundle\Entity\User":private]=>
string(9) "ROLE_USER"
["isActive":"UsedBundle\Entity\User":private]=>
bool(true)
as requested, excerpts of var_dump($exception) on onAuthenticationFailure()
object(Symfony\Component\Security\Core\Exception\BadCredentialsException)#322 (8) {
["token":"Symfony\Component\Security\Core\Exception\AuthenticationException":private]=>
object(Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken)#61 (6) {
["credentials":"Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken":private]=>
array(2) {
["username"]=>
string(22) "myemail@gmail.com"
["password"]=>
string(8) "senha444"
}
["guardProviderKey":"Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken":private]=>
string(6) "main_0"
["user":"Symfony\Component\Security\Core\Authentication\Token\AbstractToken":private]=>
NULL
["roles":"Symfony\Component\Security\Core\Authentication\Token\AbstractToken":private]=>
array(0) {
}
["authenticated":"Symfony\Component\Security\Core\Authentication\Token\AbstractToken":private]=>
bool(false)
["attributes":"Symfony\Component\Security\Core\Authentication\Token\AbstractToken":private]=>
array(0) {
}
}
["message":protected]=>
string(0) ""
["string":"Exception":private]=>
string(0) ""
["code":protected]=>
int(0)
["file":protected]=>
string(78) "/Users/BAMAC/Sites/Symfony1/src/UsedBundle/Security /FormLoginAuthenticator.php"
["line":protected]=>
int(58)
回答1:
After Edwin put me in the right direction, I managed to get this to work. In reality the encryption was not the only issue. The changes concern mainly the registration controller, where the code for password encryption was modified. I also changed the form authenticator which is now based upon this
as the previous base indicated on my question is outdated.
Finally, Symfony are not very much friends with Ajax, so the Ajax URL was adapted to work for dev environment.
Here is the whole code:
security.yml
security:
encoders:
UsedBundle\Entity\User:
algorithm: bcrypt
providers:
db_provider:
entity:
class: UsedBundle:User
property: email
manager_name: used
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: ~
provider: db_provider
form_login:
login_path: /
username_parameter: _email
check_path: /login_check
guard:
authenticators:
- app.form_login_authenticator
logout:
path: /logout
target: /
access_control:
- { path: ^/admin, roles: ROLE_ADMIN }
- { path: ^/, roles: IS_AUTHENTICATED_ANONYMOUSLY }
Changes on registration controller:
if ($form->isSubmitted() && $form->isValid()) {
$this->formData = $request->request->get($form->getName('user'));
$this->plainPassword = $this->formData['plainPassword']['first'];
$password = $this->get('security.password_encoder')
->encodePassword($user, $this->plainPassword );
.....
The new authenticator:
namespace UsedBundle\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Guard\AbstractGuardAuthenticator;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Security\Core\Security;
class FormLoginAuthenticator extends AbstractGuardAuthenticator
{
private $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
public function getCredentials(Request $request)
{
if ($request->getPathInfo() != '/login_check') {
return;
}
$email = $request->request->get('_email');
$request->getSession()->set(Security::LAST_USERNAME, $email);
$password = $request->request->get('_password');
return array(
'email' => $email,
'password' => $password
);
}
public function getUser($credentials, UserProviderInterface $userProvider)
{
$email = $credentials['email'];
return $this->user = $this->container
->get('doctrine')
->getRepository('UsedBundle:User', 'used')
->findOneByemail( $email );
}
public function checkCredentials($credentials, UserInterface $user)
{
$plainPassword = $credentials['password'];
$encoder = $this->container->get('security.password_encoder');
if (!$encoder->isPasswordValid($user, $plainPassword)){
throw new BadCredentialsException();
}else{
return true;
}
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
$session=$request->getSession();
$session->set('seller_id', $token->getUser()->getId());
$session->set('email', $token->getUser()->getEmail());
return new JsonResponse(
array(
'userId' => $token->getUser()->getId(),
'message' => $this->credentials,
'statut' => 'ok',
'roles' => $token->getUser()->getRoles(),
'email' => $token->getUser()->getEmail(),
)
);
// for non-AJAX requests, return the normal redirect
//return parent::onAuthenticationSuccess($request, $token, $providerKey);
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
$data = array(
'message' => strtr($exception->getMessageKey(), $exception->getMessageData())
// or to translate this message
// $this->translator->trans($exception->getMessageKey(), $exception->getMessageData())
);
return new JsonResponse($data, Response::HTTP_FORBIDDEN);
}
/**
* Called when authentication is needed, but it's not sent
*/
public function start(Request $request, AuthenticationException $authException = null)
{
$data = array(
'message' => 'Authentication Required'
);
return new JsonResponse($data, Response::HTTP_UNAUTHORIZED);
}
public function supportsRememberMe()
{
return false;
}
}
The login controller:
namespace UsedBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
class LoginController extends Controller
{
/**
* @Route("/login", name="login")
*/
public function loginAction(Request $request)
{
$helper = $this->get('security.authentication_utils');
return $this->render('common/login.html.twig', array(
// last username entered by the user (if any)
'last_username' => $helper->getLastUsername(),
// last authentication error (if any)
'error' => $helper->getLastAuthenticationError(),
));
}
/**
* @Route("/login_check", name="security_login_check")
*/
public function loginCheckAction()
{
// will never be executed
}
}
And this is how the Ajax url should look like while in dev:]
$.ajax({
url: "/app_dev.php/login_check",
type: "POST",
dataType: "json",
data: str,
success: function(data) {
.....
来源:https://stackoverflow.com/questions/44921412/symfony-3-bcrypt-password-does-not-verify