[英]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.