通常在根据LDAP进行身份验证时一般进行以下三步:

  1. 利用一个LDAP用户的用户名和密码绑定到LDAP服务器。
  2. 在LDAP中检索一个用户的条目,然后将提供的密码和检索到的LDAP记录中进行验证。
  3. 根据LDAP提供的记录,再去本系统中查找授权信息。

Shiro 提供了DefaultLdapRealm,只做了第二步,根据用户的条目和密码来验证。并不能满足我们的需求,所以肯定是要定制化LdapRealm。

这里使用Spring Ldap 来简化Ldap操作

public class LdapRealm extends AuthorizingRealm {    private static final Logger logger = LoggerFactory.getLogger(LdapRealm.class);    private LdapTemplate ldapTemplate;    @Autowired    private UserService userService;    @Autowired    private RoleService roleService;    @Autowired    private MenuService menuService;    @Override    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {        String username = (String) token.getPrincipal();        String password = new String((char[]) token.getCredentials());        try {            LdapQuery ldapQuery = LdapQueryBuilder.query().base("DC=example,DC=com").searchScope(SearchScope.SUBTREE)                    .filter("(sAMAccountName={0})", username);            boolean authResult = ldapTemplate.authenticate(ldapQuery.base(), ldapQuery.filter().encode(), password);            if (!authResult) {                logger.debug("ldap authentication for {} failed", username);                return null;            }            User ldapUser = (User) ldapTemplate.searchForObject(ldapQuery, new LdapUserAttrMapper());            User user = userService.selectUserById(ldapUser.getUserId());            if (user == null) {                // 用户名不存在抛出异常                throw new UnknownAccountException();            }            if (user.getRemoveFlag()) {                // 用户被管理员锁定抛出异常                throw new LockedAccountException();            }            SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user, token.getCredentials(),                    "LdapRealm");            return authenticationInfo;        } catch (Exception e) {            logger.error("ldap authentication failed", e.toString());            return null;        }    }    @Override    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {        Long userId = ShiroUtils.getUserId();        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();        // 角色加入AuthorizationInfo认证对象        info.setRoles(roleService.selectRoleKeys(userId));        // 权限加入AuthorizationInfo认证对象        info.setStringPermissions(menuService.selectPermsByUserId(userId));        return info;    }    public LdapTemplate getLdapTemplate() {        return ldapTemplate;    }    public void setLdapTemplate(LdapTemplate ldapTemplate) {        this.ldapTemplate = ldapTemplate;    }}

关键的代码如下,验证用户和获取LDAP用户信息

LdapQuery ldapQuery = LdapQueryBuilder.query().base("DC=example,DC=com").searchScope(SearchScope.SUBTREE)                    .filter("(sAMAccountName={0})", username);boolean authResult = ldapTemplate.authenticate(ldapQuery.base(), ldapQuery.filter().encode(), password);User ldapUser = (User) ldapTemplate.searchForObject(ldapQuery, new LdapUserAttrMapper());

Spring 的 ldap 配置如下:

<bean id="ldapContextSource" class="org.springframework.ldap.core.support.LdapContextSource">    <property name="url" value="ldap://192.168.100.1:3268"/>    <property name="userDn" value="CN=Reader"/>    <property name="password" value="secret"/></bean><bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate">    <property name="contextSource" ref="ldapContextSource"/></bean><bean id="ldapRealm" class="com.example.shiro.LdapRealm">    <property name="ldapTemplate" ref="ldapTemplate"/></bean>