Commit 682de9c3 authored by idefav's avatar idefav Committed by Jason Song

增强LDAP用户服务,支持按组查找用户 (#1794)

enhance ldap user service to support filtering users by group
parent daa18396
spring:
ldap:
base: "dc=example,dc=com"
username: "uid=admin,ou=system" # 配置管理员账号,用于搜索、匹配用户
password: "password"
searchFilter: "(uid={0})" # 用户过滤器,登录的时候用这个过滤器来搜索用户
urls:
- "ldap://localhost:10389"
ldap:
mapping: # 配置 ldap 属性
objectClass: "inetOrgPerson" # ldap 用户 objectClass 配置
loginId: "uid" # ldap 用户惟一 id,用来作为登录的 id
rdnKey: "cn" # ldap rdn key
userDisplayName: "displayName" # ldap 用户名,用来作为显示名
email: "mail" # ldap 邮箱属性
group: # 配置ldap group
objectClass: "groupOfNames" # 配置groupClassName
groupBase: "ou=group" # group search base
groupSearch: "(&(cn=apollo-admins)(&(member=*)))" # group filter
groupMembership: "member" # group memberShip eg. member or memberUid
\ No newline at end of file
spring:
ldap:
base: "dc=example,dc=com"
username: "cn=Manager,dc=example,dc=com" # 配置管理员账号,用于搜索、匹配用户
password: "password"
searchFilter: "(uid={0})" # 用户过滤器,登录的时候用这个过滤器来搜索用户
urls:
- "ldap://localhost:389"
ldap:
mapping: # 配置 ldap 属性
objectClass: "inetOrgPerson" # ldap 用户 objectClass 配置
loginId: "uid" # ldap 用户惟一 id,用来作为登录的 id
rdnKey: "uid" # ldap rdn key
userDisplayName: "displayName" # ldap 用户名,用来作为显示名
email: "mail" # ldap 邮箱属性
group: # 配置ldap group
groupBase: "ou=Group" # group search base
groupSearch: "(&(cn=apollo-admins))" # group filter
groupMembership: "memberUid" # group memberShip
\ No newline at end of file
package com.ctrip.framework.apollo.portal.spi.configuration;
import com.ctrip.framework.apollo.common.condition.ConditionalOnMissingProfile;
import com.ctrip.framework.apollo.core.utils.StringUtils;
import com.ctrip.framework.apollo.portal.component.config.PortalConfig;
import com.ctrip.framework.apollo.portal.spi.LogoutHandler;
import com.ctrip.framework.apollo.portal.spi.SsoHeartbeatHandler;
......@@ -14,6 +15,7 @@ import com.ctrip.framework.apollo.portal.spi.defaultimpl.DefaultLogoutHandler;
import com.ctrip.framework.apollo.portal.spi.defaultimpl.DefaultSsoHeartbeatHandler;
import com.ctrip.framework.apollo.portal.spi.defaultimpl.DefaultUserInfoHolder;
import com.ctrip.framework.apollo.portal.spi.defaultimpl.DefaultUserService;
import com.ctrip.framework.apollo.portal.spi.ldap.FilterLdapByGroupUserSearch;
import com.ctrip.framework.apollo.portal.spi.ldap.LdapUserService;
import com.ctrip.framework.apollo.portal.spi.springsecurity.SpringSecurityUserInfoHolder;
import com.ctrip.framework.apollo.portal.spi.springsecurity.SpringSecurityUserService;
......@@ -285,7 +287,7 @@ public class AuthConfiguration {
*/
@Configuration
@Profile("ldap")
@EnableConfigurationProperties(LdapProperties.class)
@EnableConfigurationProperties({LdapProperties.class,LdapExtendProperties.class})
static class SpringSecurityLDAPAuthAutoConfiguration {
private final LdapProperties properties;
......@@ -353,17 +355,33 @@ public class AuthConfiguration {
private final LdapProperties ldapProperties;
private final LdapContextSource ldapContextSource;
public SpringSecurityLDAPConfigurer(final LdapProperties ldapProperties, final LdapContextSource ldapContextSource) {
private final LdapExtendProperties ldapExtendProperties;
public SpringSecurityLDAPConfigurer(final LdapProperties ldapProperties,
final LdapContextSource ldapContextSource,
final LdapExtendProperties ldapExtendProperties) {
this.ldapProperties = ldapProperties;
this.ldapContextSource = ldapContextSource;
this.ldapExtendProperties = ldapExtendProperties;
}
@Bean
public FilterBasedLdapUserSearch userSearch() {
FilterBasedLdapUserSearch filterBasedLdapUserSearch = new FilterBasedLdapUserSearch("",
ldapProperties.getSearchFilter(), ldapContextSource);
filterBasedLdapUserSearch.setSearchSubtree(true);
return filterBasedLdapUserSearch;
if (ldapExtendProperties.getGroup() == null || StringUtils
.isBlank(ldapExtendProperties.getGroup().getGroupSearch())) {
FilterBasedLdapUserSearch filterBasedLdapUserSearch = new FilterBasedLdapUserSearch("",
ldapProperties.getSearchFilter(), ldapContextSource);
filterBasedLdapUserSearch.setSearchSubtree(true);
return filterBasedLdapUserSearch;
} else {
FilterLdapByGroupUserSearch filterLdapByGroupUserSearch = new FilterLdapByGroupUserSearch(
ldapProperties.getBase(), ldapProperties.getSearchFilter(), ldapExtendProperties.getGroup().getGroupBase(),
ldapContextSource, ldapExtendProperties.getGroup().getGroupSearch(),
ldapExtendProperties.getMapping().getRdnKey(),
ldapExtendProperties.getGroup().getGroupMembership(),ldapExtendProperties.getMapping().getLoginId());
filterLdapByGroupUserSearch.setSearchSubtree(true);
return filterLdapByGroupUserSearch;
}
}
@Bean
......
/*
* Copyright (c) 2019 www.ceair.com Inc. All rights reserved.
*/
package com.ctrip.framework.apollo.portal.spi.configuration;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* the LdapExtendProperties description.
*
* @author wuzishu
*/
@ConfigurationProperties(prefix = "ldap")
public class LdapExtendProperties {
private LdapMappingProperties mapping;
private LdapGroupProperties group;
public LdapMappingProperties getMapping() {
return mapping;
}
public void setMapping(LdapMappingProperties mapping) {
this.mapping = mapping;
}
public LdapGroupProperties getGroup() {
return group;
}
public void setGroup(LdapGroupProperties group) {
this.group = group;
}
}
class LdapMappingProperties{
/**
* user ldap objectClass
*/
private String objectClass;
/**
* user login Id
*/
private String loginId;
/**
* user rdn key
*/
private String rdnKey;
/**
* user display name
*/
private String userDisplayName;
/**
* email
*/
private String email;
public String getObjectClass() {
return objectClass;
}
public void setObjectClass(String objectClass) {
this.objectClass = objectClass;
}
public String getLoginId() {
return loginId;
}
public void setLoginId(String loginId) {
this.loginId = loginId;
}
public String getRdnKey() {
return rdnKey;
}
public void setRdnKey(String rdnKey) {
this.rdnKey = rdnKey;
}
public String getUserDisplayName() {
return userDisplayName;
}
public void setUserDisplayName(String userDisplayName) {
this.userDisplayName = userDisplayName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
class LdapGroupProperties{
/**
* group search base
*/
private String groupBase;
/**
* group search filter
*/
private String groupSearch;
/**
* group membership prop
*/
private String groupMembership;
public String getGroupBase() {
return groupBase;
}
public void setGroupBase(String groupBase) {
this.groupBase = groupBase;
}
public String getGroupSearch() {
return groupSearch;
}
public void setGroupSearch(String groupSearch) {
this.groupSearch = groupSearch;
}
public String getGroupMembership() {
return groupMembership;
}
public void setGroupMembership(String groupMembership) {
this.groupMembership = groupMembership;
}
}
package com.ctrip.framework.apollo.portal.spi.ldap;
import static org.springframework.ldap.query.LdapQueryBuilder.query;
import javax.naming.Name;
import javax.naming.directory.SearchControls;
import javax.naming.ldap.LdapName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
import org.springframework.ldap.support.LdapUtils;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.ldap.SpringSecurityLdapTemplate;
import org.springframework.security.ldap.search.FilterBasedLdapUserSearch;
/**
* the FilterLdapByGroupUserSearch description.
*
* @author wuzishu
*/
public class FilterLdapByGroupUserSearch extends FilterBasedLdapUserSearch {
private static final Logger logger = LoggerFactory.getLogger(FilterLdapByGroupUserSearch.class);
private static final String MEMBER_UID_ATTR_NAME = "memberUid";
private String searchBase;
private String groupBase;
private String groupSearch;
private String rdnKey;
private String groupMembershipAttrName;
private String loginIdAttrName;
private final SearchControls searchControls = new SearchControls();
private BaseLdapPathContextSource contextSource;
public FilterLdapByGroupUserSearch(String searchBase, String searchFilter,
String groupBase, BaseLdapPathContextSource contextSource, String groupSearch,
String rdnKey, String groupMembershipAttrName, String loginIdAttrName) {
super(searchBase, searchFilter, contextSource);
this.searchBase = searchBase;
this.groupBase = groupBase;
this.groupSearch = groupSearch;
this.contextSource = contextSource;
this.rdnKey = rdnKey;
this.groupMembershipAttrName = groupMembershipAttrName;
this.loginIdAttrName = loginIdAttrName;
}
private Name searchUserById(String userId) {
SpringSecurityLdapTemplate template = new SpringSecurityLdapTemplate(this.contextSource);
template.setSearchControls(searchControls);
return template.searchForObject(query().where(this.loginIdAttrName).is(userId),
ctx -> ((DirContextAdapter) ctx).getDn());
}
@Override
public DirContextOperations searchForUser(String username) {
if (logger.isDebugEnabled()) {
logger.debug("Searching for user '" + username + "', with user search " + this);
}
SpringSecurityLdapTemplate template = new SpringSecurityLdapTemplate(this.contextSource);
template.setSearchControls(searchControls);
return template
.searchForObject(groupBase, groupSearch, ctx -> {
if (!MEMBER_UID_ATTR_NAME.equals(groupMembershipAttrName)) {
String[] members = ((DirContextAdapter) ctx)
.getStringAttributes(groupMembershipAttrName);
for (String item : members) {
LdapName memberDn = LdapUtils.newLdapName(item);
LdapName memberRdn = LdapUtils
.removeFirst(memberDn, LdapUtils.newLdapName(searchBase));
String rdnValue = LdapUtils.getValue(memberRdn, rdnKey).toString();
if (rdnValue.equalsIgnoreCase(username)) {
return new DirContextAdapter(memberRdn.toString());
}
}
throw new UsernameNotFoundException("User " + username + " not found in directory.");
} else {
String[] memberUids = ((DirContextAdapter) ctx)
.getStringAttributes(groupMembershipAttrName);
for (String memberUid : memberUids) {
if (memberUid.equalsIgnoreCase(username)) {
Name name = searchUserById(memberUid);
LdapName ldapName = LdapUtils.newLdapName(name);
LdapName ldapRdn = LdapUtils
.removeFirst(ldapName, LdapUtils.newLdapName(searchBase));
return new DirContextAdapter(ldapRdn);
}
}
}
throw new UsernameNotFoundException("User " + username + " not found in directory.");
});
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment