你如何验证对使用Spring Security的Active Directory服务器? [英] How do you authenticate against an Active Directory server using Spring Security?

查看:575
本文介绍了你如何验证对使用Spring Security的Active Directory服务器?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我写信,要求用户登录一个Spring Web应用程序。我公司有一个Active Directory服务器,我想利用这个目的。不过,我使用Spring的安全连接到服务器有问题。

我使用Spring 2.5.5和Spring 2.0.3安全,以及与Java 1.6。

如果我更改LDAP URL到错误的IP地址,它不会抛出异常或任何东西,所以我想知道如果它甚至的尝试的连接,开始与服务器。

虽然Web应用程序启动后就好了,我进入登录页面的任何信息将被拒绝。我有previously使用的InMemoryDaoImp​​l,它工作得很好,所以我的应用程序的其余似乎是正确配置。

下面是我的与安全相关的豆类:

 <豆类:绿豆的id =ldapAuthProvider级=org.springframework.security.providers.ldap.LdapAuthenticationProvider>
    <豆类:构造带参数的>
      <豆类:绿豆类=org.springframework.security.providers.ldap.authenticator.BindAuthenticator>
        <豆类:构造带参数REF =initialDirContextFactory/>
        <豆类:属性名=userDnPatterns>
          <豆类:列表>
            <豆类:值GT; CN = {0},OU = SBSUsers,OU =用户,OU = MyBusiness,DC = Acme公司,DC = COM< /豆:值GT;
          < /豆:列表>
        < /大豆:性>
      < /豆类:绿豆>
    < /豆:构造带参数的>
  < /豆类:绿豆>

  <豆类:绿豆的id =的UserDetailsS​​ervice级=org.springframework.security.userdetails.ldap.LdapUserDetailsManager>
    <豆类:构造带参数REF =initialDirContextFactory/>
  < /豆类:绿豆>

  <豆类:绿豆的id =initialDirContextFactory级=org.springframework.security.ldap.DefaultInitialDirContextFactory>
    <豆类:构造带参数的值=LDAP://192.168.123.456:389 / DC = Acme公司,DC = COM/>
  < /豆类:绿豆>
 

解决方案

我有同样的撞击声,我的头,对着墙式的经验,你做到了,最后写一个自定义的身份验证提供者,做一个LDAP查询反对Active Directory服务器。

所以,我的安全相关的豆类:

 <豆类:绿豆的id =好的ContextSource
类=org.springframework.security.ldap.DefaultSpringSecurityContextSource>
<豆类:构造带参数的值=LDAP://hostname.queso.com:389 //>
< /豆类:绿豆>

<豆类:绿豆的id =LdapAuthenticationProvider可疑
类=org.queso.ad.service.authentication.LdapAuthenticationProvider>
<豆类:属性名=身份验证REF =ldapAuthenticator/>
<自定义验证提供商/>
< /豆类:绿豆>

<豆类:绿豆的id =ldapAuthenticator
类=org.queso.ad.service.authentication.LdapAuthenticatorImpl>
<豆类:属性名=contextFactoryREF =好的ContextSource/>
<豆类:属性名=本金preFIX值=QUESO \/>
< /豆类:绿豆>
 

然后LdapAuthenticationProvider可疑类:

  / **
 *自定义的Spring Security认证提供器,试图绑定到LDAP服务器
 *传入的凭证;值得注意的,与自定义使用时{@link LdapAuthenticatorImpl},
 *不<强>不< / STRONG>需要一个LDAP用户名和密码,初始绑定。
 *
 * @author贾森
 * /
公共类LdapAuthenticationProvider可疑实现的AuthenticationProvider {

私人LdapAuthenticator的认证;

公开身份验证(验证AUTH)抛出的AuthenticationException {

//验证,使用传入的凭据。
DirContextOperations authAdapter = authenticator.authenticate(AUTH);

//创建LdapAuthenticationToken(而不是使用现有的认证
//对象)可以让我们增加已经创建的LDAP环境为我们的应用程序,以备后用。
LdapAuthenticationToken ldapAuth =新LdapAuthenticationToken(AUTH,ROLE_USER);
InitialLdapContext LdapContext的=(InitialLdapContext)authAdapter
.getObjectAttribute(LdapContext的);
如果(LdapContext的!= NULL){
ldapAuth.setContext(LdapContext的);
}

返回ldapAuth;
}

公共布尔的支持(类clazz所){
返程(UsernamePasswordAut​​henticationToken.class.isAssignableFrom(clazz所));
}

公共LdapAuthenticator getAuthenticator(){
返回的认证;
}

公共无效setAuthenticator(LdapAuthenticator身份验证){
this.authenticator =身份验证;
}

}
 

