簡體   English   中英

Symfony 5 中的多重身份驗證與 LDAP 和 DB

[英]Multiple authentication in Symfony 5 with LDAP and DB

我想在 Symfony 5 中進行多重身份驗證。第一個身份驗證是 LDAP。 如果用戶在它的確定。 他將被連接。 如果他不在我想要 symfony 如果用戶存在則檢查數據庫。 如果是,那么用戶將被連接。

我嘗試部署 LDAP 身份驗證器和安全身份驗證器,但是......它總是 LDAP 身份驗證器起作用。 從不安全。

它是如何工作的,我必須做什么?

# app/config/services.yaml

# This file is the entry point to configure your own services.
# Files in the packages/ subdirectory configure your dependencies.

# Put parameters here that don't need to change on each machine where the app is deployed
# https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration
parameters:

services:
    # default configuration for services in *this* file
    _defaults:
        autowire: true      # Automatically injects dependencies in your services.
        autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.

    Symfony\Component\Ldap\Ldap:
        arguments: ['@Symfony\Component\Ldap\Adapter\ExtLdap\Adapter']
    Symfony\Component\Ldap\Adapter\ExtLdap\Adapter:
        arguments:
            -   host: ldap.example.com
                port: 389
                #encryption: tls
                options:
                    protocol_version: 3
                    referrals: false

    # makes classes in src/ available to be used as services
    # this creates a service per class whose id is the fully-qualified class name
    App\:
        resource: '../src/*'
        exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'

    # controllers are imported separately to make sure services can be injected
    # as action arguments even if you don't extend any base controller class
    App\Controller\:
        resource: '../src/Controller'
        tags: ['controller.service_arguments']

    # add more service definitions when explicit configuration is needed
    # please note that last definitions always *replace* previous ones
# app/config/packages/security.yaml

security:
    encoders:
        App\Entity\User:
            algorithm: auto

    # https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
    providers:
        my_ldap:
            ldap:
                service: Symfony\Component\Ldap\Ldap
                base_dn: dc=example,dc=com
                search_dn: cn=username,ou=Administration,dc=example,dc=com
                search_password: userPassword
                default_roles: ROLE_USER

        app_user_provider:
            entity:
                class: App\Entity\User
                property: email
                
    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        ldap:
            anonymous: ~
             
            form_login_ldap:
                login_path: login
                check_path: login
                service: Symfony\Component\Ldap\Ldap
                dn_string: ou=aGroupWhereAreMyUsers,dc=example,dc=com
                query_string: '(samaccountname={username})'
                search_dn: cn=username,ou=Administration,dc=example,dc=com
                search_password: userPassword
            
            logout:
                path: app_logout

        main:
            anonymous: ~
            provider: app_user_provider
            logout:
                path: app_logout
            guard:
                authenticators:
                    - App\Security\LoginAuthenticator
        
    access_control:
        - { path: ^/$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/info$, roles: ROLE_USER }

    role_hierarchy:
        ROLE_SUPER_ADMIN: [ROLE_SUPER_ADMIN, ROLE_USER]
        ROLE_USER: ROLE_USER
//app/src/Security/LoginAuthenticator.php

<?php

namespace App\Security;

use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Csrf\CsrfToken;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Security\Guard\Authenticator\AbstractFormLoginAuthenticator;
use Symfony\Component\Security\Guard\PasswordAuthenticatedInterface;
use Symfony\Component\Security\Http\Util\TargetPathTrait;

class LoginAuthenticator extends AbstractFormLoginAuthenticator implements PasswordAuthenticatedInterface
{
    use TargetPathTrait;

    public const LOGIN_ROUTE = 'login';

    private $entityManager;
    private $urlGenerator;
    private $csrfTokenManager;
    private $passwordEncoder;

    public function __construct(EntityManagerInterface $entityManager, UrlGeneratorInterface $urlGenerator, CsrfTokenManagerInterface $csrfTokenManager, UserPasswordEncoderInterface $passwordEncoder)
    {
        $this->entityManager = $entityManager;
        $this->urlGenerator = $urlGenerator;
        $this->csrfTokenManager = $csrfTokenManager;
        $this->passwordEncoder = $passwordEncoder;
    }

    public function supports(Request $request)
    {
        return self::LOGIN_ROUTE === $request->attributes->get('_route')
            && $request->isMethod('POST');
    }

    public function getCredentials(Request $request)
    {
        $credentials = [
            'email' => $request->request->get('email'),
            'password' => $request->request->get('password'),
            'csrf_token' => $request->request->get('_csrf_token'),
        ];
        $request->getSession()->set(
            Security::LAST_USERNAME,
            $credentials['email']
        );

        return $credentials;
    }

