Migrating a Waffle Spring Security XML configuration to Spring Boot - spring-security

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

Related

#Secured is not working while integrating Spring Security in Jersey project

I have a demo JAX-RS project using Jersey. Now I am trying add Spring Security's method level security but unfortunately its not working although intercept-url xml way is working fine.
Added all the dependency in my pom.xml
Updating web.xml as
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/security.xml,
/WEB-INF/beans.xml
</param-value>
</context-param>
<!-- this is default security impl name used by deletetingFiterProxy -->
<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>
Updating /WEB-INF/security.xml
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<!-- kind of authentication applied 1) Basic 2) form-based etc.. auto-config="true" use-expressions="true"-->
<http auto-config="true">
<http-basic />
</http>
<!-- this allow to enable security annotations in restful resoruces -->
<global-method-security secured-annotations="enabled" />
<!-- for defining users and roles -->
<authentication-manager>
<authentication-provider>
<user-service>
<user name="admin" password="admin" authorities="ROLE_CUSTOMER,ROLE_ADMIN"/>
<user name="student" password="student" authorities="ROLE_CUSTOMER"/>
</user-service>
</authentication-provider>
</authentication-manager>
</beans:beans>
Annotating service inteface methods
public interface StudentServiceInterface {
#GET
#Path("/students")
#Secured("ROLE_CUSTOMER")
public Response getStudents();
#GET
#Path("/students/{id}")
#Secured("ROLE_CUSTOMER")
public Response getStudent(#PathParam("id") int id);
#POST
#Path("/students")
#Consumes(MediaType.APPLICATION_JSON)
#Secured("ROLE_ADMIN")
public Response addStudent(Student stu);
}
Now when I try to access the resource student (/student) class it opens without asking password.
http://localhost:3126/securitydemo/webapi/db/students
StudentServiceInterface interface implementation
#Path("/db")
#Produces(MediaType.APPLICATION_JSON)
public class StudentService implements StudentServiceInterface{
static StudentDao data= new StudentDaoImpl();
#Override
public Response getStudents(){
GenericEntity<List<Student>> entity = new GenericEntity<List<Student>>(data.getAllStudents()){};
return Response.ok(entity).build();
}
#Override
public Response getStudent(#PathParam("id") int id){
return Response.ok(data.getStudent(id)).build();
}
#Override
public Response addStudent(Student stu) {
data.addStudent(stu);
return Response.ok(stu).build();
}
}
You have to use the extention for Spring DI, see Jersey 2.25.1 User Guide:
Jersey provides an extension to support Spring DI. This enables Jersey to use Spring beans as JAX-RS components (e.g. resources and providers) and also allows Spring to inject into Jersey managed components.
The Spring extension module configuration is based on annotations. Spring beans are injected and JAX-RS classes are made Spring managed using annotations. Injected Spring beans can have further dependencies injected using Spring XML configuration. Spring singleton and request scopes are supported.
To enable JAX-RS resources to work Spring functionality that requires proxying, such as Spring transaction management (with #Transactional), Spring Security and aspect oriented programming (such as #Aspect), the resources must themselves be managed by Spring, by annotating with #Component, #Service, #Controller or #Repository:
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import org.springframework.stereotype.Component;
#Component
#Path("/")
public class SomeResource {
#Transactional
#GET
public void updateResource() {
// ...
}
}
Limitations:
Spring beans can't be injected directly into JAX-RS classes by using Spring XML configuration
25.1. Dependencies
If you want to use Jersey Spring DI support you will need to add the jersey-spring3 module into the list of your dependencies:
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-spring3</artifactId>
<version>2.25.1</version>
</dependency>
The above module adds transitive dependencies on Spring modules. See jersey-spring3 module dependencies for more details about list and scope of dependencies. Please note the module depends on The Spring/HK2 Bridge that is used to inject Spring services into HK2 services or inject HK2 services into Spring services.

How to keep SecurityContext set through WithSecurityContextFactory in Spring Security tests?

I'm using Spring 4.1.5 and Spring Security 4.0.0.RELEASE.
I read http://spring.io/blog/2014/05/07/preview-spring-security-test-method-security (nice article by Rob Winch) and developed my own implementation of WithSecurityContextFactory to be able to test my Spring MVC controllers:
public class WithMockCustomUserSecurityContextFactory implements WithSecurityContextFactory<WithMockCustomUser> {
#Override
public SecurityContext createSecurityContext(WithMockCustomUser customUser) {
final User fakeUser = new User();
final SecurityUser principal = new SecurityUser(fakeUser);
final Authentication auth = new UsernamePasswordAuthenticationToken(principal, "password", HelpersTest.getAuthorities(customUser.faps()));
final SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(auth);
return context;
}
}
My abstract resource test class is as follow:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration(locations =
{
"classpath:spring/mock-daos-and-scan-for-services.xml",
"classpath:security.xml",
"classpath:singletons.xml",
"classpath:controller-scan.xml",
"classpath:servlet.xml" })
#TestExecutionListeners(listeners=
{
ServletTestExecutionListener.class,
DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class,
WithSecurityContextTestExcecutionListener.class })
public abstract class AbstractResourceMockMvcTest {
#Autowired
private WebApplicationContext wac;
#Autowired
private Filter springSecurityFilterChain;
private MockMvc mockMvc;
[...]
#Before
public void setup() {
this.mockMvc =
MockMvcBuilders.webAppContextSetup(this.getWac())
.addFilters(springSecurityFilterChain)
.build();
}
[...]
}
Then, my concrete test class inherits from AbstractResourceTest (from above) and it uses the following annotation on a #Test-enabled method:
#WithMockCustomUser(faps={"promotion_read"})
Tracing the code, I can confirm WithMockCustomUserSecurityContextFactory.createSecurityContext() is called and its return value is set in SecurityContextHolder.setContext() (through TestSecurityContextHolder.setContext()).
So far, so good !
However, later in the process, SecurityContextPersistenceFilter.doFilter() calls SecurityContextHolder.setContext() and this overwrites the context set by the test and I lose track of the mocked security context I prepared.
security.xml:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.0.xsd
"
>
<!-- HTTP security handling -->
<security:http use-expressions="true">
<security:logout logout-url="/j_spring_security_logout" invalidate-session="true" logout-success-url="/login.jsp?loggedout=true" />
<security:custom-filter before="FIRST" ref="multiTenantRequestFilter" />
<!-- make sure following page are not secured -->
<security:intercept-url pattern="/*/*/internal/**" access="hasIpAddress('127.0.0.1')" />
<!-- make sure everything else going through the security filter is secured -->
<security:intercept-url pattern="/resources/**" access="hasRole('ROLE_USER')" requires-channel="any" />
<!-- supporting basic authentication for unattended connections (web services) -->
<security:http-basic />
</security:http>
<!-- authentication strategy -->
<security:authentication-manager alias="authManager">
<security:authentication-provider user-service-ref="userSecurityService">
<security:password-encoder ref="passwordEncoder" />
</security:authentication-provider>
</security:authentication-manager>
<!-- custom filter to intercept the tenant name from the login form -->
<bean id="multiTenantRequestFilter" class="com.meicpg.ti.web.MultiTenantRequestFilter" />
</beans>
servlet.xml:
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.1.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
"
>
<mvc:annotation-driven>
<!-- Content skipped for StackOverflow question -->
</mvc:annotation-driven>
<context:annotation-config />
<bean id="annotationExceptionResolver" class="org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver"></bean>
<security:global-method-security pre-post-annotations="enabled"/>
<aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>
How can I prevent this security context overwrite ? Does my security.xml contain an obvious flaw I missed ?
PS: I skipped the other context configuration files as they seem irrelevant to the problem.
Thanks in advance !
Unfortunately that blog post is just for method level security and does not have complete instructions for MockMvc setup (the following blog in the series does). Additionally, the blogs are actually dated (I have updated them to reflect that readers should refer to the reference documentation). You can find updated instructions in the Testing Section of the reference.
In short, update your code to the following:
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*;
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration(locations =
{
"classpath:spring/mock-daos-and-scan-for-services.xml",
"classpath:security.xml",
"classpath:singletons.xml",
"classpath:controller-scan.xml",
"classpath:servlet.xml" })
public abstract class AbstractResourceMockMvcTest {
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
[...]
#Before
public void setup() {
this.mockMvc =
MockMvcBuilders.webAppContextSetup(this.getWac())
.apply(springSecurity())
.build();
}
#Test
#WithMockCustomUser(faps={"promotion_read"})
public void myTest() {
...
}
[...]
}
A few highlights:
You no longer need to provide the TestExecutionListeners
Use .apply(springSecurity()) instead of adding the spring security filter chain manually
This works because Spring Security's test support i.e. apply(springSecurity()) will override the SecurityContextRepository used by the springSecurityFilterChain to first try the TestSecurityContextHolder.

Spring-security not working, need help understanding why

I'm trying to understand Spring-security but my pages can be accessed without loggin in and I don't understand why.
The "secure" page is located in WEB-INF/pages/secure and is accessed using http://localhost:8080/secret. This should not allow access, but currently does.
/secure maps here
#Controller
public class HelloWorld {
...
#RequestMapping("/secret")
public String showSecret(ModelMap model) {
return "secure/secretPage";
}
}
web.xml
...
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/applicationContext-security.xml
/WEB-INF/springmvc-config.xml
</param-value>
</context-param>
...
applicationContext-security.xml
<http auto-config="true">
<form-login login-processing-url="/j_spring_security_check"
login-page="/login"
authentication-failure-url="/login?login_error=t"/>
<logout logout-url="/j_spring_security_logout"/>
<intercept-url pattern="/pages/secure/**" access="IS_AUTHENTICATED_FULLY" requires-channel="https"/>
<intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
</http>
<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref="userAccountDetailsService"/>
</authentication-manager>
userAccountDetailsService
#Service("userAccountDetailsService") // enables component to be found to <component-scan/>
public class UserAccountDetailsService implements UserDetailsService {
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
throw new UsernameNotFoundException("Could not find user");
}
}
The /login page does not currently exist. There are no users anyway. I just want this to disallow access for now.
The URL you are hitting is /secure, not /pages/secure and yet in your Spring Security configuration you are protecting /pages/secure/** instead of /secure/**. Change the intercept URL from /pages/secure/** to /secure/** and try again.

Spring Security with JDBC Authentication - No AuthenticationProvider found

I'm new to Spring Security, and I'm trying to set up authentication/authorization against a PostgreSQL database. I followed the first 3 chapters here and got in-memory username/password working without problems. After creating the tables required for the schema (here), then setting up a JNDI DataSource in tomcat (here) along with all the beans needed for Spring, login is now failing with this message:
Your login attempt was not successful, try again.
Reason: No AuthenticationProvider found for
org.springframework.security.authentication.UsernamePasswordAuthenticationToken
Here are the beans defined in my servlet-context.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd">
<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/TyedArtDB"/>
<!-- Enables the Spring MVC #Controller programming model -->
<annotation-driven />
<context:component-scan base-package="com.tyedart.web" />
<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
<resources mapping="/resources/**" location="/resources/" />
<!-- Resolves views selected for rendering by #Controllers to .jsp resources in the /WEB-INF/views directory -->
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
<beans:bean id="userDetailsService" class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
<beans:property name="dataSource" ref="dataSource"/>
</beans:bean>
<beans:bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
<beans:bean id="daoAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<beans:property name="userDetailsService" ref="userDetailsService"/>
<beans:property name="passwordEncoder" ref="passwordEncoder"/>
</beans:bean>
<beans:bean id="authenticationManager" class="org.springframework.security.authentication.ProviderManager">
<beans:property name="providers">
<beans:list>
<beans:ref bean="daoAuthenticationProvider"/>
</beans:list>
</beans:property>
</beans:bean>
</beans:beans>
And here's my SecurityConfig class:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// auth
// .inMemoryAuthentication()
// .withUser("rob").password("22").roles("ADMIN");
InitialContext ctx = new InitialContext();
DataSource dataSource = (DataSource) ctx.lookup("java:/comp/env/jdbc/TyedArtDB");
auth
.jdbcAuthentication()
.dataSource(dataSource)
.withDefaultSchema()
.passwordEncoder(new BCryptPasswordEncoder());
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/manage/**").hasRole("ADMIN")
.and()
.formLogin();
}
}
Any ideas what I'm doing wrong here?
I just configured Security with JDBC. I used the java annotation to configure my app. A couple things that I noted:
.withDefaultSchema()
For me, unexpected. I had a data source configured, however I did not create the default tables. Spring connected to my data source and automatically create the schema. I noticed you created the schema manually. That's what I expected also. The docs seemed to be ambiguous, so I just ran my app without creating the tables. Voila Spring created the database tables for me. Perhaps the schema does not match (see my next section, I founds the docs slightly out of date).
Next, I was adding Spring Security to a small Spring MVC app. The Spring blog contained the correct configuration annotation:
#EnableWebMvcSecurity
Let me know if you want me to post my security config. It's java not xml. Thus, I don't know if it would help.

Changing Spring Security configuration

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>

Resources