#ManagementContextConfiguration usage with WebSecurityConfigurerAdapter doesn't work - spring-security

I have a working Spring Boot 1.x application, configured with different management port and security (Basic Auth).
After migration to Spring 2.1 it is not working anymore.
See the code:
#ManagementContextConfiguration
public class ManagementConfig {
#EnableWebSecurity
#Order(SecurityProperties.BASIC_AUTH_ORDER)
static class SecurityConfig extends WebSecurityConfigurerAdapter {
public SecurityConfig() {
super(true); // disable defaults
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
auth.inMemoryAuthentication()
.withUser("admin")
.password(encoder.encode("admin"))
.roles(Collections.emptyList().toArray(new String[]{}));
}
#Override
protected void configure(HttpSecurity http) throws Exception {
RequestMatcher matcher = new AntPathRequestMatcher("/**");
http
.authorizeRequests()
.requestMatchers(matcher).authenticated();
}
}
}
My spring.factories contains
org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration=\
com.test.config.ManagementConfig
During begugging, I can see that SecurityConfig instance created, but no configure() method called (as I see, there was a post-processor call in Spring Boot 1.x after ManagementConfig created...).
My application.yml contains:
management.server:
address: localhost
port: 18543
security:
enabled: true
The problem with it, that I can access any management endpoint now with simple
curl http://localhost:18543/info

I've solved this issue with separating
#ManagementContextConfiguration
and
#EnableWebSecurity
into 2 different file.

Related

spring-boot, spring-security and dropwizard metrics

I have a spring-boot application with spring-security and dropwizard metrics. It uses Angularjs as a frontend. Authentication is done using separate login.html page with angularjs controller posting credentials to '/login' and after seccessful response routing to index.html (separate angularjs app). This all works quite well until I try to access dropwizard metrics. In this case I get a spring-security exception saying that user is anonymous (all other urls work fine).
My spring-security config:
#Configuration
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
#EnableWebSecurity
public class FormLoginSecurityConfigurer extends WebSecurityConfigurerAdapter {
private class AuthSuccessHandler implements AuthenticationSuccessHandler {
#Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication)
throws IOException, ServletException {
response.setStatus(HttpServletResponse.SC_OK);
}
}
private class AuthFailureHandler implements AuthenticationFailureHandler {
#Override
public void onAuthenticationFailure(HttpServletRequest request,
HttpServletResponse response, AuthenticationException exception)
throws IOException, ServletException {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
}
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login.html", "/scripts/login/**", "/libs/**", "/styles/**", "/images/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/login.html").loginProcessingUrl("/login")
.usernameParameter("username").passwordParameter("password")
.successHandler(new AuthSuccessHandler())
.failureHandler(new AuthFailureHandler())
.and().logout().logoutUrl("/logout").logoutSuccessUrl("/login.html")
.and().addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class)
.csrf().csrfTokenRepository(CsrfHeaderFilter.csrfTokenRepository());
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
}
}
The metrics servlet is registered in the ServletContextInitilizer:
/**
* Configuration of web application with Servlet 3.0 APIs.
*/
#Configuration
public class WebConfigurer implements ServletContextInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
initMetrics(servletContext,
EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.ASYNC));
}
/**
* Initializes Metrics.
*/
private void initMetrics(ServletContext servletContext, EnumSet<DispatcherType> disps) {
log.debug("Initializing Metrics registries");
servletContext.setAttribute(InstrumentedFilter.REGISTRY_ATTRIBUTE,
metricRegistry);
servletContext.setAttribute(MetricsServlet.METRICS_REGISTRY,
metricRegistry);
log.debug("Registering Metrics Filter");
FilterRegistration.Dynamic metricsFilter = servletContext.addFilter("webappMetricsFilter",
new InstrumentedFilter());
metricsFilter.addMappingForUrlPatterns(disps, true, "/*");
metricsFilter.setAsyncSupported(true);
log.debug("Registering Metrics Servlet");
ServletRegistration.Dynamic metricsAdminServlet =
servletContext.addServlet("metricsServlet", new MetricsServlet());
metricsAdminServlet.addMapping("/metrics/metrics/*");
metricsAdminServlet.setAsyncSupported(true);
metricsAdminServlet.setLoadOnStartup(2);
}
}
However when I access anything under /metrics/metrics the browser prompts for basic authentication. The response has the following header WWW-Authenticate:"Basic realm="Spring"". Other resources are downloaded fine.
I'm new to this kind of applications and getting a bit frustrated :) Any help is appreciated.
Seems its all in the docs if one knows what to look for - link
The Actuator security features can be modified using external properties (management.security.*). To override the application access rules add a #Bean of type WebSecurityConfigurerAdapter and use #Order(SecurityProperties.ACCESS_OVERRIDE_ORDER) if you don’t want to override the actuator access rules, or #Order(ManagementServerProperties.ACCESS_OVERRIDE_ORDER) if you do want to override the actuator access rules.
Changed the order to ManagementServerProperties.ACCESS_OVERRIDE_ORDER and now it works.

