Simple demo for Spring Security with JavaConfig - spring-security

My Maven project has some integration tests which need to satisfy some simple in memory Spring security.
I had this XML based configuration which works fine, but now I would like to have it in a JavaConfig based configuration:
<!-- A REST authentication -->
<http use-expressions="true" pattern="/admin/**">
<intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')" />
<http-basic entry-point-ref="restAuthenticationEntryPoint" />
<logout />
</http>
<!-- A hard coded authentication provider -->
<authentication-manager>
<authentication-provider>
<user-service>
<user name="stephane" password="mypassword" authorities="ROLE_ADMIN" />
</user-service>
</authentication-provider>
</authentication-manager>
I tried the following Java configuration:
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("stephane").password("mypassword").roles("ADMIN");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic();
}
But a GET request is denied access:
Failed tests: testGreetingSucceedsWithCorrectUserCredentials(com.thalasoft.learnintouch.rest.AdminControllerTest): Status expected:<200> but was:<401>
Any clue ?
Kind Regards,
Stephane Eybert

I could fix the security with the following configuration:
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("stephane").password("mypassword").roles("ADMIN");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.httpBasic()
.authenticationEntryPoint(restAuthenticationEntryPoint)
.and()
.authorizeRequests()
.antMatchers("/**").hasRole("ADMIN")
.anyRequest().authenticated();
}
I was missing the .authenticationEntryPoint(restAuthenticationEntryPoint) and the .csrf().disable() configuration.
The tests now pass as expected.

Related

Converting XML security config to Java config

I am at my wits end trying to convert the below XML based config to a POJO based config:
<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.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-4.0.xsd">
<bean id="auth0EntryPoint" class="com.auth0.spring.security.auth0.Auth0AuthenticationEntryPoint" />
<!-- all urls starting with unsecured are -->
<security:http pattern="${auth0.securedRoute}" create-session="stateless" entry-point-ref="auth0EntryPoint" use-expressions="false">
<security:intercept-url pattern="${auth0.securedRoute}" access="ROLE_USER" />
<security:custom-filter ref="auth0Filter" after="SECURITY_CONTEXT_FILTER" ></security:custom-filter>
<security:csrf disabled="true"></security:csrf>
</security:http>
<!-- Otherwise by default everything is secured -->
<security:http auto-config="true" use-expressions="true" pattern="/**" create-session="stateless" entry-point-ref="auth0EntryPoint">
<security:intercept-url pattern="/**" access='permitAll' />
<security:csrf disabled="true"></security:csrf>
</security:http>
<bean id="auth0Filter" class="com.auth0.spring.security.auth0.Auth0AuthenticationFilter">
<property name="entryPoint" ref="auth0EntryPoint"></property>
</bean>
<bean id="auth0AuthenticationProvider" class="com.auth0.spring.security.auth0.Auth0AuthenticationProvider">
<property name="clientSecret" value="${auth0.clientSecret}" ></property>
<property name="clientId" value="${auth0.clientId}" ></property>
<property name="securedRoute" value="${auth0.securedRoute}" ></property>
</bean>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="auth0AuthenticationProvider" />
</security:authentication-manager>
At the moment, I have something like this:
package com.simplymeasured.uam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import com.auth0.spring.security.auth0.Auth0AuthenticationEntryPoint;
import com.auth0.spring.security.auth0.Auth0AuthenticationFilter;
import com.auth0.spring.security.auth0.Auth0AuthenticationProvider;
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic().authenticationEntryPoint(auth0EntryPoint());
http
.csrf().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http
.addFilter(auth0Filter());
http
.authorizeRequests()
.anyRequest()
.authenticated();
System.out.println("CONFIGURE GETTING CALLED");
super.configure(http);
}
public AuthenticationProvider authenticationProvider() {
Auth0AuthenticationProvider provider = new Auth0AuthenticationProvider();
provider.setClientId("<auth0client>");
provider.setClientSecret("<auth0secret>");
provider.setSecuredRoute("/v2/**");
return provider;
}
public Auth0AuthenticationFilter auth0Filter() {
Auth0AuthenticationFilter auth0Filter = new Auth0AuthenticationFilter();
auth0Filter.setEntryPoint(auth0EntryPoint());
return auth0Filter;
}
public Auth0AuthenticationEntryPoint auth0EntryPoint() {
Auth0AuthenticationEntryPoint auth0EntryPoint = new Auth0AuthenticationEntryPoint();
return auth0EntryPoint;
}
}
but it has been through dozens of iterations and I am just not getting anything lined up where it needs to be. At the moment I am dealing with an issue where it is claiming that the filter chain is already a defined bean, so no requests are going through. Any help or hints would be immense, I haven't found a reference book for spring-security-4, so I am just trying to cobble together code samples.
I am convinced that I am fated to only get relevant search results once I post my question: Why isn’t this Spring Security AuthenticationProvider found after being Java configured?

Migrating a Waffle Spring Security XML configuration to Spring Boot

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

UserDetailsService bean missing since Spring Security migration from XML to JavaConfig

