如何使用Spring Security对Active Directory服务器进行身份验证?
我在写一个需要用户登录的Spring Web应用程序。我的公司有一个Active Directory服务器,我想利用这个目的。但是,我无法使用Spring Security连接到服务器。如何使用Spring Security对Active Directory服务器进行身份验证?
我使用Spring 2.5.5和Spring Security 2.0.3以及Java 1.6。
如果我将LDAP URL更改为错误的IP地址,它不会抛出异常或任何内容,所以我想知道是否尝试连接到服务器开始。
尽管Web应用程序启动得很好,但我输入到登录页面的任何信息都被拒绝。我以前使用过的InMemoryDaoImpl工作正常,所以我的应用程序的其余部分似乎配置正确。
这里是我的与安全相关的豆类:
<beans:bean id="ldapAuthProvider" class="org.springframework.security.providers.ldap.LdapAuthenticationProvider">
<beans:constructor-arg>
<beans:bean class="org.springframework.security.providers.ldap.authenticator.BindAuthenticator">
<beans:constructor-arg ref="initialDirContextFactory" />
<beans:property name="userDnPatterns">
<beans:list>
<beans:value>CN={0},OU=SBSUsers,OU=Users,OU=MyBusiness,DC=Acme,DC=com</beans:value>
</beans:list>
</beans:property>
</beans:bean>
</beans:constructor-arg>
</beans:bean>
<beans:bean id="userDetailsService" class="org.springframework.security.userdetails.ldap.LdapUserDetailsManager">
<beans:constructor-arg ref="initialDirContextFactory" />
</beans:bean>
<beans:bean id="initialDirContextFactory" class="org.springframework.security.ldap.DefaultInitialDirContextFactory">
<beans:constructor-arg value="ldap://192.168.123.456:389/DC=Acme,DC=com" />
</beans:bean>
我拥有了你做同样的敲打,我的头抵墙式体验,并最终写入,做一个LDAP自定义身份验证提供者针对Active Directory服务器进行查询。
所以我的安全相关的豆类:
<beans:bean id="contextSource"
class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
<beans:constructor-arg value="ldap://hostname.queso.com:389/" />
</beans:bean>
<beans:bean id="ldapAuthenticationProvider"
class="org.queso.ad.service.authentication.LdapAuthenticationProvider">
<beans:property name="authenticator" ref="ldapAuthenticator" />
<custom-authentication-provider />
</beans:bean>
<beans:bean id="ldapAuthenticator"
class="org.queso.ad.service.authentication.LdapAuthenticatorImpl">
<beans:property name="contextFactory" ref="contextSource" />
<beans:property name="principalPrefix" value="QUESO\" />
</beans:bean>
然后LdapAuthenticationProvider可疑类:
/**
* Custom Spring Security authentication provider which tries to bind to an LDAP server with
* the passed-in credentials; of note, when used with the custom {@link LdapAuthenticatorImpl},
* does <strong>not</strong> require an LDAP username and password for initial binding.
*
* @author Jason
*/
public class LdapAuthenticationProvider implements AuthenticationProvider {
private LdapAuthenticator authenticator;
public Authentication authenticate(Authentication auth) throws AuthenticationException {
// Authenticate, using the passed-in credentials.
DirContextOperations authAdapter = authenticator.authenticate(auth);
// Creating an LdapAuthenticationToken (rather than using the existing Authentication
// object) allows us to add the already-created LDAP context for our app to use later.
LdapAuthenticationToken ldapAuth = new LdapAuthenticationToken(auth, "ROLE_USER");
InitialLdapContext ldapContext = (InitialLdapContext) authAdapter
.getObjectAttribute("ldapContext");
if (ldapContext != null) {
ldapAuth.setContext(ldapContext);
}
return ldapAuth;
}
public boolean supports(Class clazz) {
return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(clazz));
}
public LdapAuthenticator getAuthenticator() {
return authenticator;
}
public void setAuthenticator(LdapAuthenticator authenticator) {
this.authenticator = authenticator;
}
}
然后LdapAuthenticatorImpl类:
/**
* Custom Spring Security LDAP authenticator which tries to bind to an LDAP server using the
* passed-in credentials; does <strong>not</strong> require "master" credentials for an
* initial bind prior to searching for the passed-in username.
*
* @author Jason
*/
public class LdapAuthenticatorImpl implements LdapAuthenticator {
private DefaultSpringSecurityContextSource contextFactory;
private String principalPrefix = "";
public DirContextOperations authenticate(Authentication authentication) {
// Grab the username and password out of the authentication object.
String principal = principalPrefix + authentication.getName();
String password = "";
if (authentication.getCredentials() != null) {
password = authentication.getCredentials().toString();
}
// If we have a valid username and password, try to authenticate.
if (!("".equals(principal.trim())) && !("".equals(password.trim()))) {
InitialLdapContext ldapContext = (InitialLdapContext) contextFactory
.getReadWriteContext(principal, password);
// We need to pass the context back out, so that the auth provider can add it to the
// Authentication object.
DirContextOperations authAdapter = new DirContextAdapter();
authAdapter.addAttributeValue("ldapContext", ldapContext);
return authAdapter;
} else {
throw new BadCredentialsException("Blank username and/or password!");
}
}
/**
* Since the InitialLdapContext that's stored as a property of an LdapAuthenticationToken is
* transient (because it isn't Serializable), we need some way to recreate the
* InitialLdapContext if it's null (e.g., if the LdapAuthenticationToken has been serialized
* and deserialized). This is that mechanism.
*
* @param authenticator
* the LdapAuthenticator instance from your application's context
* @param auth
* the LdapAuthenticationToken in which to recreate the InitialLdapContext
* @return
*/
static public InitialLdapContext recreateLdapContext(LdapAuthenticator authenticator,
LdapAuthenticationToken auth) {
DirContextOperations authAdapter = authenticator.authenticate(auth);
InitialLdapContext context = (InitialLdapContext) authAdapter
.getObjectAttribute("ldapContext");
auth.setContext(context);
return context;
}
public DefaultSpringSecurityContextSource getContextFactory() {
return contextFactory;
}
/**
* Set the context factory to use for generating a new LDAP context.
*
* @param contextFactory
*/
public void setContextFactory(DefaultSpringSecurityContextSource contextFactory) {
this.contextFactory = contextFactory;
}
public String getPrincipalPrefix() {
return principalPrefix;
}
/**
* Set the string to be prepended to all principal names prior to attempting authentication
* against the LDAP server. (For example, if the Active Directory wants the domain-name-plus
* backslash prepended, use this.)
*
* @param principalPrefix
*/
public void setPrincipalPrefix(String principalPrefix) {
if (principalPrefix != null) {
this.principalPrefix = principalPrefix;
} else {
this.principalPrefix = "";
}
}
}
最后,LdapAuthenticationToken类:
/**
* <p>
* Authentication token to use when an app needs further access to the LDAP context used to
* authenticate the user.
* </p>
*
* <p>
* When this is the Authentication object stored in the Spring Security context, an application
* can retrieve the current LDAP context thusly:
* </p>
*
* <pre>
* LdapAuthenticationToken ldapAuth = (LdapAuthenticationToken) SecurityContextHolder
* .getContext().getAuthentication();
* InitialLdapContext ldapContext = ldapAuth.getContext();
* </pre>
*
* @author Jason
*
*/
public class LdapAuthenticationToken extends AbstractAuthenticationToken {
private static final long serialVersionUID = -5040340622950665401L;
private Authentication auth;
transient private InitialLdapContext context;
private List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
/**
* Construct a new LdapAuthenticationToken, using an existing Authentication object and
* granting all users a default authority.
*
* @param auth
* @param defaultAuthority
*/
public LdapAuthenticationToken(Authentication auth, GrantedAuthority defaultAuthority) {
this.auth = auth;
if (auth.getAuthorities() != null) {
this.authorities.addAll(Arrays.asList(auth.getAuthorities()));
}
if (defaultAuthority != null) {
this.authorities.add(defaultAuthority);
}
super.setAuthenticated(true);
}
/**
* Construct a new LdapAuthenticationToken, using an existing Authentication object and
* granting all users a default authority.
*
* @param auth
* @param defaultAuthority
*/
public LdapAuthenticationToken(Authentication auth, String defaultAuthority) {
this(auth, new GrantedAuthorityImpl(defaultAuthority));
}
public GrantedAuthority[] getAuthorities() {
GrantedAuthority[] authoritiesArray = this.authorities.toArray(new GrantedAuthority[0]);
return authoritiesArray;
}
public void addAuthority(GrantedAuthority authority) {
this.authorities.add(authority);
}
public Object getCredentials() {
return auth.getCredentials();
}
public Object getPrincipal() {
return auth.getPrincipal();
}
/**
* Retrieve the LDAP context attached to this user's authentication object.
*
* @return the LDAP context
*/
public InitialLdapContext getContext() {
return context;
}
/**
* Attach an LDAP context to this user's authentication object.
*
* @param context
* the LDAP context
*/
public void setContext(InitialLdapContext context) {
this.context = context;
}
}
你会注意到,有一些你可能不需要的位。
例如,我的应用程序需要保留成功登录的LDAP上下文以供用户在登录后进一步使用 - 该应用程序的目的是让用户通过其AD凭据登录,然后再执行AD-相关功能。因此,我有一个自定义身份验证令牌LdapAuthenticationToken,它传递给我(附加LDAP上下文,而不是Spring的默认身份验证令牌)。在LdapAuthenticationProvider.authenticate()中,我创建了该令牌并将其传回;在LdapAuthenticatorImpl.authenticate()中,我将登录的上下文附加到返回对象,以便它可以添加到用户的Spring认证对象中。
此外,在LdapAuthenticationProvider.authenticate()中,我为所有登录用户分配了ROLE_USER角色 - 这就是让我在我的拦截url元素中测试该角色的原因。无论您想要测试哪种角色,您都希望进行匹配,甚至可以根据Active Directory组或其他角色分配角色。
最后,由此推论,我实现LdapAuthenticationProvider.authenticate()的方式为所有用户提供了有效的AD帐户,它们具有相同的ROLE_USER角色。显然,在该方法中,您可以对用户(即特定AD组中的用户?)执行进一步测试,并且甚至在准许用户访问全部之前分配角色,甚至测试某些条件。
我能够使用spring security 2.0.4对活动目录进行身份验证。
我记录的设置
http://maniezhilan.blogspot.com/2008/10/spring-security-204-with-active.html
没有SSL的LDAP认证是不安全的任何人都可以在用户凭证被转移到LDAP服务器时看到用户凭证。我建议使用LDAPS:\协议进行身份验证。它不需要对弹簧部分进行任何重大更改,但您可能会遇到一些与证书相关的问题。有关更多详细信息,请参见LDAP Active Directory authentication in Spring with SSL
卢克的回答以上:
仅供参考使用,Spring Security 3.1有一个身份验证提供 [专为Active Directory] [1]。
我试图上述与春季安全3.1.1:有从LDAP一些轻微的变化 - 活性目录组用户是作为原始情况来通过的成员。
以前,在ldap下,这些组被大写,并带有“ROLE_”前缀,这使得他们很容易在项目中使用文本搜索来查找,但是如果出于某种奇怪的原因只有2个独立的组,按情况区分(即账户和账户)。
此外,语法还需要手动指定域控制器名称和端口,这使冗余有点可怕。当然有一种仰视的SRV DNS记录在Java领域中,即相当于一种方式(从桑巴4 HOWTO):
$ host -t SRV _ldap._tcp.samdom.example.com.
_ldap._tcp.samdom.example.com has SRV record 0 100 389 samba.samdom.example.com.
其次是定期的查找:
$ host -t A samba.samdom.example.com.
samba.samdom.example.com has address 10.0.0.1
(实际上可能还需要查找_kerberos SRV记录......)
以上是使用Samba4.0rc1,我们正在逐步从Samba 3.x LDAP环境升级到Samba AD之一。
作为卢克的回答以上:
的Spring Security 3.1对Active Directory中的身份验证专门供应商。
下面是使用ActiveDirectoryLdapAuthenticationProvider可以轻松完成的细节。
在资源。常规:
ldapAuthProvider1(ActiveDirectoryLdapAuthenticationProvider,
"mydomain.com",
"ldap://mydomain.com/"
)
Config.groovy中:
grails.plugin.springsecurity.providerNames = ['ldapAuthProvider1']
这是所有你需要的代码。您几乎可以删除Config.groovy中的所有其他grails.plugin.springsecurity.ldap。*设置,因为它们不适用于此AD设置。
实现相同如果您在使用Spring 安全4你也可以使用 指定类
- SecurityConfig.java
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
static final Logger LOGGER = LoggerFactory.getLogger(SecurityConfig.class);
@Autowired
protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(activeDirectoryLdapAuthenticationProvider());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/").permitAll()
.anyRequest().authenticated();
.and()
.formLogin()
.and()
.logout();
}
@Bean
public AuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
ActiveDirectoryLdapAuthenticationProvider authenticationProvider =
new ActiveDirectoryLdapAuthenticationProvider("<domain>", "<url>");
authenticationProvider.setConvertSubErrorCodesToExceptions(true);
authenticationProvider.setUseAuthenticationRequestCredentials(true);
return authenticationProvider;
}
}
这不是一个真正的答案,而是一个澄清的问题 - 您是否已经将日志记录转换为Spring安全包的所有内容? – 2008-09-17 17:52:40
我有日志记录开启了一切。没有看到任何正在记录的消息......我用我的Log4J配置更新了我的问题。 – Michael 2008-09-17 18:35:26
告诉我这个所需的jar文件。请 – addy 2013-09-06 10:28:14