Spring security not calling my custom authentication filter when running JUnit tests

I'm trying to implement custom stateless authentication with Spring Security by following this article
The problem I'm facing is that my custom filter is not being called by the framework, even when my SecurityConfig looks almost the same as in the previous link (a bit simpler):
#Configuration
#EnableWebMvcSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
#Qualifier("appAuthenticationProvider")
private AuthenticationProvider authenticationProvider;
#Autowired
#Qualifier("appAuthenticationFilter")
private AppAuthenticationFilter appAuthenticationFilter;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable().
sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests().anyRequest().authenticated()
.and()
.anonymous().disable()
.exceptionHandling().authenticationEntryPoint(unauthorizedEntryPoint());
http.addFilterBefore(appAuthenticationFilter, BasicAuthenticationFilter.class);
}
#Bean
public AuthenticationEntryPoint unauthorizedEntryPoint() {
return (request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
}
I don't post the code for authenticationProvider and appAuthenticationFilter as the former is working fine (I can log in using /login endpoint) and the latter just implements GenericFilterBean and is not being even called.
Any help would be much appreciated!
Ok, I found the solution after I noticed that the filters were being executed when deploying the Spring Boot app, and they were not being called only when running tests. Then I found this post:
https://spring.io/blog/2014/05/23/preview-spring-security-test-web-security
I forgot to configure my mock MVC to use filters. So finally my test class for the authentication looks as follows:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = GasApplication.class)
#WebAppConfiguration
public class LoginControllerTest {
#Autowired
private WebApplicationContext context;
#Autowired
#Qualifier("appAuthenticationFilter")
private Filter appAuthenticationFilter;
private MockMvc mockMvc;
#Before
public void init() throws Exception {
this.mockMvc = MockMvcBuilders.webAppContextSetup(context)
.addFilter(appAuthenticationFilter, "/resource")
.build();
}
// Tests here...
}
To not autowire and set up your filter by hands as in the previous answer, you can use SecurityMockMvcConfigurers.springSecurity():
MockMvcBuilders
.webAppContextSetup(context)
.apply(SecurityMockMvcConfigurers.springSecurity())
.build();

Using multiple WebSecurityConfigurerAdapter in spring boot

