Я пишу веб-приложение Spring, которое требует от пользователей входа в систему. В моей компании есть сервер Active Directory, который я хотел бы использовать для этой цели. Однако у меня возникают проблемы с использованием Spring Security для подключения к серверу.
Я использую Spring 2.5.5 и Spring Security 2.0.3 вместе с Java 1.6.
Если я изменю URL-адрес LDAP на неправильный IP-адрес, это не вызовет исключения или чего-то еще, поэтому мне интересно, даже пытающийся для подключения к серверу для начала.
Хотя веб-приложение запускается нормально, любая информация, которую я вводю на странице входа, отклоняется. Ранее я использовал InMemoryDaoImpl, который работал нормально, поэтому остальная часть моего приложения, похоже, настроена правильно.
Вот мои компоненты, связанные с безопасностью:
<beans:bean id = "ldapAuthProvider" class = "org.springframework.security.providers.ldap.LdapAuthenticationProvider">
<beans:constructor-arg>
<beans:bean class = "org.springframework.security.providers.ldap.authenticator.BindAuthenticator">
<beans:constructor-arg ref = "initialDirContextFactory" />
<beans:property name = "userDnPatterns">
<beans:list>
<beans:value>CN = {0},OU=SBSUsers,OU=Users,OU=MyBusiness,DC=Acme,DC=com</beans:value>
</beans:list>
</beans:property>
</beans:bean>
</beans:constructor-arg>
</beans:bean>
<beans:bean id = "userDetailsService" class = "org.springframework.security.userdetails.ldap.LdapUserDetailsManager">
<beans:constructor-arg ref = "initialDirContextFactory" />
</beans:bean>
<beans:bean id = "initialDirContextFactory" class = "org.springframework.security.ldap.DefaultInitialDirContextFactory">
<beans:constructor-arg value = "ldap://192.168.123.456:389/DC=Acme,DC=com" />
</beans:bean>
У меня для всего включено логирование. Не вижу никаких сообщений в журнале ... Я обновил свой вопрос с помощью моей конфигурации Log4J.
Подскажите, какие для этого нужны jar-файлы. пожалуйста




