本文目录导读:

我来帮你详细介绍Symfony框架中LDAP与目录服务集成的实现方案。
基础LDAP配置
安装LDAP组件
composer require symfony/ldap
配置LDAP连接
# config/packages/ldap.yaml
ldap:
# 默认连接
default:
host: 'ldap.example.com'
port: 389
encryption: tls # tls, ssl, none
options:
protocol_version: 3
referrals: false
# 备用连接
backup:
host: 'backup-ldap.example.com'
port: 636
encryption: ssl
options:
protocol_version: 3
network_timeout: 10
LDAP认证服务
创建LDAP服务类
<?php
namespace App\Service;
use Symfony\Component\Ldap\Ldap;
use Symfony\Component\Ldap\Entry;
use Symfony\Component\Ldap\Exception\LdapException;
class LdapService
{
private Ldap $ldap;
private string $baseDn;
public function __construct(Ldap $ldap, string $baseDn = 'dc=example,dc=com')
{
$this->ldap = $ldap;
$this->baseDn = $baseDn;
}
/**
* 用户认证
*/
public function authenticate(string $username, string $password): ?Entry
{
try {
// 绑定管理员账号(用于搜索)
$this->ldap->bind('cn=admin,dc=example,dc=com', 'admin_password');
// 搜索用户
$query = $this->ldap->query(
$this->baseDn,
sprintf('(uid=%s)', $username)
);
$entries = $query->execute();
if ($entries->count() === 0) {
return null;
}
$userEntry = $entries[0];
$userDn = $userEntry->getDn();
// 验证用户密码
$this->ldap->bind($userDn, $password);
return $userEntry;
} catch (LdapException $e) {
throw new \RuntimeException('LDAP认证失败: ' . $e->getMessage());
}
}
/**
* 查询用户信息
*/
public function findUser(string $uid): ?Entry
{
try {
$this->ldap->bind('cn=admin,dc=example,dc=com', 'admin_password');
$query = $this->ldap->query(
$this->baseDn,
sprintf('(|(uid=%s)(mail=%s))', $uid, $uid)
);
$results = $query->execute();
return $results->count() > 0 ? $results[0] : null;
} catch (LdapException $e) {
return null;
}
}
/**
* 获取用户组信息
*/
public function getUserGroups(string $userDn): array
{
try {
$this->ldap->bind('cn=admin,dc=example,dc=com', 'admin_password');
$query = $this->ldap->query(
$this->baseDn,
sprintf('(member=%s)', $userDn)
);
$results = $query->execute();
$groups = [];
foreach ($results as $entry) {
$groups[] = $entry->getAttribute('cn')[0];
}
return $groups;
} catch (LdapException $e) {
return [];
}
}
}
自定义LDAP用户提供器
创建用户实体
<?php
namespace App\Entity;
use Symfony\Component\Security\Core\User\UserInterface;
class LdapUser implements UserInterface
{
private string $username;
private array $roles;
private string $email;
private string $displayName;
public function __construct(string $username, array $roles = ['ROLE_USER'])
{
$this->username = $username;
$this->roles = $roles;
}
public function getRoles(): array
{
return $this->roles;
}
public function getPassword(): ?string
{
return null; // LDAP管理密码
}
public function getSalt(): ?string
{
return null;
}
public function getUsername(): string
{
return $this->username;
}
public function getUserIdentifier(): string
{
return $this->username;
}
public function eraseCredentials(): void
{
// 如果需要清除敏感数据
}
// Getter/Setter
public function getEmail(): string
{
return $this->email;
}
public function setEmail(string $email): void
{
$this->email = $email;
}
public function getDisplayName(): string
{
return $this->displayName;
}
public function setDisplayName(string $displayName): void
{
$this->displayName = $displayName;
}
}
创建LDAP用户提供器
<?php
namespace App\Security;
use App\Entity\LdapUser;
use App\Service\LdapService;
use Symfony\Component\Ldap\Entry;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Symfony\Component\Security\Core\Exception\UserNotFoundException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
class LdapUserProvider implements UserProviderInterface
{
private LdapService $ldapService;
public function __construct(LdapService $ldapService)
{
$this->ldapService = $ldapService;
}
public function loadUserByIdentifier(string $identifier): UserInterface
{
$entry = $this->ldapService->findUser($identifier);
if (!$entry) {
throw new UserNotFoundException(sprintf('User "%s" not found', $identifier));
}
return $this->createUserFromEntry($entry);
}
public function loadUserByUsername(string $username): UserInterface
{
return $this->loadUserByIdentifier($username);
}
public function refreshUser(UserInterface $user): UserInterface
{
if (!$user instanceof LdapUser) {
throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user)));
}
return $this->loadUserByIdentifier($user->getUserIdentifier());
}
public function supportsClass(string $class): bool
{
return LdapUser::class === $class;
}
private function createUserFromEntry(Entry $entry): LdapUser
{
$username = $entry->getAttribute('uid')[0];
$email = $entry->getAttribute('mail')[0] ?? '';
$displayName = $entry->getAttribute('displayName')[0] ?? $username;
// 基于LDAP组分配角色
$roles = $this->determineRoles($entry);
$user = new LdapUser($username, $roles);
$user->setEmail($email);
$user->setDisplayName($displayName);
return $user;
}
private function determineRoles(Entry $entry): array
{
$roles = ['ROLE_USER'];
$groups = $this->ldapService->getUserGroups($entry->getDn());
// 映射LDAP组到角色
$roleMappings = [
'admins' => 'ROLE_ADMIN',
'managers' => 'ROLE_MANAGER',
];
foreach ($groups as $group) {
if (isset($roleMappings[$group])) {
$roles[] = $roleMappings[$group];
}
}
return $roles;
}
}
自定义LDAP认证器
创建认证器
<?php
namespace App\Security;
use App\Service\LdapService;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\Authenticator\AbstractAuthenticator;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
class LdapAuthenticator extends AbstractAuthenticator
{
private LdapService $ldapService;
private LdapUserProvider $userProvider;
public function __construct(LdapService $ldapService, LdapUserProvider $userProvider)
{
$this->ldapService = $ldapService;
$this->userProvider = $userProvider;
}
public function supports(Request $request): ?bool
{
return $request->attributes->get('_route') === 'app_login'
&& $request->isMethod('POST');
}
public function authenticate(Request $request): Passport
{
$username = $request->request->get('_username', '');
$password = $request->request->get('_password', '');
// LDAP认证
$userEntry = $this->ldapService->authenticate($username, $password);
if (!$userEntry) {
throw new AuthenticationException('Invalid credentials.');
}
return new SelfValidatingPassport(
new UserBadge($username, function($username) {
return $this->userProvider->loadUserByIdentifier($username);
})
);
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
return new RedirectResponse('/dashboard');
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
{
$request->getSession()->getFlashBag()->add('error', 'Login failed: ' . $exception->getMessage());
return new RedirectResponse('/login');
}
}
安全配置
# config/packages/security.yaml
security:
providers:
ldap_users:
id: App\Security\LdapUserProvider
firewalls:
main:
lazy: true
provider: ldap_users
custom_authenticators:
- App\Security\LdapAuthenticator
logout:
path: app_logout
target: app_login
# 登录表单配置
form_login:
login_path: app_login
check_path: app_login
# 记住登录功能
remember_me:
secret: '%kernel.secret%'
lifetime: 604800 # 7天
path: /
access_control:
- { path: ^/login, roles: PUBLIC_ACCESS }
- { path: ^/admin, roles: ROLE_ADMIN }
- { path: ^/dashboard, roles: ROLE_USER }
高级功能实现
LDAP用户同步
<?php
namespace App\Service;
use Doctrine\ORM\EntityManagerInterface;
use App\Entity\User;
use Symfony\Component\Ldap\Ldap;
class LdapSyncService
{
private Ldap $ldap;
private EntityManagerInterface $entityManager;
private string $baseDn;
public function __construct(Ldap $ldap, EntityManagerInterface $entityManager, string $baseDn = 'dc=example,dc=com')
{
$this->ldap = $ldap;
$this->entityManager = $entityManager;
$this->baseDn = $baseDn;
}
/**
* 同步所有用户
*/
public function syncAllUsers(): SyncResult
{
$result = new SyncResult();
try {
$this->ldap->bind('cn=admin,dc=example,dc=com', 'admin_password');
$query = $this->ldap->query(
$this->baseDn,
'(objectClass=inetOrgPerson)',
['attributes' => ['uid', 'mail', 'cn', 'sn', 'department']]
);
$entries = $query->execute();
foreach ($entries as $entry) {
$uid = $entry->getAttribute('uid')[0];
// 检查用户是否存在
$existingUser = $this->entityManager
->getRepository(User::class)
->findOneBy(['username' => $uid]);
if ($existingUser) {
$this->updateUser($existingUser, $entry);
$result->updated++;
} else {
$this->createUser($entry);
$result->created++;
}
}
$this->entityManager->flush();
} catch (\Exception $e) {
$result->errors[] = $e->getMessage();
}
return $result;
}
private function createUser(Entry $entry): void
{
$user = new User();
$user->setUsername($entry->getAttribute('uid')[0]);
$user->setEmail($entry->getAttribute('mail')[0] ?? '');
$user->setDisplayName($entry->getAttribute('cn')[0] ?? '');
$user->setDepartment($entry->getAttribute('department')[0] ?? '');
$user->setEnabled(true);
$this->entityManager->persist($user);
}
private function updateUser(User $user, Entry $entry): void
{
$user->setEmail($entry->getAttribute('mail')[0] ?? $user->getEmail());
$user->setDisplayName($entry->getAttribute('cn')[0] ?? $user->getDisplayName());
$user->setDepartment($entry->getAttribute('department')[0] ?? $user->getDepartment());
}
}
class SyncResult
{
public int $created = 0;
public int $updated = 0;
public int $deleted = 0;
public array $errors = [];
}
LDAP目录浏览器
<?php
namespace App\Controller;
use App\Service\LdapService;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class LdapBrowserController extends AbstractController
{
private LdapService $ldapService;
public function __construct(LdapService $ldapService)
{
$this->ldapService = $ldapService;
}
#[Route('/admin/ldap/browse', name: 'ldap_browse')]
public function browse(Request $request): Response
{
$baseDn = $request->query->get('dn', 'dc=example,dc=com');
$filter = $request->query->get('filter', '(objectClass=*)');
try {
$entries = $this->ldapService->search($baseDn, $filter);
return $this->render('admin/ldap/browse.html.twig', [
'entries' => $entries,
'baseDn' => $baseDn,
'filter' => $filter,
]);
} catch (\Exception $e) {
$this->addFlash('error', 'LDAP browse failed: ' . $e->getMessage());
return $this->redirectToRoute('admin_dashboard');
}
}
#[Route('/admin/ldap/entry/{dn}', name: 'ldap_entry')]
public function viewEntry(string $dn): Response
{
try {
$entry = $this->ldapService->getEntry($dn);
return $this->render('admin/ldap/entry.html.twig', [
'entry' => $entry,
]);
} catch (\Exception $e) {
$this->addFlash('error', 'Failed to load LDAP entry: ' . $e->getMessage());
return $this->redirectToRoute('ldap_browse');
}
}
}
测试LDAP连接
<?php
namespace App\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Ldap\Ldap;
class LdapTestCommand extends Command
{
protected static $defaultName = 'app:ldap:test';
private Ldap $ldap;
public function __construct(Ldap $ldap)
{
parent::__construct();
$this->ldap = $ldap;
}
protected function configure(): void
{
$this
->setDescription('Test LDAP connection and configuration')
->addOption('username', 'u', InputOption::VALUE_REQUIRED, 'Username to test')
->addOption('password', 'p', InputOption::VALUE_REQUIRED, 'Password to test');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$io->title('LDAP Connection Test');
try {
// 测试基本连接
$io->section('Testing connection...');
$this->ldap->bind();
$io->success('Connection successful');
// 如果提供了用户名和密码
if ($username = $input->getOption('username')) {
$password = $input->getOption('password');
$io->section('Testing authentication...');
try {
$this->ldap->bind(
sprintf('uid=%s,ou=users,dc=example,dc=com', $username),
$password
);
$io->success("Authentication successful for user: $username");
} catch (\Exception $e) {
$io->error("Authentication failed: " . $e->getMessage());
}
}
// 显示服务器信息
$io->section('Server Information');
$io->table(
['Property', 'Value'],
[
['Host', getenv('LDAP_HOST') ?: 'localhost'],
['Port', getenv('LDAP_PORT') ?: '389'],
['Base DN', 'dc=example,dc=com'],
['Protocol', 'LDAPv3'],
]
);
} catch (\Exception $e) {
$io->error('Connection failed: ' . $e->getMessage());
return Command::FAILURE;
}
return Command::SUCCESS;
}
}
最佳实践
错误处理
// 创建LDAP异常监听器
namespace App\EventSubscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\Ldap\Exception\LdapException;
class LdapExceptionSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
ExceptionEvent::class => 'onKernelException',
];
}
public function onKernelException(ExceptionEvent $event): void
{
$exception = $event->getThrowable();
if ($exception instanceof LdapException) {
// 记录日志
// 返回用户友好的错误信息
}
}
}
安全建议
# config/packages/ldap.yaml
ldap:
default:
host: '%env(LDAP_HOST)%'
port: '%env(int:LDAP_PORT)%'
encryption: tls
options:
protocol_version: 3
# 安全选项
network_timeout: 5
ssl:
verify_peer: true
verify_peer_name: true
allow_self_signed: false
# 连接池配置
connection_pool:
max_connections: 10
min_connections: 2
idle_timeout: 300
这个完整的LDAP集成方案包含了从基础配置到高级功能的所有内容,可以根据你的具体需求进行调整和扩展。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播