Reputation: 3520
So, I have this in my WebSecurityConfigurerAdapter
public class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// Use this configuration for endpoints starting with 'api'
.antMatcher("/api/**")
// Do not secure endpoints receiving callbacks
.authorizeRequests().antMatchers(""/api/v1/notification").permitAll()
// Allow only users with role "ROLE_API"
.anyRequest().hasRole(Users.UserRoles.ROLE_API.role.replace("ROLE_", ""))
.and()
.httpBasic()
.and()
// Do not create any sessions
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
// Disable csrf
.csrf().disable();
}
}
which should not secure /api/v1/notification
. If I call that endpoint without Authorization: Basic abcd
in the HTTP
header the request is permitted, but if I add the Authorization: Basic abcd
header, I get a 401
http response code.
NB: Basic abcd
is just random so no such user is in my database
The question is why adding Authorization...
in the http header makes the endpoint secured again?
Upvotes: 5
Views: 3308
Reputation: 7707
Good question, this can be kind of confusing since it means that a legitimate client, just with a bad password, could be denied a page that the rest of the world can see without credentials.
Actually, it's by design. Generally speaking, an authorization system would need to know who the user is before knowing whether the user can do X, Y, or Z operation. And even with a public endpoint, it's possible that the endpoint may behave differently when a user is in context. So, really, they are separate systems with authentication coming first: If a request presents credentials, then the framework will try and authenticate the user and accept or deny the request accordingly.
I realize you didn't ask for how to address it (you may be completely satisfied with the behavior and just curious), but one thing you can do with BasicAuthenticationFilter
is configure it to ignore failures, just for that endpoint:
static class IgnoreFailuresBasicAuthenticationFilter extends BasicAuthenticationFilter {
private final BasicAuthenticationFilter everythingElse;
public IgnoreFailuresBasicAuthenticationFilter(BasicAuthenticationFilter everythingElse) {
super(everythingElse.getAuthenticationManager());
this.everythingElse = everythingElse;
}
protected void doFilterInternal(request, response, chain) {
if ("/api/v1/notification".equals(request.getPathInfo())) {
super.doFilterInternal(request, response, chain);
} else {
this.everythingElse.doFilterInternal(request, response, chain);
}
}
}
And then replace the filter in the DSL:
http
.httpBasic()
.withObjectPostProcessor(
new ObjectPostProcessor<BasicAuthenticationFilter>() {
public BasicAuthenticationFilter postProcess(BasicAuthenticationFilter filter) {
return new IgnoreFailuresBasicAuthenticationFilter(filter);
}
});
What this will do is allow the filter chain to continue, even though Basic authentication failed. The consequence would be that, in the case authentication fails, you'd get a 403 instead of a 401.
Upvotes: 2