- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在使用 Symfony 5.1 并尝试实现 LDAP 身份验证,而用户属性(用户名、角色等)存储在 MySQL 数据库中。因此我为 Doctrine 添加了一个 User Entity,并配置了 Documentation 对应的 services.yml 和 security.yml。
我还使用 Maker Bundle 生成了一个 LoginFormAuthenticator
,它似乎使用了 Guard Authenticator 模块。
当我尝试登录时,它看起来好像没有做任何与 LDAP 相关的事情。我还使用 tcpdump 监听了 TCP 包,但没有看到任何到 LDAP 服务器的流量。
这是我的代码:
services.yml
:
services:
_defaults:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
App\:
resource: '../src/*'
exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'
App\Controller\:
resource: '../src/Controller'
tags: ['controller.service_arguments']
Symfony\Component\Ldap\Ldap:
arguments: ['@Symfony\Component\Ldap\Adapter\ExtLdap\Adapter']
Symfony\Component\Ldap\Adapter\ExtLdap\Adapter:
arguments:
- host: <ldap-IP>
port: 389
options:
protocol_version: 3
referrals: false
security.yml
:
security:
encoders:
App\Entity\User:
algorithm: auto
app_user_provider:
entity:
class: App\Entity\User
property: email
my_ldap:
ldap:
service: Symfony\Component\Ldap\Ldap
base_dn: "<base_dn>"
search_dn: "<search_dn>"
search_password: "<password>"
default_roles: ROLE_USER
uid_key: sAMAccountName
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: true
lazy: true
provider: my_ldap
form_login_ldap:
login_path: login
check_path: login
service: Symfony\Component\Ldap\Ldap
dn_string: 'uid={username},OU=Test,DC=domain,DC=domain'
guard:
authenticators:
- App\Security\LoginFormAuthenticator
logout:
path: app_logout
# where to redirect after logout
target: index
access_control:
- { path: ^/$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin, roles: ROLE_ADMIN }
- { path: ^/profile, roles: ROLE_USER }
LoginFormAuthenticator
,我想问题出在 checkCredentials
函数中。我找到了 LdapBindAuthenticationProvider
类,它的目的似乎是再次检查 LDAP 的用户凭据,但我完全不确定我该怎么做:
<?php
namespace App\Security;
use Psr\Log\LoggerInterface;
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;
use Symfony\Component\Ldap\Ldap;
use Symfony\Component\Security\Core\Authentication\Provider\LdapBindAuthenticationProvider;
class LoginFormAuthenticator extends AbstractFormLoginAuthenticator implements PasswordAuthenticatedInterface
{
use TargetPathTrait;
public const LOGIN_ROUTE = 'app_login';
private $logger;
private $entityManager;
private $urlGenerator;
private $csrfTokenManager;
private $passwordEncoder;
private $ldap;
public function __construct(LoggerInterface $logger, EntityManagerInterface $entityManager, UrlGeneratorInterface $urlGenerator, CsrfTokenManagerInterface $csrfTokenManager, UserPasswordEncoderInterface $passwordEncoder, Ldap $ldap, LdapBindAuthenticationProvider $form_login_ldap)
{
$this->logger = $logger;
$this->entityManager = $entityManager;
$this->urlGenerator = $urlGenerator;
$this->csrfTokenManager = $csrfTokenManager;
$this->passwordEncoder = $passwordEncoder;
$this->ldap = $ldap;
}
public function supports(Request $request): ?bool
{
return self::LOGIN_ROUTE === $request->attributes->get('_route')
&& $request->isMethod('POST');
}
public function getCredentials(Request $request)
{
$credentials = [
'username' => $request->request->get('username'),
'password' => $request->request->get('password'),
'csrf_token' => $request->request->get('_csrf_token'),
];
$request->getSession()->set(
Security::LAST_USERNAME,
$credentials['username']
);
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['username']]);
if (!$user) {
// user not found in db, but may exist in ldap:
$user = $userProvider->loadUserByUsername($credentials['username']);
if (!$user) {
// user simply doesn't exist
throw new CustomUserMessageAuthenticationException('Email could not be found.');
} else {
// user never logged in before, create user in DB and proceed...
// TODO
}
}
return $user;
}
public function checkCredentials($credentials, UserInterface $user)
{
// TODO: how to use the LdapBindAuthenticationProvider here to check the users credentials agains LDAP?
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);
}
}
很遗憾,我没有找到任何示例代码。
感谢 T. van den Berg 的回答,我终于设法让身份验证部分正常工作。我从 security.yml 中删除了 LoginFormAuthenticator Guard 并稍微调整了 form_login_ldap。
security:
encoders:
App\Entity\User:
algorithm: auto
providers:
app_user_provider:
entity:
class: App\Entity\User
property: email
my_ldap:
ldap:
service: Symfony\Component\Ldap\Ldap
base_dn: '<baseDN>'
search_dn: '<bindDN>'
search_password: '<bindDN password>'
default_roles: ['ROLE_USER']
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: true
lazy: true
provider: my_ldap
form_login_ldap:
login_path: app_login
check_path: app_login
service: Symfony\Component\Ldap\Ldap
dn_string: '<baseDN>'
query_string: '(sAMAccountName={username})'
search_dn: '<bindDN>'
search_password: '<bindDN password>'
logout:
path: app_logout
target: index
access_control:
- { path: ^/$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin, roles: ROLE_ADMIN }
- { path: ^/profile, roles: ROLE_USER }
它现在使用 LDAPUserProvider 来使用 LDAP 服务用户(绑定(bind) DN)通过其登录名(sAMAccountName)获取用户 LDAP 对象,然后在第二个请求中使用此 LDAP 对象的可分辨名称(DN)来使用提供的密码对 LDAP 服务器进行另一次身份验证。到目前为止一切正常。
唯一缺少的是数据库存储的用户实体。我的想法如下:
密码未保存在数据库中,但其他应用程序特定信息在 LDAP 中不可用(例如上次事件)。
有人知道如何实现吗?
最佳答案
如果您想在登录后将 LDAP 用户保存到本地数据库,则可以使用此 bundle ldaptools/ldaptools-bundle(或 Maks3w/FR3DLdapBundle)
。
这就是我让它工作的方式(没有外部包):
security:
encoders:
App\Entity\User:
algorithm: auto
# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
providers:
# used to reload user from session & other features (e.g. switch_user)
app_user_provider:
entity:
class: App\Entity\User
property: username
ldap_server:
ldap:
service: Symfony\Component\Ldap\Ldap
base_dn: "dc=example,dc=com"
search_dn: "cn=read-only-admin,dc=example,dc=com"
search_password: "password"
default_roles: ROLE_USER
uid_key: uid
chain_provider:
chain:
providers: [ 'app_user_provider', 'ldap_server' ]
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: lazy
provider: chain_provider
form_login:
login_path: app_login
check_path: app_login
form_login_ldap:
login_path: app_login
check_path: app_login
service: Symfony\Component\Ldap\Ldap
dn_string: 'uid={username},dc=example,dc=com'
logout:
path: app_logout
<?php
namespace App\EventListener;
use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
class LoginEventListener
{
/**
* @var EntityManagerInterface
*/
protected $em;
/**
* @var UserPasswordEncoderInterface
*/
private $encoder;
/**
* LoginEventListener constructor.
* @param EntityManagerInterface $em
* @param UserPasswordEncoderInterface $encoder
*/
public function __construct(EntityManagerInterface $em, UserPasswordEncoderInterface $encoder)
{
$this->em = $em;
$this->encoder = $encoder;
}
/**
* @param InteractiveLoginEvent $event
*/
public function onLoginSuccess(InteractiveLoginEvent $event)
{
$request = $event->getRequest();
$token = $event->getAuthenticationToken();
$loggedUser = $token->getUser();
// If the logged user is not an instance of User (not ldapUser), then it hasn't been saved to the database. So save it..
if(!($loggedUser instanceof User)) {
$user = new User();
$user->setUsername($request->request->get('_username'));
$user->setPassword($this->encoder->encodePassword($user, $request->request->get('_password')));
$user->setRoles($loggedUser->getRoles());
$this->em->persist($user);
$this->em->flush();
}
}
# ldap service
Symfony\Component\Ldap\Ldap:
arguments: [ '@Symfony\Component\Ldap\Adapter\ExtLdap\Adapter' ]
Symfony\Component\Ldap\Adapter\ExtLdap\Adapter:
arguments:
- host: ldap.forumsys.com
port: 389
options:
protocol_version: 3
referrals: false
app_bundle.event.login_listener:
class: App\EventListener\LoginEventListener
arguments: [ '@doctrine.orm.entity_manager', '@security.user_password_encoder.generic' ]
tags:
- { name: kernel.event_listener, event: security.interactive_login, method: onLoginSuccess }
关于php - 交响乐 5.1 : LDAP Authentication with Entity User Provider,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62898066/
我有这些实体(这只是我为这篇文章创建的抽象): 语言 区 说明 这些是它们之间的引用: 区 * - 1 语言 说明 * - 1 语言 区 1 - 1 说明 如果我这样取: var myFetch =
经过大量谷歌搜索后,除了降级 hibernate 版本之外,我没有找到问题的答案。但我在 2003 年类似的帖子中遇到了这种情况。 问题是什么: //in the first session I d
我听说过 linq to entities 。 Entity Framework 是利用linq to entities吗? 最佳答案 LINQ to Entities 是 Entity Framew
我是 Entity Framework 和 ASP.Net MVC 的新手,主要从教程中学习,对任何一个都没有深入了解。 (我确实有 .Net 2.0、ADO.Net 和 WebForms 方面的经验
如果我编写 LINQ to Entities 查询,该查询是否会转换为提供程序理解的 native 查询(即 SqlClient)? 或者 它是否会转换为实体 SQL,然后 Entity Framew
这个问题已经有答案了: EF: Include with where clause [duplicate] (5 个回答) 已关闭 2 年前。 看来我无法从数据库中获取父级及其子级的子集。 例如...
我开始在一家新公司工作,我必须在一个旧项目上使用 C++ 工作。所以,我忘记了一些 C++ 本身的代码结构。在一个函数中,我在一个函数中有双冒号::,但我不知道如何理解它。 例如,我知道如果我有 EN
我写了一个方法来允许为 orderby 子句传递一个表达式,但我遇到了这个问题。 Unable to cast the type 'System.DateTime' to type 'System.I
简单的问题:LINQ to Entities 和 Entity Framework 有什么区别?到目前为止,我认为这两个名称是用来描述同一个查询的,但我开始觉得事实并非如此。 最佳答案 Entity
我想使用 Entity Framework 。但是,我还要求允许我的用户在我们的系统中定义自定义字段。我想仍然使用 Entity Framework ,而不是使用具有哈希表属性的分部类。 下面是我想到
我正在阅读这个 E.F. 团队博客的这个系列 http://blogs.msdn.com/b/adonet/archive/2011/01/27/using-dbcontext-in-ef-featu
我正在使用 EF6 开发插件应用程序,代码优先。 我有一个名为 User 的实体的主要上下文。 : public class MainDataContext : DbContext { pub
当我得到最后的 .edmx 时,我遇到了问题。 我收到一条消息说 错误 11007:未映射实体类型“pl_Micro”。 查看设计器 View ,我确实看到该表确实存在。 我怎样才能克服这个消息? 最
我已阅读与使用 Entity Framework 时在 Linq to Entities (.NET 3.5) 中实现等效的 LEFT OUTER JOIN 相关的所有帖子,但尚未找到解决以下问题的方
使用 WCF RIA 服务和 Entity Framework 4. 我有 3 个 DTO:学校、州、区。 州 DTO 有一个地区属性(property),其构成。学校 DTO 有一个国家属性(pro
我有一个 Employee 实体,它继承自一个继承自 Resource 实体(Employee -> Person -> Resource)的 Person 实体。是否可以通过编程方式获取 Emplo
我有一个使用 JPA 的 java 应用程序。 假设我有一个名为 Product 的实体与 name和 price属性(所有实体都有一个 id 属性)。 自然我可以得到一个List相当容易(来自查询或
我有一个 Entity Framework 类,其中有两个指向另一个对象的引用 public class Review { [Key] public int Id {get;s
我是 Symfony 2 的新手,我想知道一些事情: 假设我的项目中有 2 个 bundle 。我想在两个包中使用从我的数据库生成的实体。 我应该在哪里生成实体? (对我来说,最好的方法是在 bund
我想在具有方法和属性的部分类中扩展 EF 实体。我经常这样做。 但是现在我需要将来自该实体的数据与来自其他实体的数据结合起来。因此,我需要能够访问实体 objectcontext(如果附加)来进行这些
我是一名优秀的程序员,十分优秀!