我正在使用带有DoctrineMongoDBBundle和LexikJWTAuthenticationBundle的symfony 3.4。我正在尝试创建一个返回JWT令牌的用户登录。如果我在in_memory提供者下指定用户名和密码,它将返回令牌但如果我使用实体提供者,则返回{“code”:401,“message”:“bad credentials”}。
这是我的security.yml
# To get started with security, check out the documentation:
# https://symfony.com/doc/current/security.html
security:
# https://symfony.com/doc/current/security.html#b-configuring-how-users-are-loaded
encoders:
AppBundle\Document\User:
algorithm: bcrypt
cost: 12
providers:
webprovider:
entity:
class: AppBundle\Document\User
property: username
firewalls:
# disables authentication for assets and the profiler, adapt it according to your needs
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
login:
pattern: ^/api/login
stateless: true
anonymous: true
provider: webprovider
form_login:
username_parameter: username
password_parameter: password
check_path: /api/login_check
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
require_previous_session: false
api:
pattern: ^/api
stateless: true
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
main:
anonymous: ~
# activate different ways to authenticate
# https://symfony.com/doc/current/security.html#a-configuring-how-your-users-will-authenticate
#http_basic: ~
# https://symfony.com/doc/current/security/form_login_setup.html
#form_login: ~
access_control:
- { path: ^/api/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
这是我的用户类
<?php
// /AppBundle/Document/User.php
namespace AppBundle\Document;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\Bundle\MongoDBBundle\Validator\Constraints\Unique as MongoDBUnique;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* @MongoDB\Document(collection="users")
* @MongoDBUnique(fields="email")
*/
class User implements UserInterface
{
/**
* @MongoDB\Id
*/
protected $id;
/**
* @MongoDB\Field(type="string")
* @Assert\Email()
*/
protected $email;
/**
* @MongoDB\Field(type="string")
* @Assert\NotBlank()
*/
protected $username;
/**
* @MongoDB\Field(type="string")
* @Assert\NotBlank()
*/
protected $password;
/**
* @MongoDB\Field(type="boolean")
*/
private $isActive;
public function __construct()
{
var_dump("1");
$this->isActive = true;
// may not be needed, see section on salt below
// $this->salt = md5(uniqid('', true));
}
public function getId()
{
return $this->id;
}
public function getEmail()
{
return $this->email;
}
public function setEmail($email)
{
$this->email = $email;
}
public function setUsername($username)
{
$this->username = $username;
}
public function getUsername()
{
var_dump($this->username);
return $this->username;
}
public function getSalt()
{
return null;
}
public function getPassword()
{
return $this->password;
}
public function setPassword($password)
{
$this->password = $password;
}
public function getRoles()
{
return array('ROLE_USER');
}
public function eraseCredentials()
{
}
}
如果有人可以提供帮助,我真的很感激,谢谢。
答案 0 :(得分:1)
你应该通过在实体中扩展Base model User calss来使用FOS bundle组件,比直接使用UserInterface更好。在你的情况下问题可能是你的密码没有被编码正确。最好使用security.password_encoder
进行编码。为了更好地理解,我正在分享一个示例来设置登录和生成令牌。
您的security.yml应该如下所示
`# Symfony 3.4 security.yml
security:
encoders:
FOS\UserBundle\Model\UserInterface: bcrypt
Symfony\Component\Security\Core\User\User: plaintext
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: ROLE_ADMIN
ROLE_API: ROLE_USER, ROLE_MEDIC, ROLE_STUDENT
# https://symfony.com/doc/current/security.html#b-configuring-how-users-are-loaded
providers:
fos_userbundle:
id: fos_user.user_provider.username_email
in_memory:
memory: ~
firewalls:
# disables authentication for assets and the profiler, adapt it according to your needs
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
api_login:
pattern: ^/api/login
stateless: true
anonymous: true
form_login:
check_path: /api/login_check
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
require_previous_session: false
api:
pattern: ^/(api)
stateless: true #false (not assign cookies)
anonymous: ~
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
provider: fos_userbundle
access_control: .................`
您的用户类应如下所示:
// /AppBundle/Document/User.php
namespace AppBundle\Document;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
use FOS\UserBundle\Model\User as BaseUser;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\Bundle\MongoDBBundle\Validator\Constraints\Unique as MongoDBUnique;
/**
* @MongoDB\Document(collection="users")
* @MongoDBUnique(fields="email")
*/
class User implements BaseUser
{
/**
* @MongoDB\Id
*/
protected $id;
/**
* @MongoDB\Field(type="string")
* @Assert\Email()
*/
protected $email;
/**
* @MongoDB\Field(type="string")
* @Assert\NotBlank()
*/
protected $username;
/**
* @MongoDB\Field(type="string")
* @Assert\NotBlank()
*/
protected $password;
/**
* @MongoDB\Field(type="boolean")
*/
private $isActive;
public function __construct()
{
var_dump("1");
$this->isActive = true;
// may not be needed, see section on salt below
// $this->salt = md5(uniqid('', true));
}
public function getId()
{
return $this->id;
}
public function getEmail()
{
return $this->email;
}
public function setEmail($email)
{
$this->email = $email;
}
public function setUsername($username)
{
$this->username = $username;
}
public function getUsername()
{
var_dump($this->username);
return $this->username;
}
public function getSalt()
{
return null;
}
public function getPassword()
{
return $this->password;
}
public function setPassword($password)
{
$this->password = $password;
}
public function getRoles()
{
return array('ROLE_USER');
}
public function eraseCredentials()
{
}
}
现在你的Api tokenController.php生成令牌并验证令牌
namespace \your_namespace\Api;
use FOS\RestBundle\Context\Context;
use FOS\RestBundle\Controller\Annotations as Rest;
use FOS\RestBundle\Controller\FOSRestController;
use AppBundle\Document\User;
# Below most of the components belongs to Nelmio api or Sensio or Symfony
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Security\Core\Encoder\EncoderFactory;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Lexik\Bundle\JWTAuthenticationBundle\Exception\ExpiredTokenException;
use Lexik\Bundle\JWTAuthenticationBundle\Exception\InvalidTokenException;
use Lexik\Bundle\JWTAuthenticationBundle\Exception\JWTDecodeFailureException;
use Lexik\Bundle\JWTAuthenticationBundle\Security\Authentication\Token\PreAuthenticationJWTUserToken;
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
class TokenController extends FOSRestController
{
/**
* @Rest\Post("/tokens", name="api_token_new",
* options={"method_prefix" = false},
* defaults={"_format"="json"}
* )
*
* @ApiDoc(
* section = "Security",
* description = "Get user token",
* parameters={
* {"name"="username", "dataType"="string", "required"=true, "description"="username or email"},
* {"name"="pass", "dataType"="string", "required"=true, "description"="password "},
* }
* )
*/
public function newTokenAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$username = $request->get('username');
/** @var User $user */
$user = $em->getRepository('AppBundle:User')->findOneBy(['email' => $username]);
if (!$user) {
throw $this->createNotFoundException();
}
if (!$user->isEnabled()){
throw $this->createNotFoundException($this->get('translator')->trans('security.user_is_disabled'));
}
$pass = $request->get('pass');
$isValid = $this->get('security.password_encoder')->isPasswordValid($user, $pass);
if (!$isValid) {
throw new BadCredentialsException();
}
$token = $this->get('lexik_jwt_authentication.encoder')->encode([
'username' => $user->getUsername(),
'id' => $user->getId(),
'roles' => $user->getRoles(),
'exp' => time() + (30 * 24 * 3600) // 30 days expiration -> move to parameters or config
]);
// Force login
$tokenLogin = new UsernamePasswordToken($user, $pass, "public", $user->getRoles());
$this->get("security.token_storage")->setToken($tokenLogin);
// Fire the login event
// Logging the user in above the way we do it doesn't do this automatically
$event = new InteractiveLoginEvent($request, $tokenLogin);
$this->get("event_dispatcher")->dispatch("security.interactive_login", $event);
$view = $this->view([
'token' => $token,
'user' => $user
]);
$context = new Context();
$context->addGroups(['Public']);
$view->setContext($context);
return $this->handleView($view);
}
/**
* @Rest\Post("/validate", name="api_token_validate",
* options={"method_prefix" = false},
* defaults={"_format"="json"}
* )
*
* @ApiDoc(
* section = "Security",
* description = "Get user by token",
* parameters={
* {"name"="token", "dataType"="textarea", "required"=true, "description"="token"},
* }
* )
*/
public function validateUserToken(Request $request)
{
$token = $request->get('token');
//get UserProviderInterface
$fos = $this->get('fos_user.user_provider.username_email');
//create PreAuthToken
$preAuthToken = new PreAuthenticationJWTUserToken($token);
try {
if (!$payload = $this->get('lexik_jwt_authentication.jwt_manager')->decode($preAuthToken)) {
throw new InvalidTokenException('Invalid JWT Token');
}
$preAuthToken->setPayload($payload);
} catch (JWTDecodeFailureException $e) {
if (JWTDecodeFailureException::EXPIRED_TOKEN === $e->getReason()) {
throw new ExpiredTokenException();
}
throw new InvalidTokenException('Invalid JWT Token', 0, $e);
}
//get user
/** @var User $user */
$user = $this->get('lexik_jwt_authentication.security.guard.jwt_token_authenticator')->getUser($preAuthToken, $fos);
$view = $this->view([
'token' => $token,
'user' => $user
]);
$context = new Context();
$context->addGroups(array_merge(['Public'],$user->getRoles()));
$view->setContext($context);
return $this->handleView($view);
}
如果你遇到任何问题,请告诉我。