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); } } }