使用Apache Shiro进行身份认证-LDAP两次绑定认证

时间:2021-09-14 07:49:43
通常在根据LDAP进行身份验证时会采取以下三种方法:

1、利用一个LDAP用户的用户名和密码绑定到LDAP服务器。

2、在LDAP中检索一个用户的条目,然后将提供的密码和检索到的LDAP记录中的密码属性相比较。

3、“两次绑定”验证方法。

基于LDAP进行身份验证,最好也是最通用的方法就是 “两次绑定”。这种方法的步骤以及优点可参看我的另一篇博客: 基于LDAP进行验证-方法和问题

当前Shiro只支持了第一种方法,即使用用户名和密码到LDAP服务器中进行绑定来判断合法性。

我自己写了一个认证类,实现了“两次绑定”验证。同时解决了目前做LDAP认证时没有区分错误情况,返回的错误提示信息不够准确的问题。

配置信息:

[main]
ldapRealm = main.java.name.peter.shiro.realm.ldap.LdapAuthenticator
ldapRealm.rootDN = dc=example,dc=com
ldapRealm.contextFactory.url = ldap://localhost:389
ldapRealm.contextFactory.systemUsername = cn=Manager,dc=example,dc=com
ldapRealm.contextFactory.systemPassword = secret

认证类:

/***
 * 基于Ldap进行身份认证,二次绑定方式.
 * 
 * @author wanghao
 * 
 */
public class LdapAuthenticator extends JndiLdapRealm {

	private static final Logger log = LoggerFactory
			.getLogger(LdapAuthenticator.class);
	private String rootDN;

	public LdapAuthenticator() {
		super();
	}

	public String getRootDN() {
		return rootDN;
	}

	public void setRootDN(String rootDN) {
		this.rootDN = rootDN;
	}

	@Override
	/***
	 * 认证
	 */
	protected AuthenticationInfo doGetAuthenticationInfo(
			AuthenticationToken token) throws AuthenticationException {
		AuthenticationInfo info;
		try {
			info = queryForAuthenticationInfo(token, getContextFactory());
		} catch (AuthenticationNotSupportedException e) {
			String msg = "Unsupported configured authentication mechanism";
			throw new UnsupportedAuthenticationMechanismException(msg, e);
		} catch (javax.naming.AuthenticationException e) {
			String msg = "LDAP authentication failed.";
			throw new AuthenticationException(msg, e);
		} catch (NamingException e) {
			String msg = "LDAP naming error while attempting to authenticate user.";
			throw new AuthenticationException(msg, e);
		} catch (UnknownAccountException e) {
			String msg = "UnknownAccountException";
			throw new UnknownAccountException(msg, e);
		} catch (IncorrectCredentialsException e) {
			String msg = "IncorrectCredentialsException";
			throw new IncorrectCredentialsException(msg, e);
		}

		return info;
	}
	
	@Override
	protected AuthenticationInfo queryForAuthenticationInfo(
			AuthenticationToken token, LdapContextFactory ldapContextFactory)
			throws NamingException {

		Object principal = token.getPrincipal();
		Object credentials = token.getCredentials();

		LdapContext systemCtx = null;
		LdapContext ctx = null;
		try {
			systemCtx = ldapContextFactory.getSystemLdapContext();

			SearchControls constraints = new SearchControls();
			constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
			NamingEnumeration results = systemCtx.search(rootDN, "cn="
					+ principal, constraints);
			if (results != null && !results.hasMore()) {
				throw new UnknownAccountException();
			} else {
				while (results.hasMore()) {
					SearchResult si = (SearchResult) results.next();
					principal = si.getName() + "," + rootDN;
				}
				log.info("DN="+principal);
				try {
					ctx = ldapContextFactory.getLdapContext(principal,
							credentials);
				} catch (NamingException e) {
					throw new IncorrectCredentialsException();
				}
				return createAuthenticationInfo(token, principal, credentials,
						ctx);
			}
		} finally {
			LdapUtils.closeContext(systemCtx);
			LdapUtils.closeContext(ctx);
		}
	}
}