Spring Security extensions for Active Directory |
|
Sat Oct 11 23:45:49 CST 2008发表于JRoller |
|
|
We could not find information on how to integrate Spring Security with MS ActiveDirectory, so we developed LDAP provider for Active Directory Server, using existing LDAP provider as a base for the customization. The LDAP provider for Active Directory Server generates LDAP parameters with Active Directory - specific syntax. It consists of several classes. ActiveDirectoryBindAuthenticator is an authenticator which binds as a user. Generates ActiveDirectory - specific syntax of LDAP parameters. Can only use DefaultSpringSecurityContextSource as contextSource. Similar to public class ActiveDirectoryBindAuthenticator implements LdapAuthenticator, MessageSourceAware {
private final ContextSource contextSource;
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
protected final Log logger = LogFactory.getLog(this.getClass());
public ActiveDirectoryBindAuthenticator(DefaultSpringSecurityContextSource contextSource) {
Assert.notNull(contextSource, "contextSource must not be null.");
this.contextSource = contextSource;
}
public DirContextOperations authenticate(Authentication authentication) {
Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,
"Can only process UsernamePasswordAuthenticationToken objects");
String username = authentication.getName();
String password = (String) authentication.getCredentials();
// Active Directory requires principalDn in the form: username-AT-dc1-DOT-dc2.
String principalDn = determinePrincipalDn(username);
DirContextOperations user = bindWithDn(principalDn, username, password);
if (user == null) {
throw new BadCredentialsException(messages.getMessage(
"BindAuthenticator.badCredentials", "Bad credentials"));
}
// Store password in user: will be used by the authorities populator to bind (again)
// as username/password.
user.addAttributeValue(Context.SECURITY_CREDENTIALS, password);
return user;
}
/**
* Generates principalDn in the form: ActiveDirectoryAuthoritiesPopulator obtains user role information from the directory, uses ActiveDirectory - specific syntax for LDAP parameters. It obtains roles by performing a search for "groups" the user is a member of. Can only use DefaultSpringSecurityContextSource as contextSource. Similar to DefaultLdapAuthoritiesPopulator.
public class ActiveDirectoryAuthoritiesPopulator implements LdapAuthoritiesPopulator {
protected final Log logger = LogFactory.getLog(this.getClass());
/**
* A default role which will be assigned to all authenticated users if set
*/
private GrantedAuthority defaultRole;
private ContextSource contextSource;
/**
* Controls used to determine whether group searches should be performed over the full sub-tree
* from the base DN. Modified by searchSubTree property
*/
private final SearchControls searchControls = new SearchControls();
/**
* The ID of the attribute which contains the role name for a group
*/
private String groupRoleAttribute = "cn";
/**
* The base DN from which the search for group membership should be performed
*/
private String groupSearchBase;
/**
* The pattern to be used for the user search. {0} is the user's DN
*/
private String groupSearchFilter = "member={0}";
/**
* Attributes of the User's LDAP Object that contain role name information.
*/
private String rolePrefix = "ROLE_";
private boolean convertToUpperCase = true;
public ActiveDirectoryAuthoritiesPopulator(DefaultSpringSecurityContextSource contextSource,
String groupSearchBase) {
this.setContextSource(contextSource);
this.setGroupSearchBase(groupSearchBase);
}
protected Set getAdditionalRoles(DirContextOperations user, String username) {
return null;
}
public GrantedAuthority getDefaultRole() {
return this.defaultRole;
}
public void setDefaultRole(GrantedAuthority defaultRole) {
this.defaultRole = defaultRole;
}
private void setContextSource(DefaultSpringSecurityContextSource contextSource) {
Assert.notNull(contextSource, "contextSource must not be null");
this.contextSource = contextSource;
}
protected ContextSource getContextSource() {
return contextSource;
}
private void setGroupSearchBase(String groupSearchBase) {
Assert.notNull(groupSearchBase,
"The groupSearchBase (name to search under), must not be null.");
this.groupSearchBase = groupSearchBase;
if (groupSearchBase.length() == 0) {
logger
.info("groupSearchBase is empty. Searches will be performed from the context source base");
}
}
public String getGroupRoleAttribute() {
return this.groupRoleAttribute;
}
public String getGroupSearchFilter() {
return this.groupSearchFilter;
}
public String getRolePrefix() {
return this.rolePrefix;
}
public boolean isConvertToUpperCase() {
return this.convertToUpperCase;
}
public Set getGroupMembershipRoles(String userDn, String username, String password) {
Set authorities = new HashSet();
if (getGroupSearchBase() == null) {
return authorities;
}
if (logger.isDebugEnabled()) {
logger.debug("Searching for roles for user with DN = " + "'" + userDn
+ "', with filter " + groupSearchFilter + " in search base '" + groupSearchBase
+ "'");
}
String principalDn = determinePrincipalDn(username);
// bind as principalDn/password
ActiveDirectoryLdapTemplate template = new ActiveDirectoryLdapTemplate(
new BindWithSpecificDnContextSource((SpringSecurityContextSource) getContextSource(),
principalDn, password));
template.setSearchControls(searchControls);
// search for roles userDn is member of
Set userRoles = template.searchForSingleAttributeValues(getGroupSearchBase(),
groupSearchFilter, new String[] { userDn }, groupRoleAttribute);
if (logger.isDebugEnabled()) {
logger.debug("Roles from search: " + userRoles);
}
// convert role names to GrantedAuthorityImpl objects
// for each role
Iterator it = userRoles.iterator();
while (it.hasNext()) {
String role = (String) it.next();
if (convertToUpperCase) {
role = role.toUpperCase();
}
authorities.add(new GrantedAuthorityImpl(rolePrefix + role));
}
return authorities;
}
protected String getGroupSearchBase() {
return groupSearchBase;
}
public void setConvertToUpperCase(boolean convertToUpperCase) {
this.convertToUpperCase = convertToUpperCase;
}
public void setDefaultRole(String defaultRole) {
Assert.hasLength(defaultRole, "defaultRole must be not empty");
this.defaultRole = new GrantedAuthorityImpl(defaultRole);
}
public void setGroupRoleAttribute(String groupRoleAttribute) {
Assert.hasLength(groupRoleAttribute, "groupRoleAttribute must be not empty");
this.groupRoleAttribute = groupRoleAttribute;
}
public void setGroupSearchFilter(String groupSearchFilter) {
Assert.hasLength(groupSearchFilter, "groupSearchFilter must be not empty");
this.groupSearchFilter = groupSearchFilter;
}
public void setRolePrefix(String rolePrefix) {
Assert.notNull(rolePrefix, "rolePrefix must not be null");
this.rolePrefix = rolePrefix;
}
public void setSearchSubtree(boolean searchSubtree) {
int searchScope = searchSubtree ? SearchControls.SUBTREE_SCOPE
: SearchControls.ONELEVEL_SCOPE;
searchControls.setSearchScope(searchScope);
}
public final GrantedAuthority[] getGrantedAuthorities(DirContextOperations user, String username) {
String userDn = user.getNameInNamespace();
if (logger.isDebugEnabled()) {
logger.debug("Getting authorities for user " + userDn);
}
// password must be supplied by the ActiveDirectoryBindAuthenticator
String password = user.getStringAttribute(Context.SECURITY_CREDENTIALS);
Set roles = getGroupMembershipRoles(userDn, username, password);
Set extraRoles = getAdditionalRoles(user, username);
if (extraRoles != null) {
roles.addAll(extraRoles);
}
if (defaultRole != null) {
roles.add(defaultRole);
}
return (GrantedAuthority[]) roles.toArray(new GrantedAuthority[roles.size()]);
}
/**
* Generates principalDn in the form:
public class BindWithSpecificDnContextSource implements ContextSource {
private final SpringSecurityContextSource ctxFactory;
private final String userDn;
private final String password;
public BindWithSpecificDnContextSource(SpringSecurityContextSource ctxFactory, String userDn,
String password) {
this.ctxFactory = ctxFactory;
this.userDn = userDn;
this.password = password;
}
public DirContext getReadOnlyContext() throws DataAccessException {
return ctxFactory.getReadWriteContext(userDn, password);
}
public DirContext getReadWriteContext() throws DataAccessException {
return getReadOnlyContext();
}
}
public final class NamingUtils {
/**
* This is a static class that should not be instantiated.
*/
private NamingUtils() throws InstantiationException {
}
/**
* Prepare principalDn in the form required by Active Directory:
ActiveDirectoryLdapTemplate is an ActiveDirectory equivalent of the SpringSecurityLdapTemplate class. It simplifies ActiveDirectory access within Spring Security's ActiveDirectory-related services.
public class ActiveDirectoryLdapTemplate extends SpringSecurityLdapTemplate {
protected final Log logger = LogFactory.getLog(this.getClass());
private final SearchControls searchControls = new SearchControls();
public ActiveDirectoryLdapTemplate(ContextSource contextSource) {
super(contextSource);
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
}
public Set searchForSingleAttributeValues(String base, String filter, Object[] filterArgs,
final String attributeName) {
final HashSet set = new HashSet();
ContextMapper roleMapper = new ContextMapper() {
public Object mapFromContext(Object ctx) {
DirContextAdapter adapter = (DirContextAdapter) ctx;
String[] values = adapter.getStringAttributes(attributeName);
if (values == null || values.length == 0) {
logger.debug("No attribute value found for '" + attributeName + "'");
} else {
set.addAll(Arrays.asList(values));
}
return null;
}
};
SearchControls ctls = new SearchControls();
ctls.setSearchScope(searchControls.getSearchScope());
ctls.setReturningAttributes(new String[] { attributeName });
// ActiveDirectory - specific
ctls.setReturningObjFlag(true);
// ActiveDirectory - specific
search(base, filter, filterArgs, ctls, roleMapper);
return set;
}
public List search(String base, String filter, Object[] filterArgs, SearchControls controls,
ContextMapper mapper) {
return search(base, filter, filterArgs, controls, mapper, new NullDirContextProcessor());
}
public List search(String base, String filter, Object[] filterArgs, SearchControls controls,
ContextMapper mapper, DirContextProcessor processor) {
ContextMapperCallbackHandler handler = new ContextMapperCallbackHandler(mapper);
search(base, filter, filterArgs, controls, handler, processor);
return handler.getList();
}
public void search(final String base, final String filter, final Object[] filterArgs,
final SearchControls controls, NameClassPairCallbackHandler handler,
DirContextProcessor processor) {
// Create a SearchExecutor to perform the search.
SearchExecutor se = new SearchExecutor() {
public NamingEnumeration executeSearch(DirContext ctx)
throws javax.naming.NamingException {
return ctx.search(base, filter, filterArgs, controls);
}
};
search(se, handler, processor);
}
private final class NullDirContextProcessor implements DirContextProcessor {
public void postProcess(DirContext ctx) throws NamingException {
// Do nothing
}
public void preProcess(DirContext ctx) throws NamingException {
// Do nothing
}
}
}
You can find source code (including unit and integration tests) in the Spring Security project JIRA: SEC-876
| |
| 阅读全文... | |
本站相关内容:(RSS) |
|
MyFaces Security EL ExtensionsMyFaces Security EL Extensions Posted by cagataycivici on November 20, 2006 Last week I’ve completed my work on the Security EL extension in Myfaces and added it to sandbox. By the help of SecurityContextVariableResolver and the SecurityContextPropertyResolver it’s possible to retrieve information from the underlying authentication/authorization mechanism that is used in the JSF application. The advantage of the resolver over existing enabledOnUserRole-visibleOnUserRole attributes is that th |
|
List of Spring ExtensionsSpringframework.org - (28 reads) LiveLive extension projects come with full source code, samples and documentation. Extensions are grouped according to the platform they target.
IncubatorIncubator extension projects are in the process of maturing until they are ready for becoming prime-time live extensions. Extensions are grouped according to the platform they target. Want to propose a new Spring Extension?If you think you have a project, either an existing project or maybe an entirely new idea, that would make a good Spring Extension then here's the process to follow. |
|
Spring Extensions FAQSpringframework.org - (30 reads) What is Spring Extensions?Spring Extensions is a new collaboration effort from SpringSource to encourage and enable community driven extensions to the Spring projects. It provides infrastructure, process and endorsement to selected projects that are consistent with the best of breed practices, principles and high quality associated with the SpringSource engineering teams. How do I submit my project as a Spring Extension?Please read these instructions to learn more about the proposal process. What's in it for me?Every Spring Extension is officially endorsed by SpringSource. While not every extension is developed or contributed by a SpringSource employee, every extension has a SpringSource sponsor who is responsible for working with the project lead to make the extension as successful as possible. As well as an official endorsement, SpringSource also provide a development environment and services, including:
How do you differ from SourceForge, code.google.com etc.?SourceForge provides development infrastructure and is very good at it. It also provides a public distribution channel. What it doesn't provide are project management capabilities (release management, etc.). Critically, it doesn't provide any endorsement or affiliation with the hosted code. Spring Extensions provides the infrastructure but also provides the endorsement and quality assurance that is expected from the strong association with SpringSource. What happens to my extension once it has been created?Every extension starts off in the incubator, at version 0.1. If appropriate, after reaching a level of maturity and stability, the extension will be released as "live", where it will continue to grow. If the extension becomes redundant (maybe the portfolio projects provide an alternative, or the requirements are no longer relevant) it will be archived. If the extension is of a particular interest to a product lead, it may be incorporated into that full Spring project, or in some cases it might be that an extension represents enough value to the community to become a Spring project in its own right. Are the extensions purely Java based? What about extensions to Spring.NET? Python?
|
|
Spring Extensions HomeSpringframework.org - (28 reads) Introducing Spring Extensions!
|
|
互联网相关内容: |
|
| MyFaces Security EL Extensions (2007年11月24日) | |
| Spring Security - Spring Security (2008年04月21日) | |
| List of Spring Extensions (2008年10月14日) | |
| Spring Extensions FAQ (2008年10月14日) | |
| Spring Extensions Home (2008年10月14日) | |
| List of Spring Extensions (2008年10月14日) | |
| SOAP Security Extensions: Digital Signature (2007年11月07日) | |
| Spring Security and Java Security Community (2008年04月17日) | |
| Spring Security and Java Security Community (2008年04月17日) |




