如何在 Spring 安全性中从 LDAP 获取额外的用户属性?
How to get additional user attributes from LDAP in Spring Security?
我目前正在尝试开发一个 Spring 引导应用程序,其目的是管理我们 LDAP 目录中的用户条目。
LDAP 登录已经可用;查找用户所属的组也是如此。
此外,我想用该用户的更多 LDAP 属性填充 Spring 安全 Principal
对象。我已经阅读了几篇关于 SO 的文章以及官方 Spring 文档,但就是无法让它工作。
这是我当前的代码:
ClassAuthenticationConfiguration
@Configuration
class AuthenticationConfiguration extends GlobalAuthenticationConfigurerAdapter {
private String ldapUrl = "ldap://127.0.0.1:10389/dc=corp,dc=org";
private String bindUser = "cn=spring,ou=users,dc=corp,dc=org";
private String bindPW = "<password>";
private String groupSearchBase = "ou=groups";
private String groupSearchFilter = "(member={0})";
private String userDnPattern = "uid={0},ou=users";
private Log log = LogFactory.getLog(this.getClass());
@Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(ldapUrl);
contextSource.setUserDn(bindUser);
contextSource.setPassword(bindPW);
contextSource.afterPropertiesSet();
log.info(contextSource.getReadOnlyContext().getAttributes("uid=testuser,ou=users")); // returns all LDAP attributes from that user
DefaultLdapAuthoritiesPopulator populator = new DefaultLdapAuthoritiesPopulator(contextSource, groupSearchBase);
populator.setGroupSearchFilter(groupSearchFilter);
populator.setSearchSubtree(true);
populator.setIgnorePartialResultException(true);
auth
.ldapAuthentication()
.ldapAuthoritiesPopulator(populator)
.contextSource(contextSource)
.userDetailsContextMapper(userDetailsContextMapper())
.userDnPatterns(userDnPattern)
;
}
@Bean
public UserDetailsContextMapper userDetailsContextMapper() {
return new CustomUserDetailsContextMapper();
}
}
如您所见,我正在使用 userDetailsContextMapper() 方法 return 我的 CustomUserDetailsContextMapper
:
的一个实例
@Configuration
public class CustomUserDetailsContextMapper extends LdapUserDetailsMapper implements UserDetailsContextMapper {
private Log log = LogFactory.getLog(this.getClass());
@Override
public LdapUserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection<? extends GrantedAuthority> authorities) {
LdapUserDetailsImpl details = (LdapUserDetailsImpl) super.mapUserFromContext(ctx, username, authorities);
log.info("DN from ctx: " + ctx.getDn()); // return correct DN
log.info("Attributes size: " + ctx.getAttributes().size()); // always returns 0
return new CustomUserDetails(details);
}
@Override
public void mapUserToContext(UserDetails user, DirContextAdapter ctx) {
// default
}
}
现在,当您查看 AuthenticationConfiguration
中的第一个日志语句时,Spring 实际上会将整个用户对象打印到标准输出,正确显示用户的所有 LDAP 属性:
{displayname=displayName: Test User, givenname=givenName: Test, objectclass=objectClass: posixAccount, top [...]}
然而,我的 CustomUserDetailsContextMapper
class 中的日志语句没有。尽管第一个正确显示登录用户的 DN,第二个只显示 0,即 ctx 似乎不包含当前用户的任何属性。
我也尝试通过 ctx.getAttribute("attribute")
、ctx.getStringAttribute("attribute")
或 ctx.getObjectAttribute("attribute")
直接查询属性,但无济于事。
如何从 mapUserFromContext
方法中访问 LDAP 属性?
我真的没有想法,所以任何帮助将不胜感激:-)
解决了。问题不在应用程序代码中,而在 LDAP 配置中。
因为我使用的是默认的 BindAuthenticator,Spring 安全尝试使用登录表单中指定的用户绑定到 LDAP。不幸的是,ou=users 下的所有用户在 LDAP 配置中只有 search
权限。将 search
更改为 read
(参见第 8.2.3 点 http://www.openldap.org/doc/admin24/access-control.html)为我解决了这个问题。
请注意 login/bind 本身仍然成功(因为 auth
权限是 search
的子集),但是任何属性检索都失败了,因为这需要 read
许可。
@mpm 示例提前:
希望对某些人有所帮助。这是我 UserDetails
的装饰器
public class CustomUserDetails implements LdapUserDetails {
private String iin;
private String colvirId;
private LdapUserDetails details;
public CustomUserDetails(LdapUserDetails details) {
this.details = details;
}
public String getIin() {
return iin;
}
public void setIin(String iin) {
this.iin = iin;
}
public String getColvirId() {
return colvirId;
}
public void setColvirId(String colvirId) {
this.colvirId = colvirId;
}
@Override
public String getDn() {
return details.getDn();
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return details.getAuthorities();
}
@Override
public String getPassword() {
return details.getPassword();
}
@Override
public String getUsername() {
return details.getUsername();
}
@Override
public boolean isAccountNonExpired() {
return details.isAccountNonExpired();
}
@Override
public boolean isAccountNonLocked() {
return details.isAccountNonLocked();
}
@Override
public boolean isCredentialsNonExpired() {
return details.isCredentialsNonExpired();
}
@Override
public boolean isEnabled() {
return details.isEnabled();
}
}
我目前正在尝试开发一个 Spring 引导应用程序,其目的是管理我们 LDAP 目录中的用户条目。
LDAP 登录已经可用;查找用户所属的组也是如此。
此外,我想用该用户的更多 LDAP 属性填充 Spring 安全 Principal
对象。我已经阅读了几篇关于 SO 的文章以及官方 Spring 文档,但就是无法让它工作。
这是我当前的代码:
ClassAuthenticationConfiguration
@Configuration
class AuthenticationConfiguration extends GlobalAuthenticationConfigurerAdapter {
private String ldapUrl = "ldap://127.0.0.1:10389/dc=corp,dc=org";
private String bindUser = "cn=spring,ou=users,dc=corp,dc=org";
private String bindPW = "<password>";
private String groupSearchBase = "ou=groups";
private String groupSearchFilter = "(member={0})";
private String userDnPattern = "uid={0},ou=users";
private Log log = LogFactory.getLog(this.getClass());
@Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(ldapUrl);
contextSource.setUserDn(bindUser);
contextSource.setPassword(bindPW);
contextSource.afterPropertiesSet();
log.info(contextSource.getReadOnlyContext().getAttributes("uid=testuser,ou=users")); // returns all LDAP attributes from that user
DefaultLdapAuthoritiesPopulator populator = new DefaultLdapAuthoritiesPopulator(contextSource, groupSearchBase);
populator.setGroupSearchFilter(groupSearchFilter);
populator.setSearchSubtree(true);
populator.setIgnorePartialResultException(true);
auth
.ldapAuthentication()
.ldapAuthoritiesPopulator(populator)
.contextSource(contextSource)
.userDetailsContextMapper(userDetailsContextMapper())
.userDnPatterns(userDnPattern)
;
}
@Bean
public UserDetailsContextMapper userDetailsContextMapper() {
return new CustomUserDetailsContextMapper();
}
}
如您所见,我正在使用 userDetailsContextMapper() 方法 return 我的 CustomUserDetailsContextMapper
:
@Configuration
public class CustomUserDetailsContextMapper extends LdapUserDetailsMapper implements UserDetailsContextMapper {
private Log log = LogFactory.getLog(this.getClass());
@Override
public LdapUserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection<? extends GrantedAuthority> authorities) {
LdapUserDetailsImpl details = (LdapUserDetailsImpl) super.mapUserFromContext(ctx, username, authorities);
log.info("DN from ctx: " + ctx.getDn()); // return correct DN
log.info("Attributes size: " + ctx.getAttributes().size()); // always returns 0
return new CustomUserDetails(details);
}
@Override
public void mapUserToContext(UserDetails user, DirContextAdapter ctx) {
// default
}
}
现在,当您查看 AuthenticationConfiguration
中的第一个日志语句时,Spring 实际上会将整个用户对象打印到标准输出,正确显示用户的所有 LDAP 属性:
{displayname=displayName: Test User, givenname=givenName: Test, objectclass=objectClass: posixAccount, top [...]}
然而,我的 CustomUserDetailsContextMapper
class 中的日志语句没有。尽管第一个正确显示登录用户的 DN,第二个只显示 0,即 ctx 似乎不包含当前用户的任何属性。
我也尝试通过 ctx.getAttribute("attribute")
、ctx.getStringAttribute("attribute")
或 ctx.getObjectAttribute("attribute")
直接查询属性,但无济于事。
如何从 mapUserFromContext
方法中访问 LDAP 属性?
我真的没有想法,所以任何帮助将不胜感激:-)
解决了。问题不在应用程序代码中,而在 LDAP 配置中。
因为我使用的是默认的 BindAuthenticator,Spring 安全尝试使用登录表单中指定的用户绑定到 LDAP。不幸的是,ou=users 下的所有用户在 LDAP 配置中只有 search
权限。将 search
更改为 read
(参见第 8.2.3 点 http://www.openldap.org/doc/admin24/access-control.html)为我解决了这个问题。
请注意 login/bind 本身仍然成功(因为 auth
权限是 search
的子集),但是任何属性检索都失败了,因为这需要 read
许可。
@mpm 示例提前:
希望对某些人有所帮助。这是我 UserDetails
public class CustomUserDetails implements LdapUserDetails {
private String iin;
private String colvirId;
private LdapUserDetails details;
public CustomUserDetails(LdapUserDetails details) {
this.details = details;
}
public String getIin() {
return iin;
}
public void setIin(String iin) {
this.iin = iin;
}
public String getColvirId() {
return colvirId;
}
public void setColvirId(String colvirId) {
this.colvirId = colvirId;
}
@Override
public String getDn() {
return details.getDn();
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return details.getAuthorities();
}
@Override
public String getPassword() {
return details.getPassword();
}
@Override
public String getUsername() {
return details.getUsername();
}
@Override
public boolean isAccountNonExpired() {
return details.isAccountNonExpired();
}
@Override
public boolean isAccountNonLocked() {
return details.isAccountNonLocked();
}
@Override
public boolean isCredentialsNonExpired() {
return details.isCredentialsNonExpired();
}
@Override
public boolean isEnabled() {
return details.isEnabled();
}
}