I'm having 2 classes which extends WebSecurityConfigurerAdapter. And can't make them work together.
The idea is as follows:
Have one WebSecurityConfigurerAdapter which only adds custom filter to security chain. The filter does some custom authentication and saves Authentication into SecurityContext. This generally works fine. Configured as follows (imports omitted):
#Order(1)
#Configuration
#EnableWebMvcSecurity
public class BestSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private BestPreAuthenticationFilter ssoAuthenticationFilter;
#Bean
protected FilterRegistrationBean getSSOAuthenticationFilter() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(ssoAuthenticationFilter);
// Avoid include to the default chain
filterRegistrationBean.setEnabled(false);
return filterRegistrationBean;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilterAfter(ssoAuthenticationFilter, SecurityContextPersistenceFilter.class);
}
#Configuration
protected static class AuthenticationConfiguration extends
GlobalAuthenticationConfigurerAdapter {
#Autowired
private BestAuthenticationProvider authenticationProvider;
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
}
}
I want the above to be kind of library class which anyone can include via #ComponentScan and get the custom authentication sorted. Obviously they want to provide custom HttpSecurity to secure edpoints. Trying something like:
#Configuration
#EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/testUrl").hasRole("NON_EXISTING")
.anyRequest().authenticated();
}
}
Obviously the test URL should not be accessible as my user is not member of role NON_EXISTING. Unfortunatelly she is.
If I move the security authorizeRequests() part to the configuration class form 1. next to adding the security filter then it blocks the access as expected. But in my case it looks like the second configuration is ignored.
I also debugged the configure() methods and noticed that HttpSecurity is not the same object which smells a bit.
Any tips how can I make this work much appreciated.
Sum up of the goal:
have one WebSecurityConfigurerAdapter which adds the filter and is hidden from the user of the library
let the user define her own custom endpoint security
Spring boot 1.1.6-RELEASE
Define a special interface
public interface ServiceWebSecurityConfigurer {
void configure(HttpSecurity http) throws Exception;
}
Then have just one ConfigurerAdapter:
public class MyConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Autowired(required = false)
ServiceWebSecurityConfigurer serviceSecConfig;
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests(). // whatever
if (serviceSecConfig != null) serviceSecConfig.configure(http);
http.authorizeRequests(). // whatever
}
}
and then just implement ServiceWebSecurityConfigurer elsewhere when needed. There can be multiple implementations as well, just autowire them as list and iterate and use them all in your main configuration.
So one option I just found is:
Remove the #Configuration annotation from the first bean
And change the 2. to:
#Configuration
#EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class SecurityConfig extends BestSecurityConfig { //Note the changed extend !
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http); // Merge of the 2 HTTP configurations
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/testUrl").hasRole("NON_EXISTING")
.anyRequest().authenticated();
}
}
Any comments on whether this is right or wrong approach much appreciated
Edit: After few years I still didn't find other way but I like this way more and more. Even in the default case you extend the abstract WebSecurityConfigurerAdapter there is no reason why some other layer of abstraction can't provide another abstract extension which provides meaningful defaults.
I founded (in my opinion) a cleaner way of structuring some default configurations and make it simple to integrate in new projects by using Custom DSLs.
I'm using it to config JWT authentication filters, but i think a CORS filter is more simple and didactic:
public class CustomCorsFilterDsl extends AbstractHttpConfigurer<CustomCorsFilterDsl, HttpSecurity> {
#Override
public void init(HttpSecurity http) throws Exception {
//your init code here, no needed in this case
}
#Override
public void configure(HttpSecurity http) throws Exception {
CorsFilter corsFilter = corsFilter(corsProperties);
http.addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class);
}
private CorsFilter corsFilter(CorsProperties corsProperties) {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("http://localhost:9000");
config.addAllowedHeader("*");
config.addAllowedMethod("GET, POST, PUT, PATCH, DELETE");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
public static CustomCorsFilterDsl dsl() {
return new CustomCorsFilterDsl();
}
}
And in your WebSecurityConfig you can use it like this:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.exceptionHandling()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/foo/**").permitAll()
//... your configurations
.antMatchers("/**").authenticated()
.and()
.apply(CustomCorsFilterDsl.dsl());
}
}
And you accomplished your objective of having libraries with default configurations independent of your projects code, in a more clear way, because you can visualize in the project's WebSecurityConfig a custom CORS entry.

spring-security with spring-boot configuration

I have a Spring-boot app that is using Spring-security, configured with Java-config. Ideally, I will have a customer UserDetailsService so I can add/modify users. Until then I am failing to configure this correctly.
I am using the following dependencies:
compile("org.springframework.boot:spring-boot-starter-web:1.1.1.RELEASE")
compile("org.springframework.boot:spring-boot:1.0.1.RELEASE")
compile("org.springframework.boot:spring-boot-starter-thymeleaf")
compile("org.springframework.boot:spring-boot-starter-data-jpa:1.1.1.RELEASE")
compile("org.springframework.security:spring-security-web:4.0.0.M1")
compile("org.springframework.security:spring-security-config:4.0.0.M1")
I have the following Configurations
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
#Configuration
#EnableWebMvcSecurity
public class ApplicationSecurity extends WebSecurityConfigurerAdapter {
#Autowired
private DataSource datasource;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/resources/**").permitAll()
.antMatchers("/css/**").permitAll();
http
.formLogin().failureUrl("/login?error")
.defaultSuccessUrl("/")
.loginPage("/login")
.permitAll()
.and()
.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/")
.permitAll();
http
.authorizeRequests().anyRequest().authenticated();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
JdbcUserDetailsManager userDetailsService = jdbcUserService();
// userDetailsService.setDataSource(datasource);
// PasswordEncoder encoder = new BCryptPasswordEncoder();
auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
auth.jdbcAuthentication().dataSource(datasource);
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public org.springframework.security.provisioning.JdbcUserDetailsManager jdbcUserService() throws Exception {
JdbcUserDetailsManager jdbcUserDetailsManager = new JdbcUserDetailsManager();
jdbcUserDetailsManager.setDataSource(datasource);
jdbcUserDetailsManager.setAuthenticationManager(authenticationManagerBean());
return jdbcUserDetailsManager;
}
}
#Order(Ordered.HIGHEST_PRECEDENCE)
#Configuration
public class AuthenticationSecurity extends GlobalAuthenticationConfigurerAdapter {
#Autowired
private DataSource dataSource;
#Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth
.jdbcAuthentication()
.dataSource( dataSource );
}
}
So, I realize that my configurations are wrong but not really sure how to best fix them. The symptoms are that when I log into my Thymeleaf UI, the session never exires.
I have used various online resources for my spring-security learning & implementation. Unfortunately, I am still not grasping why this is not correct.
You appear to be configuring 3 filter chains (3 WebSecurityConfigurerAdapters) but only one of them configures the HttpSecurity. That's probably not what you intended to do. Maybe consolidate down to WebSecurityConfigurerAdapter and one GlobalAuthenticationConfigurerAdapter and see where it gets you.

In Spring-Security with Java Config, why does httpBasic POST want csrf token?

I am using Spring-Security 3.2.0.RC2 with Java config.
I set up a simple HttpSecurity config that asks for basic auth on /v1/**.
GET requests work but POST requests fail with:
HTTP Status 403 - Invalid CSRF Token 'null' was found on the request parameter '_csrf' or header 'X-CSRF-TOKEN'.
My security config looks like this:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Resource
private MyUserDetailsService userDetailsService;
#Autowired
//public void configureGlobal(AuthenticationManagerBuilder auth)
public void configure(AuthenticationManagerBuilder auth)
throws Exception {
StandardPasswordEncoder encoder = new StandardPasswordEncoder();
auth.userDetailsService(userDetailsService).passwordEncoder(encoder);
}
#Configuration
#Order(1)
public static class RestSecurityConfig
extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/v1/**").authorizeRequests()
.antMatchers("/v1/**").authenticated()
.and().httpBasic();
}
}
}
Any help on this greatly appreciated.
CSRF protection is enabled by default with Java configuration. To disable it:
#Configuration
public class RestSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
...;
}
}
You can also disable the CSRF check only on some requests or methods, using a configuration like the following for the http object:
http
.csrf().requireCsrfProtectionMatcher(new RequestMatcher() {
private Pattern allowedMethods =
Pattern.compile("^(GET|HEAD|TRACE|OPTIONS)$");
private RegexRequestMatcher apiMatcher =
new RegexRequestMatcher("/v[0-9]*/.*", null);
#Override
public boolean matches(HttpServletRequest request) {
// CSRF disabled on allowedMethod
if(allowedMethods.matcher(request.getMethod()).matches())
return false;
// CSRF disabled on api calls
if(apiMatcher.matches(request))
return false;
// CSRF enables for other requests
return true;
}
});
You can see more here:
http://blog.netgloo.com/2014/09/28/spring-boot-enable-the-csrf-check-selectively-only-for-some-requests/

Resources