簡體   English   中英

Symfony:針對LDAP服務器對用戶進行身份驗證,但僅當用戶名在自定義db表中時才允許登錄

[英]Symfony: Authenticate users against LDAP server, but allow login only if their username is in custom db table

首先簡要解釋一下我的任務。 我正在使用Symfony 2.8,並擁有一個REST API和SonataAdminBundle應用程序。 該網站的訪問者可以通過持久存儲到數據庫的REST API發布某些數據。 某組員工應通過管理區域管理這些數據。

應使用用戶名和密碼保護對管理區域的訪問。 有實體Employee具有屬性username ,但沒有密碼。 應該對LDAP服務器進行身份驗證,但是對管理區域的訪問應該僅限於實體Employee中存在的那些員工,即引用數據庫表。

對於LDAP身份驗證,我在Symfony 2.8中使用新的LDAP組件。

除此之外,應該有一個管理員帳戶作為in_memory用戶。

這就是我現在擁有的:

app/config/services.yml

services:
    app.ldap:
        class: Symfony\Component\Ldap\LdapClient
        arguments: ["ldaps://ldap.uni-rostock.de"]

    app.db_user_provider:
        class: AppBundle\Security\DbUserProvider
        arguments: ["@doctrine.orm.entity_manager"]

app/config/security.yml

security:
    role_hierarchy:
        ROLE_ADMIN:       ROLE_USER
        ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]

    providers:
        chain_provider:
            chain:
                providers: [db_user, app_users]

        in_memory:
            memory:
                users:
                    admin: { password: adminpass, roles: 'ROLE_ADMIN' }

        app_users:
            ldap:
                service: app.ldap
                base_dn: ou=people,o=uni-rostock,c=de
                search_dn: uid=testuser,ou=people,o=uni-rostock,c=de
                search_password: testpass
                filter: (uid={username})
                default_roles: ROLE_USER

        db_user:
            id: app.db_user_provider

    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false

        admin:
            anonymous: true
            pattern:   ^/
            form_login_ldap:
                provider: chain_provider
                service: app.ldap
                dn_string: "uid={username},ou=people,o=uni-rostock,c=de"
                check_path: /login_check
                login_path: /login
            form_login:
                provider: in_memory
                check_path: /login_check
                login_path: /login
            logout:
                path: /logout
                target: /

    access_control:
        - { path: ^/admin, roles: ROLE_USER }

    encoders:
        Symfony\Component\Security\Core\User\User: plaintext
        AppBundle\Entity\Employee: bcrypt

src/AppBundle/Entity/Employee.php

namespace AppBundle\Entity;

use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\EquatableInterface;
use Doctrine\ORM\Mapping as ORM;

class Employee implements UserInterface, EquatableInterface
{
    // other properties

    private $username;

    // getters and setters for the other properties

    public function getUsername()
    {
        return $this->username;
    }

    public function getRoles()
    {
        return array('ROLE_USER');
    }

    public function getPassword()
    {
        return null;
    }

    public function getSalt()
    {
        return null;
    }

    public function eraseCredentials()
    {
    }

    public function isEqualTo(UserInterface $user)
    {
        if (!$user instanceof Employee) {
            return false;
        }

        if ($this->username !== $user->getUsername()) {
            return false;
        }

        return true;
    }
}

src/AppBundle/Security/DbUserProvider.php

<?php

namespace AppBundle\Security;

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 Doctrine\ORM\EntityManager;
use AppBundle\Entity\Employee;

class DbUserProvider implements UserProviderInterface
{
    private $em;

    public function __construct(EntityManager $em)
    {
        $this->em = $em;
    }

    public function loadUserByUsername($username)
    {
        $repository = $this->em->getRepository('AppBundle:Employee');
        $user = $repository->findOneByUsername($username);

        if ($user) {
            return new Employee();
        }

        throw new UsernameNotFoundException(
            sprintf('Username "%s" does not exist.', $username)
        );
    }

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

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

    public function supportsClass($class)
    {
        return $class === 'AppBundle\Entity\Employee';
    }
}

針對LDAP的身份驗證就像一個魅力。 當數據庫中的員工嘗試登錄時,他/她將被重定向到主頁('/')並且登錄失敗。 不在數據庫中的所有其他用戶都可以毫無問題地登錄。

這正是我想要的反面!

如果我像這樣鏈接提供者:

chain_provider:
    chain:
        providers: [app_users, db_user]

然后甚至沒有調用方法loadUserByUsername ,所有用戶都可以登錄,數據庫中的用戶和非數據庫的用戶。

在任何情況下, in_memory用戶admin都可以in_memory登錄。

我感謝任何幫助。 如果有人認為我的整個方法都很糟糕並且知道更好的方法,那么請不要饒恕批評者。

我知道有FOSUserBundle和SonataUserBundle,但我更喜歡自定義用戶提供商,因為我不想膨脹實體Employee,因為我真的不需要所有這些屬性,如密碼,鹽,isLocked等。我也不喜歡我認為在我的特定情況下配置SonataUserBundle會簡單得多。 如果您仍然認為有更優雅的方式來完成這兩個捆綁包的任務,我將很高興得到一個好的建議。

您可以為每個防火牆配置用戶檢查程序,您的數據庫用戶提供程序實際上不是用戶提供程序,因為它沒有驗證用戶所需的所有信息(例如密碼)所以我要做的是,我會刪除數據庫用戶提供程序並添加用戶檢查程序,用戶檢查程序的主要思想是在身份驗證過程中添加其他檢查,在您的情況下我們需要檢查用戶是否在employee表中

你需要做三件事來實現這個

實現UserCheckerInterface Symfony\\Component\\Security\\Core\\User\\UserCheckerInterface

checkPostAuth()方法中檢查用戶是否在employee表中

將您的新用戶檢查器作為服務公開

services:
    app.employee_user_checker:
        class: Path\To\Class\EmployeeUserChecker

更改防火牆配置以使用新的用戶檢查程序

security:
    firewalls:
        admin:
            pattern: ^/admin
            user_checker: app.employee_user_checker
            #...

閱讀更多

暫無
暫無

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

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