使用LDAP和DB在Symfony 5中进行多重身份验证 [英] Multiple authentication in Symfony 5 with LDAP and DB

查看:66
本文介绍了使用LDAP和DB在Symfony 5中进行多重身份验证的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在Symfony 5中进行多重身份验证.第一个身份验证是LDAP.如果用户在里面就可以了.他将被连接.如果他不在,我想在用户存在的情况下在数据库中进行symfony检查.如果是,则将连接用户.

I woul'd like to do a multiple authentication in Symfony 5. The first authentication is LDAP. If the user is in it's ok. He will be connected. If he is not in I want symfony checks in DB if the user exists. If it's a yes so the user will be connected.

我尝试部署LDAP身份验证器和安全性验证器,但是...始终都是LDAP身份验证器起作用.永远没有安全感.

I tried to deploy LDAP authenticator and Security authenticator but... It's always LDAP authenticator who works. Never the security.

它如何工作以及我必须做什么?

How does it works and what I have to do ?

# 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 %}

推荐答案

@Ping,您可以使用两个提供程序和两个防火墙,如下所示:

@Ping you can use two providers and two firewalls like below

    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

这篇关于使用LDAP和DB在Symfony 5中进行多重身份验证的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