Computer scienceBackendSpring BootSpring SecuritySpring Security internals

Advanced Configuration with HttpSecurity

5 minutes read

In a previous topic, you saw that a SecurityFilterChain acts as a key component for handling and processing security-related tasks inside a web application. The HttpSecurity class is essential when building a SecurityFilterChain. This class represents the web-based security configuration of a Spring application. This topic will delve deeper into the HttpSecurity class and its role in configuring a Spring application's security. But before we dive in, let's revisit some important facts about the HttpSecurity class.

HttpSecurity recap

The HttpSecurity class is a builder with a fluent API that helps in configuring the security of a Spring application. It contains many methods that accept a Customizer as a parameter, used to configure the HttpSecurity object. The actual configurations stem from the configurer the customizer receives. The sections below spotlight other customizations you can make to the HttpSecurity object.

Here is the blueprint of a method used to create a security filter chain bean:

@Bean
@Throws(Exception::class) // The @Throws annotation is not mandatory in Kotlin, but it can be used for Java interoperability.
fun filterChain(http: HttpSecurity): SecurityFilterChain {
    return http
        .headers { it.disable() }
        .build()
}

Enforcing web security

  • cors(Customizer corsCustomizer)

  • csrf(Customizer csrfCustomizer)

The cors() method adds a org.springframework.web.filter.CorsFilter to be used. You can enable Cross-Origin Resource Sharing with:

http.cors(withDefaults())

Disabling CORS support is as simple as enabling it:

http.cors { it.disable() }

If you want more control over the CORS settings, you can achieve this by implementing the interface org.springframework.web.cors.CorsConfigurationSource, which provides a org.springframework.web.cors.CorsConfiguration instance based on the provided request. The example below demonstrates the interface implemented by a lambda, providing a new CorsConfiguration object with the specified settings.

http.cors { cors ->
    cors.configurationSource { request ->
        val corsConfiguration = CorsConfiguration()
        corsConfiguration.allowedOrigins = listOf("*")
        corsConfiguration.allowedMethods = listOf("GET", "POST", "PUT", "DELETE")
        corsConfiguration.allowedHeaders = listOf("*")
        corsConfiguration
    }
}

The csrf() method enables Cross-Site Request Forgery protection. It adds a org.springframework.security.web.csrf.CsrfFilter that contains several other components. This is activated by default when you use EnableWebSecurity. Disabling it can be done with:

http.csrf { it.disable() }

The Spring Security documentation page provides comprehensive information on how CSRF protection works.

Handling exceptions

  • exceptionHandling(Customizer exceptionHandlingCustomizer)

This allows you to configure exception handling. It's automatically applied when using EnableWebSecurity. The following customization ensures that users denied access are forwarded to the page /errors/access-denied.

http
    .authorizeHttpRequests { authorizeRequests ->
        authorizeRequests
            .requestMatchers("/**").hasRole("USER")
    }
    .exceptionHandling { exceptionHandling ->
        exceptionHandling
            .accessDeniedPage("/errors/access-denied")
    }

Managing headers

  • headers(Customizer headersCustomizer)

The headers() method adds the Security headers to the response. This action is enabled by default when using EnableWebSecurity. You can opt to disable the headers altogether, enable only some of them, or disable only some of them.

You can disable the headers altogether using the following approach:

http.headers { it.disable() }

This configuration enables only headers regarding cache control and frame options.

http
    .headers { headers ->
        headers
            .defaultsDisabled()
            .cacheControl(withDefaults())
            .frameOptions(withDefaults())
    }

In this configuration, only the frame options headers are disabled.

http
    .headers { headers ->
        headers
            .frameOptions { frameOptions -> frameOptions.disable() }
    }

Configuring logout

  • logout(Customizer logoutCustomizer)

This provides logout support. It gets applied automatically when using EnableWebSecurity. By default, accessing the URL /logout logs the user out by invalidating the HTTP Session, cleaning up any rememberMe() authentication configured, clearing the SecurityContextHolder, and then redirecting to /login?success.

Consider the following customization.

http
    .authorizeHttpRequests { authorizeRequests ->
        authorizeRequests
            .anyRequest().authenticated()
    }
    .formLogin { withDefaults() }
    .logout { logout ->
        logout.deleteCookies("remove")
            .invalidateHttpSession(false)
            .logoutUrl("/custom-logout")
            .logoutSuccessUrl("/logout-success")
    }

In this case, the logout URL is /custom-logout, the cookie named remove will be deleted, the session won't be invalidated, and once successfully completed, it will redirect to /logout-success.

Managing passwords

  • passwordManagement(Customizer passwordManagementCustomizer)

Aids in managing passwords. The example mentioned below demonstrates how to use a custom page for changing the password instead of the default one /change-password.

http
    .passwordManagement { passwordManagement ->
        passwordManagement
            .changePasswordPage("/custom-change-password-page")
    }

Mapping ports

  • portMapper(Customizer portMapperCustomizer)

Allows the mapping of the ports used for HTTPS and HTTP. The following configuration ensures that redirects within Spring Security from HTTP of a port of 9090 will redirect to HTTPS port of 9443 and the HTTP port of 80 to the HTTPS port of 443.

http {
    requiresChannel {
        it.anyRequest().requiresSecure()
    }
    portMapper {
        it.http(9090).mapsTo(9443)
        it.http(8080).mapsTo(443)
    }
}

Http(s)

  • requiresChannel(Customizer requiresChannelCustomizer)

It allows you to configure when channel security is enabled. The following configuration will require HTTPS when the relative URL starts with /admin/ and HTTP when it starts with /public/.

http
    .requiresChannel { requiresChannel ->
        requiresChannel
            .requestMatchers("/admin/**").requiresSecure()
            .requestMatchers("/public/**").requiresInsecure()
    }

If you want all requests to be secured, you can use this format:

http
    .requiresChannel { requiresChannel ->
        requiresChannel
            .anyRequest().requiresSecure()
    }

Managing sessions

  • sessionManagement(Customizer sessionManagementCustomizer)

Allows configuring Session Management. The following configuration enforces that only one instance of a user is authenticated at a time. If a user authenticates with the username user without logging out, and another attempt to authenticate with user is made, the first session will be forcibly terminated and redirected to the /login?expired URL.

http
    .authorizeHttpRequests { authorizeRequests ->
        authorizeRequests
            .anyRequest().authenticated()
    }
    .formLogin { formLogin ->
        formLogin
            .permitAll()
    }
    .sessionManagement { sessionManagement ->
        sessionManagement
            .sessionConcurrency { sessionConcurrency ->
                sessionConcurrency
                    .maximumSessions(1)
                    .expiredUrl("/login?expired")
            }
    }

Conclusion

In this topic, you learned more about the HttpSecurity class and its role in configuring a Spring application's security. You also learned about other methods used to secure the application.

6 learners liked this piece of theory. 0 didn't like it. What about you?
Report a typo