We have a typical requirement in our application.
We have two Spring Security configurations:
1. CAS Server
2. LDAP (NTLM)
So, now we need to check whether the CAS server is available or not and use either CAS or LDAP security configuration based on CAS server availability.
I was trying to dynamically change the Entrypoint url, however, both the config files are using different beans/classes.
Is there any other way to achieve this?
Please let me know how if we can achieve this and how?
Thanks in advance.
Raj
You could create a DelegatingAuthenticationEntryPoint that would delegate to the standard CasAuthenticationEntryPoint if the CAS Server was up or otherwise delegate to the LoginUrlAuthenticationEntryPoint. The implementation would look something like the following
public class DelegatingAuthenticationEntryPoint implements AuthenticationEntryPoint {
private AuthenticationEntryPoint casAuthenticationEntryPoint;
private AuthenticationEntryPoint ldapAuthenticationEntryPoint;
public DelegatingAuthenticationEntryPoint(AuthenticationEntryPoint casAuthenticationEntryPoint,
AuthenticationEntryPoint ldapAuthenticationEntryPoint) {
this.casAuthenticationEntryPoint = casAuthenticationEntryPoint;
this.ldapAuthenticationEntryPoint = ldapAuthenticationEntryPoint;
}
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)
throws IOException, ServletException {
if(casServerAvailable()) {
casAuthenticationEntryPoint.commence(request, response, authException);
} else {
ldapAuthenticationEntryPoint.commence(request, response, authException);
}
}
private boolean casServerAvailable() {
// TODO implement this method
return false;
}
}
You would then wire the DelegatingAuthenticationEntryPoint using the entry-point-ref attribute similar to the following:
<sec:http entry-point-ref="delegateEntryPoint">
...
</sec:http>
<bean id="delegateEntryPoint" class="sample.DelegatingAuthenticationEntryPoint">
<constructor-arg>
<bean class="org.springframework.security.cas.web.CasAuthenticationEntryPoint"
p:serviceProperties-ref="serviceProperties"
p:loginUrl="https://example.com/cas/login" />
</constructor-arg>
<constructor-arg>
<bean class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint"
p:loginFormUrl="/login"/>
</constructor-arg>
</bean>
Related
I'm trying to use Waffle authentication with Spring Security, in a Spring Boot fashion. Expected result is 'block everything if Negotiate fails'.
Waffle project provides a configuration example for this kind of use case (there is in this example a fallback to simple HTTP auth if Negotiate fails, which I don't need), assuming configuration is done through web.xml. But despite many attempts, I don't understand how to plug Waffle with Spring Security using Boot and Java-only configuration. I'm using Spring Boot 1.2.1.RELEASE with starters web and security, Waffle version is 1.7.3.
I realize that this is not a specific question but Spring forum now redirects here and Waffle guys don't know about Spring Boot. Could someone help me translate an XML Spring Security configuration to Spring Boot?
First step is declaring a filter chain and context loader listener.
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/waffle-filter.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
I'm assuming (am I wrong?) that this is already handled by #EnableWebMvcSecurity, so nothing to do here.
Next is declaring a couple of provider beans, so I translate this
<bean id="waffleWindowsAuthProvider" class="waffle.windows.auth.impl.WindowsAuthProviderImpl" />
<bean id="negotiateSecurityFilterProvider" class="waffle.servlet.spi.NegotiateSecurityFilterProvider">
<constructor-arg ref="waffleWindowsAuthProvider" />
</bean>
<bean id="basicSecurityFilterProvider" class="waffle.servlet.spi.BasicSecurityFilterProvider">
<constructor-arg ref="waffleWindowsAuthProvider" />
</bean>
<bean id="waffleSecurityFilterProviderCollection" class="waffle.servlet.spi.SecurityFilterProviderCollection">
<constructor-arg>
<list>
<ref bean="negotiateSecurityFilterProvider" />
<ref bean="basicSecurityFilterProvider" />
</list>
</constructor-arg>
</bean>
<bean id="waffleNegotiateSecurityFilter" class="waffle.spring.NegotiateSecurityFilter">
<property name="Provider" ref="waffleSecurityFilterProviderCollection" />
</bean>
to this
#Bean
public WindowsAuthProviderImpl waffleWindowsAuthProvider() {
return new WindowsAuthProviderImpl();
}
#Bean
#Autowired
public NegotiateSecurityFilterProvider negotiateSecurityFilterProvider(final WindowsAuthProviderImpl windowsAuthProvider) {
return new NegotiateSecurityFilterProvider(windowsAuthProvider);
}
#Bean
#Autowired
public BasicSecurityFilterProvider basicSecurityFilterProvider(final WindowsAuthProviderImpl windowsAuthProvider) {
return new BasicSecurityFilterProvider(windowsAuthProvider);
}
#Bean
#Autowired
public SecurityFilterProviderCollection waffleSecurityFilterProviderCollection(final NegotiateSecurityFilterProvider negotiateSecurityFilterProvider, final BasicSecurityFilterProvider basicSecurityFilterProvider) {
final SecurityFilterProvider[] securityFilterProviders = {
negotiateSecurityFilterProvider,
basicSecurityFilterProvider
};
return new SecurityFilterProviderCollection(securityFilterProviders);
}
#Bean
#Autowired
public NegotiateSecurityFilter waffleNegotiateSecurityFilter(final SecurityFilterProviderCollection securityFilterProviderCollection) {
final NegotiateSecurityFilter negotiateSecurityFilter = new NegotiateSecurityFilter();
negotiateSecurityFilter.setProvider(securityFilterProviderCollection);
return negotiateSecurityFilter;
}
Final step is sec:http section configuration. An entry point is declared and filter is placed before BASIC auth filter.
Example:
<sec:http entry-point-ref="negotiateSecurityFilterEntryPoint">
<sec:intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY" />
<sec:custom-filter ref="waffleNegotiateSecurityFilter" position="BASIC_AUTH_FILTER" />
</sec:http>
<bean id="negotiateSecurityFilterEntryPoint" class="waffle.spring.NegotiateSecurityFilterEntryPoint">
<property name="Provider" ref="waffleSecurityFilterProviderCollection" />
</bean>
My Boot translation:
#Autowired
private NegotiateSecurityFilterEntryPoint authenticationEntryPoint;
#Autowired
private NegotiateSecurityFilter negotiateSecurityFilter;
#Override
protected void configure(final HttpSecurity http) throws Exception {
http
.authorizeRequests().anyRequest().authenticated()
.and()
.addFilterBefore(this.negotiateSecurityFilter, BasicAuthenticationFilter.class)
.httpBasic().authenticationEntryPoint(this.authenticationEntryPoint);
}
#Bean
#Autowired
public NegotiateSecurityFilterEntryPoint negotiateSecurityFilterEntryPoint(final SecurityFilterProviderCollection securityFilterProviderCollection) {
final NegotiateSecurityFilterEntryPoint negotiateSecurityFilterEntryPoint = new NegotiateSecurityFilterEntryPoint();
negotiateSecurityFilterEntryPoint.setProvider(securityFilterProviderCollection);
return negotiateSecurityFilterEntryPoint;
}
Running this configuration leads to strange behavior: sometimes NTLM is triggered and succeed, sometimes Negotiate filter crashes with an 'invalid token supplied' error (same credentials, user, browser, configuration).
Provided example works like a charm, which makes me think that my Boot configuration is in question.
Any help appreciated!
Spring Boot auto-registers all Filter beans so in this case the NegotiateSecurityFilter ends up being twice in the filter chain.
You have to disable the auto-registration for this specific Filter by creating a FilterRegistrationBean overriding this behavior:
#Bean
public FilterRegistrationBean registration(NegotiateSecurityFilter filter) {
FilterRegistrationBean registration = new FilterRegistrationBean(filter);
registration.setEnabled(false);
return registration;
}
Also, as Dave Syer mentioned, you should be setting the authentication entry point bean using the ExceptionHandlingConfigurer.
#Override
protected void configure(HttpSecurity http) throws Exception {
http.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint);
// ...
}
I'm using spring/spring-security 3.1 and want to take some action depends on the authorities in ATHENTICATION object whenever session is timed out.AUTHENTICATION object is null on session timeout.
Here are the files for reference:
security config:
<logout invalidate-session="true" logout-url="/j_spring_security_logout" success-handler-ref="logoutSuccessHandler"/>
<!-- Custom filter to deny unwanted users even though registered -->
<custom-filter ref="blacklistFilter" before="FILTER_SECURITY_INTERCEPTOR" />
<!-- Custom filter for username, password and domain. The real customization is done in the customAuthenticationManager -->
<custom-filter ref="authenticationFilter" position="FORM_LOGIN_FILTER" />
</http>
Note: tried with "invalidate-session="false"" as well.
and my custom LogoutSuccessHandler:
public class EnterpriseLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler{
#Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
for(GrantedAuthority authority: authentication.getAuthorities()){
if(authority.getAuthority()!=null)
logger.debug("THE CURRENT AUTHORITY FOR THE SESSION IS : {} ",authority.getAuthority().toString());
else
logger.debug("THE CURRENT AUTHORITY FOR THE SESSION IS authority.getAuthority(): NULL ");
if(authority.getAuthority()!=null && authority.getAuthority().equalsIgnoreCase(Operational.SPRING_SECURITY.LOGIN_SOURCE_BEACON)){
loginSource = authority.getAuthority().toString();
break;
}
}
}
}
So, there is the question how I can obtain Authentication object on the session timeout.
Can some help me to find out the solution for the above problem.
Thanks in advance.
How to get username or UserDetail object in logout-success-url? Below is my configuration:
<form-login default-target-url="/security/loginSuccess.action"
always-use-default-target="false"
authentication-failure-url="/security/loginFailed.action"
login-page="/security/restrictUser.action"/>
<logout invalidate-session="true" logout-success-url="/security/logoutUser.action" />
<session-management
session-authentication-strategy-ref="customMsgAwareConcurrentSessionControlStartegy"
invalid-session-url="/security/logoutUser.action"/>
I want to get the username who has requested for logout within logout-success-url (for me it is a Struts 2 action method). I have checked SecurityContextHolder, but it seems that SecurityContextHolder is cleared when the control reaches my action method in logout-success-url.
Use custom LogoutSuccessHandler for that. Inside your configuration define bean and use success-handler-ref attribute of <logout> tag.
<logout success-handler-ref="customLogoutSuccessHandler" />
<beans:bean id="customLogoutSuccessHandler" class="..." />
And inside CustomLogoutSuccessHandler there is onLogoutSuccess method which has Authentication argument.
public class CustomLogoutSuccessHandler extends
AbstractAuthenticationTargetUrlRequestHandler implements
LogoutSuccessHandler {
public void onLogoutSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication)
throws IOException, ServletException {
// use authentication to get user name
}
}
I'm using password-parameter (as below) to customize the name of the request parameter which contains the password. How to do the same with remember-me (default _spring_security_remember_me) ?
<security:form-login password-parameter="j_password_input" ... />
You have a few options which I have explained in further detail below
Use the Security Namespace with a BeanPostProcessor
Use the services-ref and configure RememberMeServices Manually
Use the Security Namespace with a BeanPostProcessor
The namespace does not have support for configuring the remember me parameter, but you can use a tip from the FAQ on how to still use the namespace support, but customise the result. The trick is to use a BeanPostProcessor to set the parameter field on AbstractRememberMeServices. You can find an example of this below:
public class MyBeanPostProcessor implements BeanPostProcessor {
public Object postProcessAfterInitialization(Object bean, String name) {
if (bean instanceof AbstractRememberMeServices) {
AbstractRememberMeServices rememberMe = (AbstractRememberMeServices) bean;
rememberMe.setParameter("myParamname");
}
return bean;
}
public Object postProcessBeforeInitialization(Object bean, String name) {
return bean;
}
}
Then you would need to use the namespace as you normally would and add MyBeanPostProcessor to your Spring configuration as shown below:
<security:http ..>
...
<security:remember-me/>
</security:http>
<bean class="sample.MyBeanPostProcessor"/>
Use the services-ref and configure RememberMeServices Manually
You can also use the services-ref attribute too, but this involves a little more configuration. For example, if you wanted you could use the following configuration:
<security:http ..>
...
<security:remember-me services-ref="rememberMeServices"/>
</security:http>
<bean id="rememberMeServices"
class="org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices">
<property name="key" value="mustMatchRememberMeAuthenticationProvidersKey"/>
<property name="parameter" value="myParamName"/>
<!-- You must refer to a bean that implements UserDetailsService
in this example the bean id is userDetailsService -->
<property name="userDetailsService" ref="userDetailsService"/>
</bean>
As of Spring Security 3.2.x, you can set this with the remember-me-parameter parameter on the remember-me element.
I'm trying to authenticate and then query our corporate LDAP using Spring LDAP and Spring security. I managed to make authentication work but when I attempt to run search I always get the following exception
In order to perform this operation a successful bind must be completed on the connection
After much research I have a theory that after I authenticate and before I can query I need to bind to connection. I just don't know what and how?
Just to mention - I can successfully browse and search our LDAP using JXplorer so my parameters are correct.
Here's section of my securityContext.xml
<security:http auto-config='true'>
<security:intercept-url pattern="/reports/goodbye.html"
access="ROLE_LOGOUT" />
<security:intercept-url pattern="/reports/**" access="ROLE_USER" />
<security:http-basic />
<security:logout logout-url="/reports/logout"
logout-success-url="/reports/goodbye.html" />
</security:http>
<security:ldap-server url="ldap://s140.foo.com:1389/dc=td,dc=foo,dc=com" />
<security:authentication-manager>
<security:authentication-provider ref="ldapAuthProvider">
</security:authentication-provider>
</security:authentication-manager>
<!-- Security beans -->
<bean id="contextSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
<constructor-arg value="ldap://s140.foo.com:1389/dc=td,dc=foo,dc=com" />
</bean>
<bean id="ldapAuthProvider"
class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
<constructor-arg>
<bean class="foo.bar.reporting.server.security.ldap.LdapAuthenticatorImpl">
<property name="contextFactory" ref="contextSource" />
<property name="principalPrefix" value="TD\" />
<property name="employee" ref="employee"></property>
</bean>
</constructor-arg>
<constructor-arg>
<bean class="foo.bar.reporting.server.security.ldap.LdapAuthoritiesPopulator" />
</constructor-arg>
</bean>
<!-- DAOs -->
<bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate">
<constructor-arg ref="contextSource" />
Here's code snippet from LdapAuthenticatorImpl that performs authentication. No problem here:
#Override
public DirContextOperations authenticate(final Authentication authentication) {
// Grab the username and password out of the authentication object.
final String name = authentication.getName();
final String principal = this.principalPrefix + name;
String password = "";
if (authentication.getCredentials() != null) {
password = authentication.getCredentials().toString();
}
if (!("".equals(principal.trim())) && !("".equals(password.trim()))) {
final InitialLdapContext ldapContext = (InitialLdapContext)
this.contextFactory.getContext(principal, password);
// We need to pass the context back out, so that the auth provider
// can add it to the Authentication object.
final DirContextOperations authAdapter = new DirContextAdapter();
authAdapter.addAttributeValue("ldapContext", ldapContext);
this.employee.setqId(name);
return authAdapter;
} else {
throw new BadCredentialsException("Blank username and/or password!");
}
}
And here's another code snippet from EmployeeDao with my futile attempt to query:
public List<Employee> queryEmployeesByName(String query)
throws BARServerException {
AndFilter filter = new AndFilter();
filter.and(new EqualsFilter("objectclass", "person"));
filter.and(new WhitespaceWildcardsFilter("cn", query));
try {
// the following line throws bind exception
List result = ldapTemplate.search(BASE, filter.encode(),
new AttributesMapper() {
#Override
public Employee mapFromAttributes(Attributes attrs)
throws NamingException {
Employee emp = new Employee((String) attrs.get("cn").get(),
(String) attrs.get("cn").get(),
(String) attrs.get("cn").get());
return emp;
}
});
return result;
} catch (Exception e) {
throw new BarServerException("Failed to query LDAP", e);
}
}
And lastly - the exception I'm getting
org.springframework.ldap.UncategorizedLdapException:
Uncategorized exception occured during LDAP processing; nested exception is
javax.naming.NamingException: [LDAP: error code 1 - 00000000: LdapErr:
DSID-0C090627, comment: In order to perform this operation a successful bind
must be completed on the connection., data 0, vece]; remaining name
'DC=TD,DC=FOO,DC=COM'
It looks like your LDAP is configured to not allow a search without binding to it (no anonymous bind). Also you have implemented PasswordComparisonAuthenticator and not BindAuthenticator to authenticate to LDAP.
You could try modifying your queryEmployeesByName() method to bind and then search, looking at some examples in the doc.
I'm going to accept #Raghuram answer mainly because it got me thinking in the right direction.
Why my code was failing? Turned out - the way I wired it I was trying to perform anonymous search which is prohibited by the system - hence the error.
How to rewire example above to work? First thing (and ugly thing at that) you need to provide user name and password of user that will be used to access the system. Very counterintuitive even when you login and authenticated, even if you are using BindAuthenticator system will not attempt to reuse your credentials. Bummer. So you need to stick 2 parameters into contextSource definition like so:
<bean id="contextSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
<constructor-arg value="ldap://foo.com:389/dc=td,dc=foo,dc=com" />
<!-- TODO - need to hide this or encrypt a password -->
<property name="userDn" value="CN=admin,OU=Application,DC=TD,DC=FOO,DC=COM" />
<property name="password" value="blah" />
</bean>
Doing that allowed me to replace custom implementation of authenticator with generic BindAuthenticator and then my Java search started working
I got the same error, couldn't find a solution.
Finally I changed the application pool identity to network service and everything worked like a charm.
(I have windows authentication and anonymous enabled on my site)