4.2.3 LDAP 支持的用户存储

要为基于 LDAP 的身份验证配置 Spring Security,可以使用 ldapAuthentication() 方法。这个方法与 jdbcAuthentication() 类似。下面的 configure() 方法演示了用于 LDAP 身份验证的简单配置:

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth
        .ldapAuthentication()
            .userSearchFilter("(uid={0})")
            .groupSearchFilter("member={0}");
}

userSearchFilter() 和 groupSearchFilter() 方法用于为基本 LDAP 查询提供过滤器,这些查询用于搜索用户和组。默认情况下,用户和组的基本查询都是空的,这表示将从 LDAP 层次结构的根目录进行搜索。但你可以通过指定一个查询基数来改变这种情况:

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth
        .ldapAuthentication()
            .userSearchBase("ou=people")
            .userSearchFilter("(uid={0})")
            .groupSearchBase("ou=groups")
            .groupSearchFilter("member={0}");
}

userSearchBase() 方法提供了查找用户的基本查询。同样,groupSearchBase() 方法指定查找组的基本查询。这个示例不是从根目录进行搜索,而是指定要搜索用户所在的组织单元是 people,组应该搜索组织单元所在的 group。

配置密码比较

针对 LDAP 进行身份验证的默认策略是执行绑定操作,将用户通过 LDAP 服务器直接进行验证。另一种选择是执行比较操作,这包括将输入的密码发送到 LDAP 目录,并要求服务器将密码与用户的密码属性进行比较。因为比较是在 LDAP 服务器中进行的,所以实际的密码是保密的。

如果希望通过密码比较进行身份验证,可以使用 passwordCompare() 方法进行声明:

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth
        .ldapAuthentication()
            .userSearchBase("ou=people")
            .userSearchFilter("(uid={0})")
            .groupSearchBase("ou=groups")
            .groupSearchFilter("member={0}")
            .passwordCompare();
}

默认情况下,登录表单中给出的密码将与用户 LDAP 条目中的 userPassword 属性值进行比较。如果密码保存在不同的属性中,可以使用 passwordAttribute() 指定密码属性的名称:

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth
        .ldapAuthentication()
            .userSearchBase("ou=people")
            .userSearchFilter("(uid={0})")
            .groupSearchBase("ou=groups")
            .groupSearchFilter("member={0}")
            .passwordCompare()
            .passwordEncoder(new BCryptPasswordEncoder())
            .passwordAttribute("passcode");
}

在本例中,指定密码属性应该与给定的密码进行比较。此外,还可以指定密码编码器,在进行服务器端密码比较时,最好在服务器端对实际密码加密。但是尝试的密码仍然会通过网络传递到 LDAP 服务器,并且可能被黑客截获。为了防止这种情况,可以通过调用 passwordEncoder() 方法来指定加密策略。

在前面的示例中,使用 bcrypt 密码散列函数对密码进行加密,这里的前提是密码在 LDAP 服务器中也是使用 bcrypt 加密的。

引用远程 LDAP 服务器

到目前为止,我们忽略了 LDAP 服务器和数据实际驻留的位置,虽然已经将 Spring 配置为根据 LDAP 服务器进行身份验证,但是该服务器在哪里呢?

默认情况下,Spring Security 的 LDAP 身份验证假设 LDAP 服务器正在本地主机上监听端口 33389。但是,如果 LDAP 服务器位于另一台机器上,则可以使用 contextSource() 方法来配置位置:

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth
        .ldapAuthentication()
            .userSearchBase("ou=people")
            .userSearchFilter("(uid={0})")
            .groupSearchBase("ou=groups")
            .groupSearchFilter("member={0}")
            .passwordCompare()
            .passwordEncoder(new BCryptPasswordEncoder())
            .passwordAttribute("passcode")
            .contextSource()
                .url("ldap://tacocloud.com:389/dc=tacocloud,dc=com");
}

contextSource() 方法返回 ContextSourceBuilder,其中提供了 url() 方法,它允许指定 LDAP 服务器的位置。

配置嵌入式 LDAP 服务器

如果没有 LDAP 服务器去做身份验证,Spring Security 可提供一个嵌入式 LDAP 服务器。可以通过 root() 方法为嵌入式服务器指定根后缀,而不是将 URL 设置为远程 LDAP 服务器:

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth
        .ldapAuthentication()
            .userSearchBase("ou=people")
            .userSearchFilter("(uid={0})")
            .groupSearchBase("ou=groups")
            .groupSearchFilter("member={0}")
            .passwordCompare()
            .passwordEncoder(new BCryptPasswordEncoder())
            .passwordAttribute("passcode")
            .contextSource()
                .root("dc=tacocloud,dc=com");
}

当 LDAP 服务器启动时,它将尝试从类路径中找到的任何 LDIF 文件进行数据加载。LDIF(LDAP 数据交换格式)是在纯文本文件中表示 LDAP 数据的标准方法,每个记录由一个或多个行组成,每个行包含一个 name:value 对,记录之间用空行分隔。

如果不希望 Spring 在类路径中寻找它能找到的 LDIF 文件,可以通过调用 ldif() 方法来更明确地知道加载的是哪个 LDIF 文件:

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth
        .ldapAuthentication()
            .userSearchBase("ou=people")
            .userSearchFilter("(uid={0})")
            .groupSearchBase("ou=groups")
            .groupSearchFilter("member={0}")
            .passwordCompare()
            .passwordEncoder(new BCryptPasswordEncoder())
            .passwordAttribute("passcode")
            .contextSource()
                .root("dc=tacocloud,dc=com")
                .ldif("classpath:users.ldif");
}

这里,特别要求 LDAP 服务器从位于根路径下的 users.ldif 文件中加载数据。如果你感兴趣,这里有一个LDIF 文件,你可以使用它来加载内嵌 LDAP 服务器的用户数据:

dn: ou=groups,dc=tacocloud,dc=com
objectclass: top
objectclass: organizationalUnit
ou: groups
dn: ou=people,dc=tacocloud,dc=com
objectclass: top
objectclass: organizationalUnit
ou: people
dn: uid=buzz,ou=people,dc=tacocloud,dc=com
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Buzz Lightyear
sn: Lightyear
uid: buzz
userPassword: password
dn: cn=tacocloud,ou=groups,dc=tacocloud,dc=com
objectclass: top
objectclass: groupOfNames
cn: tacocloud
member: uid=buzz,ou=people,dc=tacocloud,dc=com

Spring Security 的内置用户存储非常方便,涵盖了一些常见的用例。但是 Taco Cloud 应用程序需要一些特殊的东西。当开箱即用的用户存储不能满足需求时,需要创建并配置一个定制的用户详细信息服务。

results matching ""

    No results matching ""