Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 24 commits into from
Feb 13, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
6e44222
增强ldap用户访问服务,支持按照group查找用户
idefav Dec 18, 2018
a6ee30f
修改StringUtils引用
idefav Dec 18, 2018
b77995d
添加LdapUserService注释
idefav Dec 18, 2018
34569a1
add default value for config
idefav Dec 19, 2018
9a8421f
bug fix
idefav Dec 19, 2018
14f65e1
Merge branch 'master' into master
idefav Dec 20, 2018
5f8f7a3
Merge branch 'master' into master
idefav Jan 21, 2019
69c444c
Merge branch 'master' into master
idefav Jan 22, 2019
b5314ae
增强ldap group 模式,支持member,memberUid方式查找用户
idefav Feb 12, 2019
efd80cc
Merge branch 'master' of https://github.com/idefav/apollo
idefav Feb 12, 2019
5cd44c5
修改rdn配置为rdnKey
idefav Feb 12, 2019
cf7a501
Merge branch 'master' into master
idefav Feb 12, 2019
b67a9be
update 注释
idefav Feb 12, 2019
61d37be
Merge branch 'master' of https://github.com/idefav/apollo
idefav Feb 12, 2019
144e165
update
idefav Feb 12, 2019
dae1d6e
Merge branch 'master' into master
idefav Feb 13, 2019
1600c9e
Merge branch 'master' into master
idefav Feb 13, 2019
9a3cf20
update auth
idefav Feb 13, 2019
4b261e6
Merge branch 'master' of https://github.com/idefav/apollo
idefav Feb 13, 2019
9a7dd0c
update yml
idefav Feb 13, 2019
6a92ec8
Merge branch 'master' into master
idefav Feb 13, 2019
cc0e516
update same bug and move ldap config to sample yml
idefav Feb 13, 2019
3636687
Merge branch 'master' of https://github.com/idefav/apollo
idefav Feb 13, 2019
be488cd
update log to slf4j
idefav Feb 13, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions apollo-portal/src/main/config/application-ldap-apacheds-sample.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
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
20 changes: 20 additions & 0 deletions apollo-portal/src/main/config/application-ldap-openldap-sample.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
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
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
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;
Expand All @@ -14,6 +15,7 @@
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;
Expand Down Expand Up @@ -285,7 +287,7 @@ protected void configure(HttpSecurity http) throws Exception {
*/
@Configuration
@Profile("ldap")
@EnableConfigurationProperties(LdapProperties.class)
@EnableConfigurationProperties({LdapProperties.class,LdapExtendProperties.class})
static class SpringSecurityLDAPAuthAutoConfiguration {

private final LdapProperties properties;
Expand Down Expand Up @@ -353,17 +355,33 @@ static class SpringSecurityLDAPConfigurer extends WebSecurityConfigurerAdapter {
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
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/*
* 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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@


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 {
idefav marked this conversation as resolved.
Show resolved Hide resolved

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.");
});
}
}
Loading