然后LdapAuthenticatorImpl类:

  / **
 *自定义Spring Security的LDAP身份验证它试图通过绑定到LDAP服务器
 *通过信用凭证;并与LT;强>不< / STRONG>需要大师资格证书为
 *初始绑定搜索传入的用户名之前。
 *
 * @author贾森
 * /
公共类LdapAuthenticatorImpl实现LdapAuthenticator {

私人DefaultSpringSecurityContextSource contextFactory;
私人字符串本金preFIX =;

公共DirContextOperations验证(验证认证){

//取得用户名和密码进行身份验证的对象。
字符串本金=本金preFIX + authentication.getName();
字符串password =;
如果(authentication.getCredentials()!= NULL){
。密码= authentication.getCredentials()的toString();
}

//如果我们有一个有效的用户名和密码,尝试验证。
如果((!等于(principal.trim()))及。&安培;!(。等于(password.trim()))){
InitialLdapContext LdapContext的=(InitialLdapContext)contextFactory
.getReadWriteContext(本金,密码);

//我们需要传递的背景下退了出来,从而使身份验证提供者可以将其添加到
//验证对象。
DirContextOperations authAdapter =新DirContextAdapter();
authAdapter.addAttributeValue(LdapContext的,LdapContext的);

返回authAdapter;
		} 其他 {
抛出新BadCredentialsException(空白的用户名和/或密码!);
}
}

/ **
*因为这是存储为LdapAuthenticationToken的属性是InitialLdapContext
*短暂的(因为它不是序列化),我们需要一些方法来重新创建
* InitialLdapContext如果是空值(例如,如果LdapAuthenticationToken已系列化
*和反序列化)。这是该机制。
*
* @参数的认证
*从应用程序上下文的LdapAuthenticator实例
* @参数AUTH
*所述LdapAuthenticationToken在其中重新创建InitialLdapContext
	 * @返回
* /
静态公共InitialLdapContext recreateLdapContext(LdapAuthenticator身份验证,
LdapAuthenticationToken AUTH){
DirContextOperations authAdapter = authenticator.authenticate(AUTH);
InitialLdapContext上下文=(InitialLdapContext)authAdapter
.getObjectAttribute(LdapContext的);
auth.setContext(上下文);
返回范围内;
}

公共DefaultSpringSecurityContextSource getContextFactory(){
返回contextFactory;
}

/ **
*设置上下文工厂用于生成一个新的LDAP环境。
*
* @参数contextFactory
* /
公共无效setContextFactory(DefaultSpringSecurityContextSource contextFactory){
this.contextFactory = contextFactory;
}

公共字符串getPrincipal preFIX(){
返回本金preFIX;
}

/ **
*之前试图验证设置为ppended所有主体名称$ P $字符串
*对LDAP服务器。 (例如,如果活动目录希望域名加
*反斜线prepended,用这个。)
*
* @参数本金preFIX
* /
公共无效setPrincipal preFIX(字符串本金preFIX){
如果(本金preFIX!= NULL){
this.principal preFIX =本金preFIX;
		} 其他 {
this.principal preFIX =;
}
}

}
 

最后,LdapAuthenticationToken类:

  / **
 *所述p为H.;
 *认证令牌时要使用一个应用程序需要进一步获取用于对LDAP环境
 *验证用户。
 *所述; / P>
 *
 *所述p为H.;
 *当这是存储在春季安全上下文的验证对象,应用程序
 *可正是如此检索当前LDAP环境:
 *所述; / P>
 *
 *< pre>
 * LdapAuthenticationToken ldapAuth =(LdapAuthenticationToken)SecurityContextHolder中
 * .getContext()getAuthentication();
 * InitialLdapContext LdapContext的= ldapAuth.getContext();
 *< / pre>
 *
 * @author贾森
 *
 * /
公共类LdapAuthenticationToken扩展AbstractAuthenticationToken {

私有静态最后长的serialVersionUID = -5040340622950665401L;

专用身份验证身份验证;
短暂的私人InitialLdapContext环境;
私人列表<的GrantedAuthority>当局=新的ArrayList<的GrantedAuthority>();

/ **
*构造一个新的LdapAuthenticationToken,利用现有的认证对象,
*授予所有用户的默认权限。
*
* @参数AUTH
* @参数defaultAuthority
* /
公共LdapAuthenticationToken(认证身份验证,GrantedAuthority的defaultAuthority){
this.auth =权威性;
如果(auth.getAuthorities()!= NULL){
this.authorities.addAll(Arrays.asList(auth.getAuthorities()));
}
如果(defaultAuthority!= NULL){
this.authorities.add(defaultAuthority);
}
super.setAuthenticated(真正的);
}

/ **
*构造一个新的LdapAuthenticationToken,利用现有的认证对象,
*授予所有用户的默认权限。
*
* @参数AUTH
* @参数defaultAuthority
* /
公共LdapAuthenticationToken(认证AUTH,串defaultAuthority){
此(验证,新的GrantedAuthorityImpl(defaultAuthority));
}

公众的GrantedAuthority [] getAuthorities(){
的GrantedAuthority [] authoritiesArray = this.authorities.toArray(新的GrantedAuthority [0]);
返回authoritiesArray;
}

公共无效addAuthority(GrantedAuthority的权威){
this.authorities.add(授权);
}

公共对象getCredentials(){
返回auth.getCredentials();
}

公共对象getPrincipal(){
返回auth.getPrincipal();
}

/ **
*检索连接到该用户的认证对象的LDAP环境。
*
返回:在LDAP环境
* /
公共InitialLdapContext的getContext(){
返回范围内;
}

/ **
*附加​​一个LDAP上下文该用户的认证对象。
*
* @参数方面
*在LDAP环境
* /
公共无效setContext(InitialLdapContext上下文){
this.context =背景;
}

}
 

您会注意到在那里,你可能不需要几个位。

例如,我的应用程序需要保留的成功,登录的LDAP环境通过一次登录用户进一步使​​用 - 应用程序的目的是让用户通过他们的AD凭据登录,然后进行进一步的AD相关功能。正因为如此所以,我有一个自定义的身份验证令牌,LdapAuthenticationToken,我通过周围(而不是Spring的默认认证令牌),允许我以附加的LDAP环境。在LdapAuthenticationProvider.authenticate(),我创建令牌,并将其传递回了;在LdapAuthenticatorImpl.authenticate(),附上登录的上下文至返回对象,以便它可以被添加到用户的弹簧认证对象

此外,在LdapAuthenticationProvider.authenticate(),我将所有登录用户的ROLE_USER角色 - 这正是让我再测试,在我的拦截,网址因素的作用。你想使这场比赛的任何角色,你想测试,甚至是基于Active Directory组或其他指定角色。

最后,也是一个必然结果是,我的方式来实现LdapAuthenticationProvider.authenticate()给所有用户提供有效的AD帐户相同ROLE_USER作用。显然,在这种方法中,可以对用户进行进一步的测试(即是在一个特定的AD组的用户?)和分配角色的方式,甚至是给予用户访问的前甚至测试某个条件的所有的。

I'm writing a Spring web application that requires users to login. My company has an Active Directory server that I'd like to make use of for this purpose. However, I'm having trouble using Spring Security to connect to the server.

I'm using Spring 2.5.5 and Spring Security 2.0.3, along with Java 1.6.

If I change the LDAP URL to the wrong IP address, it doesn't throw an exception or anything, so I'm wondering if it's even trying to connect to the server to begin with.

Although the web application starts up just fine, any information I enter into the login page is rejected. I had previously used an InMemoryDaoImpl, which worked fine, so the rest of my application seems to be configured correctly.

Here are my security-related beans:

  <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>

解决方案

I had the same banging-my-head-against-the-wall experience you did, and ended up writing a custom authentication provider that does an LDAP query against the Active Directory server.

So my security-related beans are:

<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>

Then the LdapAuthenticationProvider class:

/**
 * 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;
	}

}

Then the LdapAuthenticatorImpl class:

/**
 * 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 = "";
		}
	}

}

And finally, the LdapAuthenticationToken class:

/**
 * <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;
	}

}

You'll notice that there are a few bits in there that you might not need.

For example, my app needed to retain the successfully-logged-in LDAP context for further use by the user once logged in -- the app's purpose is to let users log in via their AD credentials and then perform further AD-related functions. So because of that, I have a custom authentication token, LdapAuthenticationToken, that I pass around (rather than Spring's default Authentication token) which allows me to attach the LDAP context. In LdapAuthenticationProvider.authenticate(), I create that token and pass it back out; in LdapAuthenticatorImpl.authenticate(), I attach the logged-in context to the return object so that it can be added to the user's Spring authentication object.

Also, in LdapAuthenticationProvider.authenticate(), I assign all logged-in users the ROLE_USER role -- that's what lets me then test for that role in my intercept-url elements. You'll want to make this match whatever role you want to test for, or even assign roles based on Active Directory groups or whatever.

Finally, and a corollary to that, the way I implemented LdapAuthenticationProvider.authenticate() gives all users with valid AD accounts the same ROLE_USER role. Obviously, in that method, you can perform further tests on the user (i.e., is the user in a specific AD group?) and assign roles that way, or even test for some condition before even granting the user access at all.

这篇关于你如何验证对使用Spring Security的Active Directory服务器?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