【问题标题】:Custom authentication in a Symfony 3 using external REST APISymfony 3 中使用外部 REST API 的自定义身份验证
【发布时间】:2018-03-03 21:07:10
【问题描述】:

我想编写一个基本的登录表单,它通过向外部 REST API 发送请求来验证用户身份。如果凭据正确,外部 API 接收登录名/密码并返回 200(ok)。 但是,我无法通过 UserProviderInterface 实现它,因为外部 REST API 在回复中给了我密码。 (loadUserByUsername方法中无法填写用户密码)。

我在这里找到了一个有效的解决方案,但它使用了 Symfony 3 中已删除的类:Symfony2 custom connection by web service

我使用自定义 Authenticator 进行了测试,它只检查密码是否为“toto”,但我得到了一个重定向循环,并且我的虚拟 UserProvider 仍然被调用:

<?php
namespace AppBundle\Security\User;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Http\Authentication\SimpleFormAuthenticatorInterface;

class WebserviceAuthenticator implements SimpleFormAuthenticatorInterface
{
    private $encoder;

    public function __construct(UserPasswordEncoderInterface $encoder)
    {
        $this->encoder = $encoder;
    }

    public function authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey)
    {
        $user = new WebserviceUser($token->getUsername(), $token->getCredentials(), null, ['ROLE_ADMIN']);

        // HERE : call the external REST API
        if ($token->getCredentials() === 'toto') {
            $token = new UsernamePasswordToken(
                $user,
                $user->getPassword(),
                'main',
                $user->getRoles()
            );
            return $token;
        }
        throw new CustomUserMessageAuthenticationException('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);
    }
}

【问题讨论】:

  • 你看过Guard Authentication了吗?这是一种超级灵活但仍然很容易创建自定义身份验证机制的方法。
  • 是的。当我删除 UserProvider 的“refreshUser”实现时,我刚刚使它工作。现在,我这样做了: public function refreshUser(UserInterface $user) { if (!$user instanceof WebserviceUser) { throw new UnsupportedUserException( sprintf('Instances of "%s" is not supported.', get_class($user)) ); } 返回$用户; } 没有风险吗?
  • 我觉得应该没问题。
  • 谢谢你,它正在工作。你对这篇文章有什么想法吗? stackoverflow.com/questions/46368737/…

标签: symfony authentication login passwords


【解决方案1】:

我让它与那个实现一起工作:

security.yml

providers:
    webservice:
        id: AppBundle\Security\User\WebserviceUserProvider

encoders:
    AppBundle\Entity\WebserviceUser: plaintext

firewalls:
    # disables authentication for assets and the profiler, adapt it according to your needs
    dev:
        pattern: ^/(_(profiler|wdt)|css|images|js)/
        security: false

    main:
        anonymous: ~
        provider: webservice
        pattern: ^/
        form_login:
            check_path: login
            login_path: login
            use_forward: true
        logout: ~
        guard:
            authenticators:
                - app.webservice_authenticator

    login:
        pattern: ^/login$
        anonymous: ~

access_control:
  - { path: ^/login$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
  - { path: ^/cache, roles: IS_AUTHENTICATED_ANONYMOUSLY }
  - { path: ^/, roles: ROLE_USER }

role_hierarchy:
    ROLE_ADMIN:       ROLE_USER

services.yml

services:
    app.webservice_authenticator:
        class: AppBundle\Security\User\WebserviceAuthenticator

用户提供者

namespace AppBundle\Security\User;

use AppBundle\Entity\WebserviceUser;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;

class WebserviceUserProvider implements UserProviderInterface
{
    public function loadUserByUsername($username)
    {
        return new WebserviceUser($username, null, null, ['ROLE_USER']);
    }

    public function refreshUser(UserInterface $user)
    {
        if (!$user instanceof WebserviceUser) {
            throw new UnsupportedUserException(
                sprintf('Instances of "%s" are not supported.', get_class($user))
            );
        }
        return $user;
    }

    public function supportsClass($class)
    {
        return WebserviceUser::class === $class;
    }
}

身份验证器

<?php

namespace AppBundle\Security\User;

use AppBundle\Service\RestClient;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Guard\Authenticator\AbstractFormLoginAuthenticator;


class WebserviceAuthenticator extends AbstractFormLoginAuthenticator
{
    private $container;
    private $restClient;

    public function __construct(ContainerInterface $container, RestClient $restClient)
    {
        $this->container = $container;
        $this->restClient = $restClient;
    }

    public function getCredentials(Request $request)
    {
        if ($request->getPathInfo() != '/login' || $request->getMethod() != 'POST') {
            return;
        }

        $username = $request->request->get('_username');
        $request->getSession()->set(Security::LAST_USERNAME, $username);
        $password = $request->request->get('_password');

        return array(
            'username' => $username,
            'password' => $password
        );
    }

    public function getUser($credentials, UserProviderInterface $userProvider)
    {
        //dump($credentials); die();
        if (array_key_exists('username', $credentials) == false) {
            return null;
        }
        $username = $credentials['username'];
        $password = strtoupper($credentials['password']);
        if ($username == '') {
            return null;
        }

        // Here the business code, provide your own implementtion
        if ($this->restClient->IsValidLogin($username, $password)) {
            return new WebserviceUser($username, $password, null, ['ROLE_USER']);
        } else {
            throw new CustomUserMessageAuthenticationException('Invalid credentials');
        }
    }

    public function checkCredentials($credentials, UserInterface $user)
    {
        return true;
    }

    public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
    {
        // AJAX! Return some JSON
        if ($request->isXmlHttpRequest()) {
            return new JsonResponse(array('message' => $exception->getMessageKey()), 403);
        }

        // for non-AJAX requests, return the normal redirect
        return parent::onAuthenticationFailure($request, $exception);
    }

    public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
    {
        // AJAX! Return some JSON
        if ($request->isXmlHttpRequest()) {
            return new JsonResponse(array('userId' => $token->getUser()->getId()));
        }

        // for non-AJAX requests, return the normal redirect
        return parent::onAuthenticationSuccess($request, $token, $providerKey);
    }

    protected function getLoginUrl()
    {
        return $this->container->get('router')
        ->generate('login');
    }

    protected function getDefaultSuccessRedirectUrl()
    {
        return $this->container->get('router')
        ->generate('homepage');
    }
}

诀窍似乎是:

  1. 在authenticator的getUser方法中实现密码验证,并且checkCredentials方法总是返回true。
  2. 禁用 UserProvider 的 refreshUser 方法

【讨论】:

    猜你喜欢
    • 2021-08-02
    • 1970-01-01
    • 1970-01-01
    • 2017-03-17
    • 2018-02-01
    • 1970-01-01
    • 2019-02-07
    • 1970-01-01
    • 2018-02-05
    相关资源
    最近更新 更多