I use Spring Security 3.2.0.RELEASE.
I am currently migrating my application's XML-based Spring Security configuration to a JavaConfig-based Spring Security configuration.
In the old security.xml, I configured a JDBC-based UserDetailsService like this:
<authentication-manager alias="authenticationManager">
<authentication-provider>
<password-encoder ref="passwordEncoder" />
<!-- parsed by JdbcUserServiceBeanDefinitionParser -->
<!-- the following creates a JdbcUserDetailsManager -->
<jdbc-user-service data-source-ref="dataSource" />
</authentication-provider>
</authentication-manager>
Among other effects, above XML configuration automatically registered a Spring Bean of type JdbcUserDetailsManager (a subtype of UserDetailsService) that I could inject into other components like so:
#Service
public class MyCustomService {
#Inject
private JdbcUserDetailsManager judm;
//...
}
My JavaConfig-based adaption of above XML looks like this:
#Configuration
// #ImportResource("classpath:to/no/longer/needed/security.xml")
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.jdbcAuthentication()
.passwordEncoder( somePasswordEncoder )
.dataSource( someDataSource );
}
// ...
}
Above JavaConfig configuration does not seem to additionally register a Spring Bean of type JdbcUserDetailsManager. This means that MyCustomService does not get injected with such a bean, and therefore I get a NoSuchBeanDefinitionException:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.security.provisioning.JdbcUserDetailsManager] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#javax.inject.Inject()}
Using the JavaConfig approach, how can I get hold of a bean of type JdbcUserDetailsManager?
have you tried to add
#Bean
public JdbcUserDetailsManager jdbcUserDetailsManager(){
return new JdbcUserDetailsManager();
}
to your SecurityConfig class?

How to put a URL with a regular expression in Spring Java based configuration

In a Spring Security XML configuration file, I have something like
<security:intercept-url pattern="\A/categories/\d+/items/admin\Z" access="ROLE_USER" />
How to handle the regular expression if I convert the above into a Java based configuration?
You have to use regexMatchers method on HttpSecurity:
public class SpringSecurity extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.regexMatchers("\A/categories/\d+/items/admin\").hasRole("USER");
}
}

Spring security 3 http-basic authentication-success-handler

H i'm using spring security
for form-login i have
<http auto-config="true">
<intercept-url pattern="/pages/**" access="ROLE_USER" />
<form-login authentication-success-handler-ref="authenticationSuccessHandler" login-page="/login.html" default-target-url="/pages/index.html"
always-use-default-target="true" authentication-failure-url="/login.html" />
<logout logout-success-url="/login.html" invalidate-session="true" />
<anonymous enabled='false'/>
</http>
here i can set an authentication-success-handler-ref, how can i add one to my basic authentication:
<http pattern="/REST/**" realm="REALM" entry-point-ref="authenticationEntryPoint">
<intercept-url pattern="/**" access="ROLE_USER" />
<http-basic />
<logout logout-url="/REST/logout" success-handler-ref="restLogoutSuccessHandler" />
</http>
i thought abour overriding BasicAuthenticationFilter, but how can i inject my cutom class for <http-basic />
You cannot set an authentication success handler for BASIC authentication. You can, however, extend BasicAuthenticationFilter and override onSuccessfulAuthentication method:
#Component("customBasicAuthFilter")
public class CustomBasicAuthFilter extends BasicAuthenticationFilter {
#Autowired
public CustomBasicAuthFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
}
protected void onSuccessfulAuthentication(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, Authentication authResult) {
// Do what you want here
}
}
Inject it in your security configuration with something like:
<http entry-point-ref="basicEntryPoint">
<custom-filter ref="customBasicAuthFilter" position="BASIC_AUTH_FILTER"/>
</http>
<authentication-manager alias="authenticationManager">
...
</authentication-manager>
Update: Or with Java config instead of XML:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilterAt(customBasicAuthFilter, BasicAuthenticationFilter.class)
.exceptionHandling().authenticationEntryPoint(basicEntryPoint);
}
As a workaround you can use http-basic in conjuction with form-login:
<http auto-config="true">
...
<http-basic />
<form-login authentication-success-handler-ref="authenticationSuccessHandler" ... />
...
</http>
BasicAuthenticationFilter will work.
EDIT.
If you want set up your overriden version of BasicAuthenticationFilter I think you need to:
Add it to filter chain at BASIC_AUTH_FILTER position as explained here
Set up corresponding BasicAuthenticationEntryPoint entry point via entry-point-ref attribute of http tag.
Instead of using an AuthenticationSuccessHandler you can rely on Spring Security's event mechanism and listen to AuthenticationSuccessEvent by using the ApplicationListener interface:
#Component
public class AuthenticationEventListener implements
ApplicationListener<AuthenticationSuccessEvent>
{
#Override
public void onApplicationEvent (AuthenticationSuccessEvent event) {
// do what you want here
// example: persist event to the database
}
}
See also this answer here: https://stackoverflow.com/a/11384001/474034

Resources