У меня был тот же опыт, что и у вас, и в итоге я написал настраиваемого поставщика аутентификации, который выполняет запрос LDAP к серверу Active Directory.
Итак, мои компоненты, связанные с безопасностью:
<beans:bean id = "contextSource"
class = "org.springframework.security.ldap.DefaultSpringSecurityContextSource">
<beans:constructor-arg value = "ldap://hostname.queso.com:389/" />
</beans:bean>
<beans:bean id = "ldapAuthenticationProvider"
class = "org.queso.ad.service.authentication.LdapAuthenticationProvider">
<beans:property name = "authenticator" ref = "ldapAuthenticator" />
<custom-authentication-provider />
</beans:bean>
<beans:bean id = "ldapAuthenticator"
class = "org.queso.ad.service.authentication.LdapAuthenticatorImpl">
<beans:property name = "contextFactory" ref = "contextSource" />
<beans:property name = "principalPrefix" value = "QUESO\" />
</beans:bean>
Затем класс LdapAuthenticationProvider:
/**
* Custom Spring Security authentication provider which tries to bind to an LDAP server with
* the passed-in credentials; of note, when used with the custom {@link LdapAuthenticatorImpl},
* does <strong>not</strong> require an LDAP username and password for initial binding.
*
* @author Jason
*/
public class LdapAuthenticationProvider implements AuthenticationProvider {
private LdapAuthenticator authenticator;
public Authentication authenticate(Authentication auth) throws AuthenticationException {
// Authenticate, using the passed-in credentials.
DirContextOperations authAdapter = authenticator.authenticate(auth);
// Creating an LdapAuthenticationToken (rather than using the existing Authentication
// object) allows us to add the already-created LDAP context for our app to use later.
LdapAuthenticationToken ldapAuth = new LdapAuthenticationToken(auth, "ROLE_USER");
InitialLdapContext ldapContext = (InitialLdapContext) authAdapter
.getObjectAttribute("ldapContext");
if (ldapContext != null) {
ldapAuth.setContext(ldapContext);
}
return ldapAuth;
}
public boolean supports(Class clazz) {
return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(clazz));
}
public LdapAuthenticator getAuthenticator() {
return authenticator;
}
public void setAuthenticator(LdapAuthenticator authenticator) {
this.authenticator = authenticator;
}
}
Затем класс LdapAuthenticatorImpl:
/**
* Custom Spring Security LDAP authenticator which tries to bind to an LDAP server using the
* passed-in credentials; does <strong>not</strong> require "master" credentials for an
* initial bind prior to searching for the passed-in username.
*
* @author Jason
*/
public class LdapAuthenticatorImpl implements LdapAuthenticator {
private DefaultSpringSecurityContextSource contextFactory;
private String principalPrefix = "";
public DirContextOperations authenticate(Authentication authentication) {
// Grab the username and password out of the authentication object.
String principal = principalPrefix + authentication.getName();
String password = "";
if (authentication.getCredentials() != null) {
password = authentication.getCredentials().toString();
}
// If we have a valid username and password, try to authenticate.
if (!("".equals(principal.trim())) && !("".equals(password.trim()))) {
InitialLdapContext ldapContext = (InitialLdapContext) contextFactory
.getReadWriteContext(principal, password);
// We need to pass the context back out, so that the auth provider can add it to the
// Authentication object.
DirContextOperations authAdapter = new DirContextAdapter();
authAdapter.addAttributeValue("ldapContext", ldapContext);
return authAdapter;
} else {
throw new BadCredentialsException("Blank username and/or password!");
}
}
/**
* Since the InitialLdapContext that's stored as a property of an LdapAuthenticationToken is
* transient (because it isn't Serializable), we need some way to recreate the
* InitialLdapContext if it's null (e.g., if the LdapAuthenticationToken has been serialized
* and deserialized). This is that mechanism.
*
* @param authenticator
* the LdapAuthenticator instance from your application's context
* @param auth
* the LdapAuthenticationToken in which to recreate the InitialLdapContext
* @return
*/
static public InitialLdapContext recreateLdapContext(LdapAuthenticator authenticator,
LdapAuthenticationToken auth) {
DirContextOperations authAdapter = authenticator.authenticate(auth);
InitialLdapContext context = (InitialLdapContext) authAdapter
.getObjectAttribute("ldapContext");
auth.setContext(context);
return context;
}
public DefaultSpringSecurityContextSource getContextFactory() {
return contextFactory;
}
/**
* Set the context factory to use for generating a new LDAP context.
*
* @param contextFactory
*/
public void setContextFactory(DefaultSpringSecurityContextSource contextFactory) {
this.contextFactory = contextFactory;
}
public String getPrincipalPrefix() {
return principalPrefix;
}
/**
* Set the string to be prepended to all principal names prior to attempting authentication
* against the LDAP server. (For example, if the Active Directory wants the domain-name-plus
* backslash prepended, use this.)
*
* @param principalPrefix
*/
public void setPrincipalPrefix(String principalPrefix) {
if (principalPrefix != null) {
this.principalPrefix = principalPrefix;
} else {
this.principalPrefix = "";
}
}
}
И, наконец, класс LdapAuthenticationToken:
/**
* <p>
* Authentication token to use when an app needs further access to the LDAP context used to
* authenticate the user.
* </p>
*
* <p>
* When this is the Authentication object stored in the Spring Security context, an application
* can retrieve the current LDAP context thusly:
* </p>
*
* <pre>
* LdapAuthenticationToken ldapAuth = (LdapAuthenticationToken) SecurityContextHolder
* .getContext().getAuthentication();
* InitialLdapContext ldapContext = ldapAuth.getContext();
* </pre>
*
* @author Jason
*
*/
public class LdapAuthenticationToken extends AbstractAuthenticationToken {
private static final long serialVersionUID = -5040340622950665401L;
private Authentication auth;
transient private InitialLdapContext context;
private List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
/**
* Construct a new LdapAuthenticationToken, using an existing Authentication object and
* granting all users a default authority.
*
* @param auth
* @param defaultAuthority
*/
public LdapAuthenticationToken(Authentication auth, GrantedAuthority defaultAuthority) {
this.auth = auth;
if (auth.getAuthorities() != null) {
this.authorities.addAll(Arrays.asList(auth.getAuthorities()));
}
if (defaultAuthority != null) {
this.authorities.add(defaultAuthority);
}
super.setAuthenticated(true);
}
/**
* Construct a new LdapAuthenticationToken, using an existing Authentication object and
* granting all users a default authority.
*
* @param auth
* @param defaultAuthority
*/
public LdapAuthenticationToken(Authentication auth, String defaultAuthority) {
this(auth, new GrantedAuthorityImpl(defaultAuthority));
}
public GrantedAuthority[] getAuthorities() {
GrantedAuthority[] authoritiesArray = this.authorities.toArray(new GrantedAuthority[0]);
return authoritiesArray;
}
public void addAuthority(GrantedAuthority authority) {
this.authorities.add(authority);
}
public Object getCredentials() {
return auth.getCredentials();
}
public Object getPrincipal() {
return auth.getPrincipal();
}
/**
* Retrieve the LDAP context attached to this user's authentication object.
*
* @return the LDAP context
*/
public InitialLdapContext getContext() {
return context;
}
/**
* Attach an LDAP context to this user's authentication object.
*
* @param context
* the LDAP context
*/
public void setContext(InitialLdapContext context) {
this.context = context;
}
}
Вы заметите, что там есть несколько фрагментов, которые могут вам не понадобиться.
Например, моему приложению необходимо было сохранить контекст LDAP, успешно выполнивший вход в систему, для дальнейшего использования пользователем после входа в систему - цель приложения - позволить пользователям входить в систему с помощью своих учетных данных AD, а затем выполнять дополнительные функции, связанные с AD. Поэтому у меня есть собственный токен аутентификации, LdapAuthenticationToken, который я передаю (а не токен аутентификации по умолчанию Spring), который позволяет мне присоединять контекст LDAP. В LdapAuthenticationProvider.authenticate () я создаю этот токен и передаю его обратно; в LdapAuthenticatorImpl.authenticate () я прикрепляю контекст входа в систему к возвращаемому объекту, чтобы его можно было добавить к объекту аутентификации пользователя Spring.
Кроме того, в LdapAuthenticationProvider.authenticate () я назначаю всем зарегистрированным пользователям роль ROLE_USER - это то, что позволяет мне затем проверить эту роль в моих элементах URL-адреса перехвата. Вы захотите, чтобы это соответствовало любой роли, которую вы хотите протестировать, или даже назначать роли на основе групп Active Directory или чего-то еще.
Наконец, и как следствие этого, способ, которым я реализовал LdapAuthenticationProvider.authenticate (), дает всем пользователям с действующими учетными записями AD одну и ту же роль ROLE_USER. Очевидно, что в этом методе вы можете выполнить дальнейшие тесты для пользователя (то есть, входит ли пользователь в определенную группу AD?) И таким образом назначить роли или даже проверить какое-то условие, прежде чем даже предоставить пользователю доступ в все.
Подскажите, какие для этого нужны jar-файлы. пожалуйста.
Я столкнулся с той же проблемой. Но поскольку многие из этих методов устарели в новых версиях Spring, я больше не могу выполнять ту же работу, что и эта. Я задал свой вопрос здесь stackoverflow.com/questions/32070142/…. Мне интересно, поможете ли вы мне в этом @delfuego
Мне удалось пройти аутентификацию в активном каталоге с помощью Spring Security 2.0.4.
Я задокументировал настройки
http://maniezhilan.blogspot.com/2008/10/spring-security-204-with-active.html
Просто чтобы привести это в актуальное состояние. Spring Security 3.0 имеет полный пакет с реализациями по умолчанию, предназначенными для ldap-bind, а также для проверки подлинности запроса и сравнения.
Аутентификация LDAP без SSL небезопасна, и никто не может видеть учетные данные пользователя, когда они передаются на сервер LDAP. Я предлагаю использовать протокол LDAPS: \ для аутентификации. Это не требует каких-либо серьезных изменений в пружинной части, но вы можете столкнуться с некоторыми проблемами, связанными с сертификатами. Подробнее см. Аутентификация LDAP Active Directory весной с SSL.
Для справки, Spring Security 3.1 имеет поставщика аутентификации специально для Active Directory.
+1 Это должен быть главный ответ. По опыту могу сказать, что вы очень усложняете себе жизнь, если используете LdapAuthenticationProvider для аутентификации в AD. В современных версиях Spring вы можете делать то, что хотите, менее чем за 5 строк кода. См. Ссылку, которую Люк дал выше. Я также привел подробности в своем ответе ниже.
Из ответа Люка выше:
For reference, Spring Security 3.1 has an authentication provider [specifically for Active Directory][1].
Я пробовал описанное выше с помощью Spring Security 3.1.1: есть некоторые небольшие изменения по сравнению с ldap - группы активного каталога, членом которых является пользователь, проходят как исходный случай.
Раньше в ldap группы начинались с заглавной буквы и начинались с префикса «ROLE_», что позволяло легко находить их с помощью текстового поиска в проекте, но, очевидно, могли возникнуть проблемы в группе unix, если по какой-то странной причине были две отдельные группы, различающиеся только регистром т.е. учетные записи и учетные записи).
Также синтаксис требует ручного указания имени и порта контроллера домена, что немного пугает с точки зрения избыточности. Конечно, есть способ найти запись SRV DNS для домена в java, то есть эквивалент (из Samba 4 howto):
$ host -t SRV _ldap._tcp.samdom.example.com.
_ldap._tcp.samdom.example.com has SRV record 0 100 389 samba.samdom.example.com.
с последующим регулярным поиском A:
$ host -t A samba.samdom.example.com.
samba.samdom.example.com has address 10.0.0.1
(На самом деле может потребоваться поиск записи _kerberos SRV ...)
Вышеупомянутое было с Samba4.0rc1, мы постепенно обновляем среду Samba 3.x LDAP до Samba AD.
Как и в ответе Люка выше:
Spring Security 3.1 has an authentication provider specifically for Active Directory.
Вот подробное описание того, как это можно легко сделать с помощью ActiveDirectoryLdapAuthenticationProvider.
В resources.groovy:
ldapAuthProvider1(ActiveDirectoryLdapAuthenticationProvider,
"mydomain.com",
"ldap://mydomain.com/"
)
В Config.groovy:
grails.plugin.springsecurity.providerNames = ['ldapAuthProvider1']
Это весь код, который вам нужен. Вы можете в значительной степени удалить все остальные настройки grails.plugin.springsecurity.ldap. * В Config.groovy, поскольку они не применяются к этой настройке AD.
Для документации см .: http://docs.spring.io/spring-security/site/docs/3.1.x/reference/springsecurity-single.html#ldap-active-directory
У меня похожая проблема. Нужно ли мне изменять представление, чтобы защищенный контроллер просто автоматически находил принципала?
If you are using Spring security 4 you can also implement same using given class
- SecurityConfig.java
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
static final Logger LOGGER = LoggerFactory.getLogger(SecurityConfig.class);
@Autowired
protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(activeDirectoryLdapAuthenticationProvider());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/").permitAll()
.anyRequest().authenticated();
.and()
.formLogin()
.and()
.logout();
}
@Bean
public AuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
ActiveDirectoryLdapAuthenticationProvider authenticationProvider =
new ActiveDirectoryLdapAuthenticationProvider("<domain>", "<url>");
authenticationProvider.setConvertSubErrorCodesToExceptions(true);
authenticationProvider.setUseAuthenticationRequestCredentials(true);
return authenticationProvider;
}
}
На самом деле это не столько ответ, сколько уточняющий вопрос - полностью ли включено ведение журнала для весенних пакетов безопасности?