Symfony:根据LDAP服务器对用户进行身份验证,但仅当用户名位于自定义db表中时才允许登录

es撒

首先简要说明一下我的任务。我正在使用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_memoryadmin用户可以登录没有在任何情况下的一个问题。

感谢您的帮助。如果有人认为我的整体方法不好,并且知道更好的方法,请不要批评家。

我知道有FOSUserBundle和SonataUserBundle,但是我更喜欢自定义用户提供程序,因为我不想want肿实体Employee,因为我真的不需要所有那些属性,例如密码,salt,isLocked等。我认为在我的特殊情况下配置SonataUserBundle不会简单得多。如果您仍然认为使用这两个捆绑包可以找到一种更优雅的方式来完成我的任务,我将不胜感激获得良好的建议。

赢了

您可以为每个防火墙配置用户检查器,您的数据库用户提供者并不是真正的用户提供者,因为它不具备验证用户身份所需的所有信息(例如密码),因此我要做的是,我将删除db用户提供者并添加一个用户检查器,该用户检查器的主要思想是在身份验证过程中添加其他检查,在这种情况下,我们需要检查用户是否在employee表中

您需要做三件事来实现这一目标

实现UserCheckerInterface Symfony\Component\Security\Core\User\UserCheckerInterface

检查用户是否在employee表中或不在checkPostAuth()方法中

将您的新用户检查器公开为服务

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

更改防火墙配置以使用新的用户检查器

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

阅读更多

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

Related 相关文章

热门标签

归档