Patrick Patrick - 2 months ago 18
Java Question

Spring security - httpbasic not working: What am i doing wrong?

I'm new to spring security. Try to use it for project with a rest backend. For my backend certain urls need to be open, certain urls need to have httpbasic auth / https and certain urls need a token authentication.

I'm trying to set this up using a test with web mvc. Trying to test it by using controller methods:

@RequestMapping(value="/auth/signup", method=RequestMethod.POST)
@ResponseStatus(HttpStatus.OK)
public void test(){
System.err.println("Controller reached!");
}

@RequestMapping(value="/auth/login", method=RequestMethod.POST)
@ResponseStatus(HttpStatus.OK)
public void test2(){
System.err.println("Controller reached!");
}


My Spring Security Config locks like the following:

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}

@Configuration
@Order(1)
public static class FreeEndpointsConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/auth/signup").permitAll()
.and().csrf().disable();
}
}

@Configuration
@Order(2)
public static class HttpBasicAuthConfig extends WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/auth/login").hasAnyRole("USER")
.and().httpBasic()
.and().csrf().disable();
}
}
}


My Test looks like this:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes={RootContext.class, WebSecurityConfig.class})
@WebAppConfiguration
public class AccountSecurityTest {

@Autowired
private WebApplicationContext wac;

private MockMvc securityMockMvc;

@Before
public void SetupContext() {
securityMockMvc = MockMvcBuilders
.webAppContextSetup(wac)
.apply(springSecurity()).build();
}

@Test
public void testSigInFree() throws Exception {
MockHttpServletRequestBuilder post = post("/auth/signup");
securityMockMvc.perform(post).andExpect(status().isOk());
}

@Test
public void testLoginHttpBasic() throws Exception {
MockHttpServletRequestBuilder post = post("/auth/login");
securityMockMvc.perform(post).andExpect(status().isOk());
}
}


The testmethod "testLoginHttpBasic" is green. But I would expect a failure, because i'm trying to configure / enforce httpbasic authentication. Where is my mistake?

Answer

Change

http.authorizeRequests().antMatchers("/auth/signup").permitAll() to http.antMatcher("/auth/signup").authorizeRequests().anyRequest().permitAll()

and

http.antMatcher("/auth/login").authorizeRequests().anyRequest().hasAnyRole("USER") to http.authorizeRequests().antMatchers("/auth/login").hasAnyRole("USER").

Your second test will fail.

Why do you need this change?

http.authorizeRequests()... creates a SecurityFilterChain that matches every URL. As soon as one SecurityFilterChain matches the request all subsequent SecurityFilterChains will never be evaluated. Hence, your FreeEndpointsConfig consumed every request.

With http.antMatcher("...") in place you restrict every SecurityFilterChain to a particular URL (pattern). Now FreeEndpointsConfig matches only /auth/signup and HttpBasicAuthConfig /auth/login.

Small improvement

You can make several URLs like paths to static resources (js, html or css) public available with WebSecurity::configure. Override WebSecurity::configure in your WebSecurityConfig

@Override
public void configure(WebSecurity webSecurity) throws Exception {
    webSecurity
            .ignoring()
            .antMatchers("/auth/signup");
}

and FreeEndpointsConfig isn't required anymore.