symfony - 使用外部 REST API 在 Symfony 3 中自定义身份验证

标签 symfony authentication passwords

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

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

我使用自定义身份验证器进行了测试,该验证器仅检查密码是否为“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);
    }
}

最佳答案

我让它与该实现一起工作:

安全.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

服务.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. 在身份验证器的 getUser 方法中实现密码验证,并让 checkCredentials 方法始终返回 true。
  2. 禁用UserProvider的refreshUser方法

关于symfony - 使用外部 REST API 在 Symfony 3 中自定义身份验证,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46362122/

相关文章:

java - 通过 UAA 进行 SAP HANA XSA Java 用户身份验证

php - GitHub Integrations API 的 JWT 凭证错误

mysql - 在读取存储在 mysql.user 中的登录信息时,MySQL 如何知道使用哪种算法?

symfony - 服务 "assetic.helper.static"依赖于不存在的服务 "assets.packages"

Symfony 容器 - 标记服务实现接口(interface)

php - 硅胶 : allow user to change langage by clicking on html element and keeping clean URL

asp.net - Page.User.Identity.IsAuthenticated 返回未设置到对象实例的对象引用

security - 密码不是一种通过默默无闻的安全形式吗?

php - 使用 Web 表单 (PHP) 重置 Linux 密码

Symfony2 功能测试 $crawler 不工作