问题
I use FriendsOfSymfony UserBundle. It works when I set everything in firewall as form_login
, but if I set it to simple_form
to use Custom Authenticator then it let's me login even if the account is locked or disabled. I want to check if the user comes from the correct IP, that's why I created the custom authenticator, but it seems that some authentication coming from FOS is not processed this way. How can I use simple_form
with custom authenticator while still keeping full functionality of FOS UserBundle?
Is there some other way I can achieve some other authentication than just the standard? Maybe I'm doing something wrong? I know I can correct this code of my authenticator to check for locked/enabled etc, but I figured - since it's actually already done in FOS - why should I?
EDIT: Also, I noticed that when I use simple_form
the methods of class Symfony\Component\Security\Core\User\UserChecker
aren't being called.
Below is my code of authenticator and security.yml
:
config.yml
services:
login_authenticator:
class: Forex\AlchemyBundle\Security\LoginAuthenticator
arguments: ["@security.encoder_factory"]
security.yml
security:
encoders:
FOS\UserBundle\Model\UserInterface: sha512
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: ROLE_ADMIN
providers:
fos_userbundle:
id: fos_user.user_provider.username_email
firewalls:
main:
pattern: ^/
simple_form:
authenticator: login_authenticator
provider: fos_userbundle
csrf_provider: form.csrf_provider
logout: true
anonymous: true
access_control:
- { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY } # To be removed
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin/, role: ROLE_ADMIN }
- { path: ^/.*, roles: ROLE_USER }
LoginAuthenticator
<?php
namespace Forex\AlchemyBundle\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Authentication\SimpleFormAuthenticatorInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\User\UserProviderInterface;
class LoginAuthenticator implements SimpleFormAuthenticatorInterface
{
private $encoderFactory;
public function __construct(EncoderFactoryInterface $encoderFactory)
{
$this->encoderFactory = $encoderFactory;
}
public function authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey)
{
try {
$user = $userProvider->loadUserByUsername($token->getUsername());
} catch (UsernameNotFoundException $e) {
throw new AuthenticationException('Invalid username or password');
}
$encoder = $this->encoderFactory->getEncoder($user);
$passwordValid = $encoder->isPasswordValid(
$user->getPassword(),
$token->getCredentials(),
$user->getSalt()
);
if ($passwordValid) {
$request = Request::createFromGlobals();
$current_ip = $request->server->get('REMOTE_ADDR');
$user->setLoggedIP($current_ip);
if (!$user->isValidIP()) {
throw new AuthenticationException(
"You cannot login from your location.",
100
);
}
return new UsernamePasswordToken(
$user,
$user->getPassword(),
$providerKey,
$user->getRoles()
);
} else {
// TODO: Check if there weren't too many tries to login
}
throw new AuthenticationException('Invalid username or password');
}
public function supportsToken(TokenInterface $token, $providerKey)
{
return $token instanceof UsernamePasswordToken
&& $token->getProviderKey() === $providerKey;
}
public function createToken(Request $request, $username, $password, $providerKey)
{
return new UsernamePasswordToken($username, $password, $providerKey);
}
}
回答1:
I was facing the same issue. Here is how I solved it.
You must call checkPreAuth() and checkPostAuth() methods of your UserChecker class from within the authenticateToken() method in your Authenticator.
That is done this way:
1) Configure the user_checker service:
services:
app.user_checker:
class: AppBundle\Security\UserChecker
2) Configure the autenticator as a service and inject the user_checker service:
services:
app.my_authenticator:
class: AppBundle\Security\MyAuthenticator
arguments: ["@app.user_checker", "@security.password_encoder"]
3) Now you can call checkPreAuth() and checkPostAuth() on authenticateToken()
Anyway I think symfony approach is the right one, because in my case y needed to perform different checks in simple_form than in login_form.
来源:https://stackoverflow.com/questions/23501777/symfony2-fos-using-simple-form-authenticator