    public function getUser($credentials, UserProviderInterface $userProvider)
    {
        $token = new CsrfToken('authenticate', $credentials['csrf_token']);
        if (!$this->csrfTokenManager->isTokenValid($token)) {
            throw new InvalidCsrfTokenException();
        }

        $user = $this->entityManager->getRepository(User::class)->findOneBy(['email' => $credentials['email']]);

        if (!$user) {
            // fail authentication with a custom error
            throw new CustomUserMessageAuthenticationException('Email could not be found.');
        }

        return $user;
    }

    public function checkCredentials($credentials, UserInterface $user)
    {
        return $this->passwordEncoder->isPasswordValid($user, $credentials['password']);
    }

    /**
     * Used to upgrade (rehash) the user's password automatically over time.
     */
    public function getPassword($credentials): ?string
    {
        return $credentials['password'];
    }

    public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
    {
        if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) {
            return new RedirectResponse($targetPath);
        }

        // For example : return new RedirectResponse($this->urlGenerator->generate('some_route'));
        throw new \Exception('TODO: provide a valid redirect inside '.__FILE__);
    }

    protected function getLoginUrl()
    {
        return $this->urlGenerator->generate(self::LOGIN_ROUTE);
    }
}
//app/src/Controller/defaultController.php

<?php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
 

class DefaultController extends AbstractController
{
    /**
     * @Route("/info", name="default")
     */
    public function index()
    {
        return $this->render('default/index.html.twig', [
            'controller_name' => 'DefaultController',
            'user' => $this->getUser(),
        ]);
    }

    /**
     * @Route("/", name="login")
     */
    public function loginAction(Request $request, AuthenticationUtils $authUtils): Response
    {
        // get the login error if there is one
        $error = $authUtils->getLastAuthenticationError();
        
        // last username entered by the user
        $lastUsername = $authUtils->getLastUsername();

        if(!is_null($this->getUser()) && in_array('CN=userHasToBeInThisGroupToAccessToTheWebsite,OU=Administration,DC=example,DC=com', $this->getUser()->getEntry()->getAttributes()['memberOf'])){

            if(in_array('CN=groupWhereUsersWhoAreInAreAdmins,OU=Administration,DC=example,DC=com', $this->getUser()->getEntry()->getAttributes()['memberOf'])){
                //Insert the code here to add "ROLE_SUPER_ADMIN" to the user before he goes on the dashboard
            }

            return $this->redirectToRoute('default');
        }


        return $this->render('security/login.html.twig', array(
            'last_username' => $lastUsername,
            'error'         => $error,
        ));
    }

    /**
     * @Route("/logout", name="app_logout")
     */
    public function logout()
    {
        // controller can be blank: it will never be executed!
        throw new \Exception('Don\'t forget to activate logout in security.yaml');
    }

}
{# app/templates/security/login.html.twig #}

{% extends 'base.html.twig' %}
 
{% block body %}
    {% if error %}
        <div class="alert alert-danger">{{ error.messageKey|trans(error.messageData, 'security') }}</div>
    {% endif %}
     
    <form method="post">
        <label for="username">Username:</label>
        <input type="text" id="username" name="_username" value="{{ last_username }}" required />
     
        <label for="password">Password:</label>
        <input type="password" id="password" name="_password" required />

        <input type="hidden" name="_csrf_token" value="{{ csrf_token('authenticate') }}" />
     
        <button type="submit">login</button>
    </form>

    <a href='/logout'> Logout </a>
{% endblock %}

您可以使用多個警衛來實現https://symfony.com/doc/current/security/multiple_guard_authenticators.html

ldap的保護和db checking的保護,當ldap保護返回 null 時, db checking保護將起作用

@Ping 您可以使用兩個提供商和兩個防火牆,如下所示

    providers:
    db_provider:
        entity:
            class: UserBundle:User
            property: username
    hr_provider:
        entity:
            class: UserBundle:User
            property: username

firewalls:
    main:
        provider: db_provider
        pattern: ^/login|/logout|^/crm
        anonymous: ~
        form_login:
            default_target_path: contact_index
            login_path: login
            check_path: login
        logout:
            path: logout
            target: login
    hr_main:
        pattern: ^/signin|^/logoff|^/hr
        anonymous: ~
        provider: hr_provider
        form_login:
            default_target_path: hr_employee_leaves_balance
            login_path: hr_signin
            check_path: hr_signin
        logout:
            path: hr_logout
            target: hr_signin

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM