Basic security not kicking in with Spring Boot - spring

I'm trying to setup a vanilla Spring Boot environment with Basic authentication.
Basically the only thing I want to customize are the users, the protected paths and a custom password encoder.
The Spring Boot documentation states:
To override the access rules without changing any other autoconfigured
features add a #Bean of type WebConfigurerAdapter with
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER).
Note: I think WebConfigurerAdapter should be WebSecurityConfigurerAdapter.
So I tried the following:
protected static class ApplicationSecurity extends WebSecurityConfigurerAdapter {
#Autowired
private PasswordEncoder passwordEncoder;
#Override
protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
.authorizeRequests()
.antMatchers("/assets/**").permitAll()
.antMatchers("/api/**").hasRole("USER")
.antMatchers("/management/**").hasRole("ADMIN");
// #formatter:on
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// #formatter:off
auth
.inMemoryAuthentication()
.passwordEncoder(passwordEncoder)
.withUser("admin")
.password(passwordEncoder.encode("pwd"))
.roles("USER", "ADMIN")
.and()
.withUser("user")
.password(passwordEncoder.encode("pwd"))
.roles("USER");
// #formatter:on
}
}
The default Boot security seem exactly what I want:
security.enable-csrf=false
security.basic.enabled=true
security.sessions=stateless
However when I run the app the Basic Authentication does not work.
When I configure it explicitly in my WebSecurityConfigurerAdapter using http.httpBasic() like:
#Override
protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
.httpBasic()
.and()
.authorizeRequests()
.antMatchers("/assets/**").permitAll()
.antMatchers("/api/**").hasRole("USER")
.antMatchers("/management/**").hasRole("ADMIN");
// #formatter:on
}
Then the Basic Authentication is working.
So the initial setup above does not seem to take the default configuration.
Am I missing something?

Every WebSecurityConfigurer has its own filter chain with its own request matchers and security rules. Adding a WebSecurityConfigurer (sorry for the typo in the docs) doesn't change the default boot autoconfig filter chain but it doesn't do anything magic for its own filter chain either. You need to tell Spring Security how to secure those resources - you gave it access rules but no authentication strategy. Makes sense?

Related

Oauth2 security configuration antmatchers request filtering not working as expected

I am working on a simple spring boot project along with spring security oauth2 to use google authentication for a specified endpoint which is /google/login.
With following security configurations everything is working perfectly.
#Configuration
public class SecurityConfigure extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http
.requestMatchers().antMatchers("/**")
.and()
.authorizeRequests().antMatchers("/ldap/login").permitAll()
.anyRequest().fullyAuthenticated()
.and()
.oauth2Login();
}
}
But I need to specify only /google/login endpoint to authenticate with oauth2. Therefore I specified it like this.
#Configuration
public class SecurityConfigure extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http
.requestMatchers().antMatchers("/google/**")
.and()
.authorizeRequests().antMatchers("/ldap/**").permitAll()
.anyRequest().fullyAuthenticated()
.and()
.oauth2Login();
}
}
with this security configuration http://localhost:8080/google/login endpoint call redirects to another endpoint called http://localhost:8081/oauth2/authorization/google which is I haven't defined.
Please help me to overcome this problem. Thank you.
This configuration works for me. I had to allow all endpoints that were redirecting while Google's authentication process was running. 
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.requestMatchers().antMatchers("/google/**","/oauth2/authorization/google","/login/oauth2/code/google")
.and()
.authorizeRequests().antMatchers("/ldap/**").permitAll()
.anyRequest().fullyAuthenticated()
.and()
.oauth2Login();
}

Configure public access, JWT authentication and HTTP basic authentication depending on paths in Spring Security

A Spring Boot application provides some REST endpoints with different authentication mechanisms. I'm trying to setup the security configuration according to the following requirements:
By default, all endpoints shall be "restricted", that is, if any endpoint is hit for which no specific rule exists, then it must be forbidden.
All endpoints starting with /services/** shall be secured with a JWT token.
All endpoints starting with /api/** shall be secured with HTTP basic authentication.
Any endpoint defined in a RESOURCE_WHITELIST shall be public, that is, they are accessible without any authentication. Even if rules #2 or #3 would apply.
This is what I came up with so far but it does not match the above requirements. Could you help me with this?
#Configuration
#RequiredArgsConstructor
public static class ApiSecurityConfiguration extends WebSecurityConfigurerAdapter {
private static final String[] RESOURCE_WHITELIST = {
"/services/login",
"/services/reset-password",
"/metrics",
"/api/notification"
};
private final JwtRequestFilter jwtRequestFilter;
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("some-username")
.password(passwordEncoder().encode("some-api-password"))
.roles("api-role");
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.cors()
.and()
.csrf().disable()
.formLogin().disable()
// apply JWT authentication
.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.mvcMatchers(RESOURCE_WHITELIST).permitAll()
.mvcMatchers("/services/**").authenticated()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
// apply HTTP Basic Authentication
.and()
.authorizeRequests()
.mvcMatchers("/api/**")
.hasRole(API_USER_ROLE)
.and()
.httpBasic()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}

Spring security - create 2 filter chains with specific matchers

I'm in the process of implementing ADFS support to an existing spring project.
Since we already have our own JWT authentication, which we want to work in parallel to ADFS authentication, I want to implement a new filter chain that will handle only certain API request paths.
By this I mean I want to create:
ADFS filter chain that will handle all the /adfs/saml/** API calls
Leave the default filter chain that will handle all the rest API calls
I'm using the ADFS spring security lib that defines the filter chain like this:
public abstract class SAMLWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
//some code
protected final HttpSecurity samlizedConfig(final HttpSecurity http) throws Exception {
http.httpBasic().authenticationEntryPoint(samlEntryPoint())
.and()
.csrf().ignoringAntMatchers("/saml/**")
.and()
.authorizeRequests().antMatchers("/saml/**").permitAll()
.and()
.addFilterBefore(metadataGeneratorFilter(), ChannelProcessingFilter.class)
.addFilterAfter(filterChainProxy(), BasicAuthenticationFilter.class);
// store CSRF token in cookie
if (samlConfigBean().getStoreCsrfTokenInCookie()) {
http.csrf()
.csrfTokenRepository(csrfTokenRepository())
.and()
.addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class);
}
return http;
}
}
And I extend this class:
#EnableWebSecurity
#Configuration
#Order(15)
#RequiredArgsConstructor
public class ADFSSecurityConfiguration extends SAMLWebSecurityConfigurerAdapter {
#Override
protected void configure(final HttpSecurity http) throws Exception {
samlizedConfig(http)
.authorizeRequests()
.antMatchers("/adfs")
.authenticated();
}
}
But when debugging I see that this new filter chain is set to match "any" request.
So I'm probably setting the matchers wrong.
Actually, after reading the official docs the answer was a simple one:
(see "Creating and Customizing Filter Chains" section)
#Override
protected void configure(final HttpSecurity http) throws Exception {
samlizedConfig(http)
.antMatcher("/adfs/**");
}
It should not be put after .authorizeRequests() but strait on the first matcher.

Custom Authentication Entrypoint not being called on failed Authentication

I have setup an OAUTH Authorization server that's supposed to allow clients request for tokens. It's also supposed to allow admin users carry out other operations.
In my Web Security Configuration:
#Configuration
#EnableWebSecurity
public class ApiSecurityConfig extends WebSecurityConfigurerAdapter {
private #Autowired CustomAuthenticationProvider authenticationProvider;
private #Autowired CustomAuthenticationEntryPoint entryPoint;
#Override
#Bean
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().httpBasic().and().cors().and()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/oauth/token").permitAll()
.anyRequest()
.authenticated()
.and()
.exceptionHandling()
.authenticationEntryPoint(entryPoint)
.defaultAuthenticationEntryPointFor(entryPoint, new AntPathRequestMatcher("/api/v1/**"));
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
}
Ideally, when an admin user tries to call any endpoint under "/api/v1/**", they should be authenticated - and in fact, they are.
The issue now is, when authentication fails, the authentication entry endpoint is ignored. I don't understand why this is.
I even included the "default authentication entry point for" just to see if that would help, but it didn't.
Please, how do I resolve this?
After playing around with the http security configuration, I took inspiration from this article (https://www.baeldung.com/spring-security-basic-authentication) and changed it to:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().cors().and()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/oauth/token").permitAll()
.anyRequest().authenticated()
.and()
.httpBasic().authenticationEntryPoint(entryPoint);
}
Honestly, I don't know why what I had before wasn't working. Plenty of people have posted that as the solution to problems about entry end points. But I guess maybe something has changed in Spring that I'm not aware of.

Spring Boot Management security works differently with port set

I'm trying to configure a Spring Boot application (1.2.3, but this also fails with the 1.2.4.BUILD-SNAPSHOT version) with Actuator support. I want to use the Actuator security config for controlling access to the management endpoints, and our own authentication for the rest of the application.
Here is my security config:
#Configuration
#EnableWebSecurity
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter
{
#Autowired
private CustomAuthenticationProvider customAuthProvider;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception
{
auth.authenticationProvider(customAuthProvider);
}
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
.authorizeRequests()
.regexMatchers(API_DOC_REGEX).permitAll()
.regexMatchers(String.format(PATH_REGEX, PUBLIC_ACCESS)).permitAll()
.regexMatchers(String.format(PATH_REGEX, INTERNAL_ACCESS)).access("isAuthenticated() && authentication.hasOrigin('INTERNAL')")
.regexMatchers(String.format(PATH_REGEX, EXTERNAL_AUTHENTICATED_ACCESS)).authenticated()
.antMatchers("/**").denyAll()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER)
.and()
.addFilterAfter(customAuthProcessingFilter(), BasicAuthenticationFilter.class)
.csrf().disable();
}
}
This works correctly when I don't set a management port, but when I set the management port, the management URLs return 401 responses. If I comment out the line .antMatchers("/**").denyAll(), then everything goes through without requiring authentication at all. So it looks like it is using my application's security config for the Actuator endpoints when I set a custom port, but I'm not sure why.
How do I get it to use it's own security when running on a custom port?
Expanding on the comment from #M. Deinum, adding another adapter for the Management stuff (even though it already has one) seems to have fixed it. This is the class I ended up with:
#Order(0)
#Configuration
public class ManagementSecurityConfig extends WebSecurityConfigurerAdapter
{
#Autowired
ManagementServerProperties managementProperties;
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
.requestMatchers()
.requestMatchers(new RequestMatcher()
{
#Override
public boolean matches(HttpServletRequest request)
{
return managementProperties.getContextPath().equals(request.getContextPath());
}
})
.and()
.authorizeRequests()
.anyRequest().hasRole("ADMIN")
.and()
.httpBasic();
}
}

Resources