我无法使用服务和安全配置(Symfony 3.3)针对多个ldap库验证用户。
我正在使用Ldap symfony组件并为两个不同的主机创建2个ldap配置服务。
services.yml:
ldap1:
class: Symfony\Component\Ldap\Ldap
arguments: ['@ldap_adapter1']
ldap_adapter1:
class: Symfony\Component\Ldap\Adapter\ExtLdap\Adapter
arguments:
- host: serldap.abc.fr
port: 389
options:
protocol_version: 3
referrals: false
ldap2:
class: Symfony\Component\Ldap\Ldap
arguments: ['@ldap_adapter2']
ldap_adapter2:
class: Symfony\Component\Ldap\Adapter\ExtLdap\Adapter
arguments:
- host: ldap.xyz.fr
port: 389
options:
protocol_version: 3
referrals: false
security.yml:
security:
providers:
chain_provider:
chain:
providers: [ldap_1, ldap_2]
ldap_1:
ldap:
service: ldap1
base_dn: ou=abcaccount,dc=abc,dc=fr
search_dn: uid=a1,ou=abcaccount,dc=abc,dc=fr
search_password: pass1
default_roles: ROLE_USER
uid_key: uid
ldap_2:
ldap:
service: ldap2
base_dn: ou=xyzaccount,dc=xyz,dc=fr
search_dn: uid=a2,ou=xyzaccount,dc=xyz,dc=fr
search_password: pass2
default_roles: ROLE_USER
uid_key: uid
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
pattern: ^/
anonymous: ~
provider: chain_provider
form_login_ldap:
login_path: login
check_path: login
access_control:
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/, roles: ROLE_USER }
如果我在form_login_ldap下添加dn_string。 I,E:
dn_string: 'uid={username},ou=xyzaccount,dc=xyz,dc=fr'
这样可行,问题是这只能配置一个Ldap。没有这一行我得到以下错误:
php.DEBUG:警告:ldap_bind():无法绑定到服务器:无效的DN语法
2个问题:
EG
dn_string: 'uid={username},ou={chosenOUInForm},dc={chosenDC1InForm},dc={chosenDC2InForm}'
提前致谢。
我设法通过将request_matcher参数添加到安全性来使其工作:
security:
providers:
chain_provider:
chain:
providers: [ldap_1, ldap_2]
ldap_1:
ldap:
service: ldap1
base_dn: ou=abcaccount,dc=abc,dc=fr
search_dn: ~
search_password: ~
default_roles: ROLE_USER
uid_key: uid
ldap_2:
ldap:
service: ldap2
base_dn: ou=xyzaccount,dc=xyz,dc=fr
search_dn: ~
search_password: ~
default_roles: ROLE_USER
uid_key: uid
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
base:
pattern: ^/
request_matcher: app.base_firewall_matcher
anonymous: ~
form_login_ldap:
service: ldap1 # this doesn't matter for the base firewall as it is never passed with no check_path
login_path: login
one:
pattern: ^/
request_matcher: app.first_firewall_matcher
anonymous: ~
provider: ldap_1
form_login_ldap:
service: ldap1
login_path: login
check_path: login_1_check
dn_string: 'uid={username},ou=abcaccount,dc=abc,dc=fr'
two:
pattern: ^/
request_matcher: app.second_firewall_matcher
anonymous: ~
provider: ldap_2
form_login_ldap:
service: ldap2
login_path: login
check_path: login_2_check
dn_string: 'uid={username},ou=xyzaccount,dc=xyz,dc=fr'
access_control:
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/, roles: ROLE_USER }
表单页面(登录路径返回的twig页面)将有一个按钮将表单提交到login_1_check,一个按钮将表单提交到login_2_check:
<form action="{{ path('login_1_check') }}" method="post">
<input type="text" id="username" name="_username"/>
<input type="password" id="password" name="_password" />
<button type="submit">login to LDAP 1</button>
<button type="submit" formaction="{{ path('login_2_check') }}">login to LDAP 2</button>
</form>
防火墙按顺序命中,因此首先检查基本防火墙,如果返回true(如果用户尚未经过身份验证或登录表单未提交),则会转到登录路径。
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestMatcherInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
class BaseFirewallMatcher implements RequestMatcherInterface
{
// Should only be matched on login page when user first arrives to GET the form, if already validated with one
// of the other two security providers, they will be checked against their respective firewalls.
public function matches(Request $request){
$session = $request->getSession();
$oneValidated = $session->get("_security_one", null);
$twoValidated = $session->get("_security_two", null);
// If already validated with another security provider
if($oneValidated or $twoValidated){
return false;
}
else{
$url = $request->getPathInfo();
// If these two logins are not matched
if($url != "/login_1_check" && $url != "/login_2_check"){
return true;
}
else{
return false;
}
}
}
}
当用户通过按下其中一个按钮提交时,基本防火墙将不会被触发,因为BaseFirewallMatcher将返回false。然后,第一次LDAP检查将转到以下请求匹配器。
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestMatcherInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
class FirstFirewallMatcher implements RequestMatcherInterface
{
public function matches(Request $request){
$session = $request->getSession();
$oneValidated = $session->get("_security_one", null);
$twoValidated = $session->get("_security_two", null);
if($twoValidated){
return false;
}
else{
if($oneValidated){
return true;
}
$url = $request->getPathInfo();
if ($url == "/login_1_check"){
return true;
}
else{
return false;
}
}
}
}
返回true,即如果用户已使用ldap1进行了身份验证,或者用户已选择登录ldap1。然后触发防火墙。
如果这返回false,例如当用户选择登录ldap2或已经使用ldap2进行身份验证时 - 它会转到下一个请求匹配器:
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestMatcherInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
class SecondFirewallMatcher implements RequestMatcherInterface
{
public function matches(Request $request){
$session = $request->getSession();
$oneValidated = $session->get("_security_one", null);
$twoValidated = $session->get("_security_two", null);
if($oneValidated){
return false;
}
else{
if($twoValidated){
return true;
}
$url = $request->getPathInfo();
dump($url);
if ($url == "/login_2_check"){
return true;
}
else{
return false;
}
}
}
}
请记住为每个request_matcher设置服务:
app.base_firewall_matcher:
class: {pathtofirewallmatcher}\BaseFirewallMatcher
app.first_firewall_matcher:
class: {pathtofirewallmatcher}\FirstFirewallMatcher
app.second_firewall_matcher:
class: {pathtofirewallmatcher}\SecondFirewallMatcher
代码可能不完美,因为最后我转向了不同类型的身份验证。但这似乎在当时有效,我认为你可以根据需要添加尽可能多的ldaps。