You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Symfony 3.3基于REST接口的登录表单开发问题求助

Hey there! Let's sort out your Symfony + REST login flow step by step. I see a few key issues in your current code, plus we need to fix how you handle the REST response and integrate it properly with Symfony's Security system.

First: Clean Up Your Existing Code

Let's start with obvious fixes and bad practices:

  1. Remove the loginCheckAction: Symfony's Security component automatically handles the /login_check route when configured correctly—your custom method here is unnecessary and will break the flow.
  2. Fix form field names: Make sure your login form uses Symfony's default field names so the Security component can pick up credentials:
    <form class="form-signin" action="{{ path('app_user_login_check') }}" method="POST">
        <input type="text" name="_username" class="form-control" placeholder="Username" required autofocus>
        <input type="password" name="_password" class="form-control" placeholder="Password" required>
        <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
    </form>
    
  3. Fix typos in WebserviceUserProvider: Correct __contsruct to __construct and remove the unused $user property (you don't need to inject a user here).

Second: Understand Symfony's Security Flow (Critical Fix!)

You're mixing up two core Security components:

  • UserProvider: Loads existing user data (by username)
  • AuthenticationProvider: Validates user credentials (username + password)

Your REST login call should live in a custom AuthenticationProvider, not the UserProvider. Here's how to build this properly:

Step 1: Create a Custom Authentication Token

This holds the user's credentials during the authentication process:

// src/AppBundle/Security/Token/WebserviceAuthToken.php
namespace AppBundle\Security\Token;

use Symfony\Component\Security\Core\Authentication\Token\AbstractToken;

class WebserviceAuthToken extends AbstractToken
{
    private $password;

    public function __construct($username, $password, array $roles = [])
    {
        parent::__construct($roles);
        $this->setUser($username);
        $this->password = $password;
        $this->setAuthenticated(count($roles) > 0);
    }

    public function getPassword()
    {
        return $this->password;
    }

    public function getCredentials()
    {
        return ''; // Don't store credentials after authentication
    }
}

Step 2: Build the Custom AuthenticationProvider

This is where you'll call your REST API to validate credentials and fetch user data:

// src/AppBundle/Security/Authentication/WebserviceAuthProvider.php
namespace AppBundle\Security\Authentication;

use AppBundle\Security\User\WebserviceUser;
use AppBundle\Security\Token\WebserviceAuthToken;
use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Unirest;

class WebserviceAuthProvider implements AuthenticationProviderInterface
{
    public function authenticate(TokenInterface $token)
    {
        $username = $token->getUser();
        $password = $token->getPassword();

        // Call your REST login endpoint
        $headers = ['Accept' => 'application/json'];
        $payload = ['user' => $username, 'password' => $password];
        
        try {
            $response = Unirest\Request::post('http://127.0.0.10:8888/login', $headers, $payload);
        } catch (\Exception $e) {
            // Handle API connection failures (timeout, unreachable)
            throw new AuthenticationException('Could not connect to authentication service.');
        }

        // Check if the API returned a successful response
        if ($response->code !== 200) {
            throw new BadCredentialsException('Invalid username or password.');
        }

        // Parse JSON response into an associative array
        $userData = json_decode($response->raw_body, true);
        if (empty($userData) || empty($userData['ldap'])) {
            throw new AuthenticationException('Invalid response from authentication service.');
        }

        // Map REST data to your WebserviceUser (ensure this class has all custom fields)
        $user = new WebserviceUser(
            $userData['ldap']['document'],
            '', // Don't store passwords locally
            '', // Salt (if your API uses it, else leave empty)
            $this->mapRoles($userData['roles'] ?? []), // Convert REST roles to Symfony roles
            $userData['ldap']['document'], // 证件号
            $userData['ldap']['full_name'], // 姓名
            $userData['ldap']['userLdap'],
            $userData['ldap']['userEpersonal'],
            $userData['ldap']['mail'], // 邮箱
            $userData['ldap']['position'] // 职位
        );

        // Create an authenticated token for the user
        $authenticatedToken = new WebserviceAuthToken($user->getUsername(), '', $user->getRoles());
        $authenticatedToken->setUser($user);
        $authenticatedToken->setAuthenticated(true);

        return $authenticatedToken;
    }

    // Convert REST roles to Symfony's ROLE_* format
    private function mapRoles(array $restRoles): array
    {
        return array_map(function ($role) {
            return 'ROLE_' . strtoupper($role);
        }, $restRoles);
    }

    public function supports(TokenInterface $token)
    {
        return $token instanceof WebserviceAuthToken;
    }
}

Step 3: Update Your WebserviceUserProvider

Now this class only handles loading user data (use a separate API endpoint if available):

// src/AppBundle/Security/User/WebserviceUserProvider.php
namespace AppBundle\Security\User;

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;
use Unirest;

class WebserviceUserProvider implements UserProviderInterface
{
    public function loadUserByUsername($username)
    {
        // Fetch user data by username (use your API's user lookup endpoint)
        $headers = ['Accept' => 'application/json'];
        $response = Unirest\Request::get("http://127.0.0.10:8888/users/{$username}", $headers);

        if ($response->code !== 200) {
            throw new UsernameNotFoundException(sprintf('User "%s" not found.', $username));
        }

        $userData = json_decode($response->raw_body, true);
        if (empty($userData) || empty($userData['ldap'])) {
            throw new UsernameNotFoundException(sprintf('User "%s" not found.', $username));
        }

        return new WebserviceUser(
            $userData['ldap']['document'],
            '',
            '',
            $this->mapRoles($userData['roles'] ?? []),
            $userData['ldap']['document'],
            $userData['ldap']['full_name'],
            $userData['ldap']['userLdap'],
            $userData['ldap']['userEpersonal'],
            $userData['ldap']['mail'],
            $userData['ldap']['position']
        );
    }

    private function mapRoles(array $restRoles): array
    {
        return array_map(function ($role) {
            return 'ROLE_' . strtoupper($role);
        }, $restRoles);
    }

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

        return $this->loadUserByUsername($user->getUsername());
    }

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

Step 4: Add a Guard Authenticator

This bridges the form submission to your custom authentication flow:

// src/AppBundle/Security/Guard/WebserviceAuthenticator.php
namespace AppBundle\Security\Guard;

use AppBundle\Security\Token\WebserviceAuthToken;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\RouterInterface;
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\Guard\AbstractGuardAuthenticator;

class WebserviceAuthenticator extends AbstractGuardAuthenticator
{
    private $router;

    public function __construct(RouterInterface $router)
    {
        $this->router = $router;
    }

    public function getCredentials(Request $request)
    {
        // Only handle the login check POST request
        if ($request->getPathInfo() !== '/login_check' || !$request->isMethod('POST')) {
            return null;
        }

        return [
            'username' => $request->request->get('_username'),
            'password' => $request->request->get('_password'),
        ];
    }

    public function getUser($credentials, \Symfony\Component\Security\Core\User\UserProviderInterface $userProvider)
    {
        // We'll handle user loading in the AuthenticationProvider
        return null;
    }

    public function checkCredentials($credentials, \Symfony\Component\Security\Core\User\UserInterface $user)
    {
        // Credential validation happens in the AuthenticationProvider
        return true;
    }

    public function createAuthenticatedToken(\Symfony\Component\Security\Core\User\UserInterface $user, $providerKey)
    {
        return new WebserviceAuthToken($user->getUsername(), '', $user->getRoles());
    }

    public function supportsToken(TokenInterface $token, $providerKey)
    {
        return $token instanceof WebserviceAuthToken;
    }

    public function start(Request $request, AuthenticationException $authException = null)
    {
        // Redirect to login page if unauthenticated
        return new RedirectResponse($this->router->generate('app_user_login'));
    }

    public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
    {
        // Store error message and redirect back to login
        $request->getSession()->set(Security::AUTHENTICATION_ERROR, $exception);
        return new RedirectResponse($this->router->generate('app_user_login'));
    }

    public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
    {
        // Redirect to homepage after successful login
        return new RedirectResponse($this->router->generate('homepage'));
    }

    public function supports(Request $request)
    {
        return $request->getPathInfo() === '/login_check' && $request->isMethod('POST');
    }
}

Step 5: Update Configuration

Update your services.yml to register the new components:

services:
    webservice_user_provider:
        class: AppBundle\Security\User\WebserviceUserProvider

    AppBundle\Security\Authentication\WebserviceAuthProvider:
        autowire: true

    AppBundle\Security\Guard\WebserviceAuthenticator:
        autowire: true
        arguments:
            $router: '@router'

And update security.yml to use the guard authenticator:

security:
    providers:
        webservice:
            id: webservice_user_provider

    firewalls:
        main:
            pattern: ^/
            anonymous: true
            logout: true
            guard:
                authenticators:
                    - AppBundle\Security\Guard\WebserviceAuthenticator

    access_control:
        # Example: Restrict admin routes to ROLE_ADMIN
        - { path: ^/admin, roles: ROLE_ADMIN }
        # Require login for all other routes
        - { path: ^/, roles: ROLE_USER }

Third: Handle REST Responses Properly

We added robust handling for your API interactions:

  • Connection Error Handling: Catch exceptions for unreachable APIs or timeouts
  • Status Code Validation: Only process responses with a 200 OK status
  • JSON Parsing: Convert the raw JSON response to an array for easy data access
  • Data Sanity Checks: Ensure the response contains the expected user data before proceeding

Fourth: Access User Data & Control Permissions

Once logged in:

  • Access User Data: In controllers, use $this->getUser() to fetch the WebserviceUser object, then call getters like $this->getUser()->getFullName() or $this->getUser()->getMail(). In Twig, use {{ app.user.fullName }}.
  • Control Access:
    • Use access_control rules in security.yml to restrict routes
    • In controllers: $this->denyAccessUnlessGranted('ROLE_ADMIN')
    • In Twig: {% if is_granted('ROLE_ADMIN') %} to show/hide elements

内容的提问来源于stack exchange,提问作者WltrRpo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 03:32:36