Spring Security Configuration is not getting applied - java

I'm working on a Spring project. The functionality is already done. What's missing is the security context.
As my project (maven) is separated in different sub projects (Services, RestControllers, Domain), I want the security configuration to be a separate sub project as well, which I only have to add as dependency to the main app to activate it.
I started with a very basic configuration, which is, by now, the only class in the security sub project:
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("admin")
.password("admin1")
.roles("ADMIN", "USER");
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests().antMatchers("/").permitAll().and().authorizeRequests().anyRequest().authenticated();
}
}
As soon as I add this project as dependency to my main app, the security context is obviously getting activated, as the default spring login dialogue pops up. The thing is, that Spring ignores the configuration which I've defined in the SecurityConfiguration. It even won't let me access '/', or neither it let's me login with the defined user. Checking in debug mode, it never runs through the public void configure(AuthenticationManagerBuilder auth) method.
In a nut shell:
It activates the spring security context, but it does not apply my configuration. Why is that?

In case of Spring MVC project with Java based configuration import SecurityConfiguration to your ApplicationConfiguration
#EnableWebMvc
#Configuration
#ComponentScan({"xx.xxx.xx.*"})
#PropertySource("classpath:xxx.properties")
#Import(value = {SecurityConfiguration.class}) // <= like this
public class ApplicationConfiguration extends WebMvcConfigurerAdapter { ... }
You may also need SecurityInitializer. This class has to be present even though it's empty. This is a good place for certain filters that must be executed before security configuration.
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer { }

Related

Intended way to add a new spring-securty chain Filter in the context of a spring boot library

I'm developing a library that introduces a new authentication filter, that should be used in the spring-security chain
I know I can add the filter via something like:
#EnableResourceServer
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
class AmazingSecurityConfiguration extends WebSecurityConfigurerAdapter {
// filter bean
#Autowired
private MyFilter myFilter;
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
// ... bunch of requirements per path
// ... or ignore path "/bla"
// etc.
.anyRequest().authenticated();
http.addFilterBefore(myFilter.getFilter(),
AbstractPreAuthenticatedProcessingFilter.class);
}
}
However, this wouldn't work alongside a user defined config. Really, I want to be configuring this bean within my library AutoConfigure class, that's triggered by the spring factories.
Looking around online, I see examples where they extend the WebSecurityConfigurerAdapter class themselves, and have the user extend this new config. But I don't know if this blocks a user from doing something else, and it also relies on the user first calling super.configure(http) to have the filter loaded.
What's the correct way here?
I was able to configure my own http configuration by fiddling with the invocation order of the beans within the autoconfiguration
#Configuration
#ConditionalOnClass(WebSecurityConfigurerAdapter.class)
// Two annotations below ensure that this is not the only WebSecurityConfigurerAdapter,
// as it might otherwise disable security for the rest of the application
// The Order ensures that Spring default which uses ConditionalOnMissingBean is still configured before this class
#AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE)
#ConditionalOnBean(WebSecurityConfigurerAdapter.class)
class LibraryFooSecurityConfig {
#RequiredArgsConstructor
#Configuration
#Order(Ordered.HIGHEST_PRECEDENCE)
class WebSecurityConfig extends WebSecurityConfigurerAdapter {
// autowired in from elsewhere in the autoconfig
private final MyFilter filter;
#Override
public void configure(HttpSecurity http) throws Exception {
http
.addFilterBefore(filter,
AbstractPreAuthenticatedProcessingFilter.class);
}
}
}
This allows a user-defined security config to extend WebSecurityConfigurerAdapter and apply their own rules, while still allowing our library to add it's own items after that of the actual spring-boot application

spring: how to transform java security config to yml

I try to translate the following websecurity configuration written in Java to a plain-yml configuration.
#Order(Ordered.HIGHEST_PRECEDENCE)
#Configuration
#EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/register/**").permitAll()
.anyRequest().authenticated().and().csrf().disable()
.httpBasic();
}
#Autowired
public void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
}
This is the security relevant part of my application.yml:
security:
require_ssl: true
basic:
enabled: true
enable_csrf: false
ignored:
- /register/**
When I use the yml-version the endpoints are not accessible without authentication, but also valid users are not permitted to access the site. I think it's because I am using a custom userDetailsService which is not recognised.
How can I define the same behaviour of the registerAuthentication-method part with the yml-version? Is there a security.userDetailsServiceClass property or something similar?
On application properties reference there is nothing related to UserDetailsService, so must be not supported,also it's a advanced condiguration which can be done by normal configuration. Boot provides via properties only simple configuration to get start quickly, like httpBasic, you can combine both, but note your
ignored:
- /register/**
are clashing with Java Config's matchers

How Spring Security add/configure AuthenticationManagerBuilder?

I am working on Spring Security Java-based configuration.
I have created my own MyAuthenticationProvider which I want to register in the ProviderManager (single instance of AuthenticationManager).
I have found that ProviderManager has a list of providers to which I can register my single
MyAuthenticationProvider.
Here is the part of my Configuration:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(MyAuthenticationProvider);
}
}
I found out that AuthenticationManagerBuilder has parentAuthenticationManager, defaultUserDetailsService and many other fields.
My questions are:
Where is this #Autowired annotation adding AuthenticationManagerBuilder auth from?
Is the AuthenticationManagerBuilder already created in the application context?
What would be the default state of AuthenticationManagerBuilder which is being injected? By default state I mean will there be some parentAuthenticationManager, authenticationProviders already registered in the AuthenticationManagerBuilder?
If I am adding auth.authenticationProvider(MyAuthenticationProvider), does this mean that I am adding one more provider in the AuthenticationManagerBuilder?
What does this mean? Taken from Spring Documentation
The name of the configureGlobal method is not important. However, it
is important to only configure AuthenticationManagerBuilder in a class
annotated with either #EnableWebSecurity, #EnableWebMvcSecurity,
#EnableGlobalMethodSecurity, or #EnableGlobalAuthentication. Doing
otherwise has unpredictable results.
Answer for 1:
#EnableWebSecurity is meta-annotated with #EnableGlobalAuthentication
...
#EnableGlobalAuthentication
#Configuration
public #interface EnableWebSecurity {
...
and #EnableGlobalAuthentication imports AuthenticationConfiguration:
...
#Import(AuthenticationConfiguration.class)
#Configuration
public #interface EnableGlobalAuthentication {
}
In AuthenticationConfiguration, you'll see that an AuthenticationManagerBuilder bean is declared:
...
#Bean
public AuthenticationManagerBuilder authenticationManagerBuilder(
ObjectPostProcessor<Object> objectPostProcessor, ApplicationContext context) {
...
}
When you #Autowire an AuthenticationManagerBuilder, this is the one that you will get. You have several methods at your disposal to easily configure in-memory, jdbc, ldap,... authentication.
Answer for 2:
Background:
The Spring Security Java config goes through several stages to seamlessly incorporate your configurations with the ApplicationContext.One place where this comes together is in the getHttp() method in WebSecurityConfigurerAdapter.
For example, this is an excerpt:
AuthenticationManager authenticationManager = authenticationManager();
authenticationBuilder.parentAuthenticationManager(authenticationManager);
To give you an idea of how "not-straightforward" the sequence of configuration is, the authenticationManager variable above will be either:
The authentication manager you added by overriding configure(AuthenticationManagerBuilder auth)
OR: The authentication manager you added in the method that #Autowired the AuthenticationManagerBuilder bean from AuthenticationConfiguration
OR: an AuthenticationManager bean found in the context
By default state I mean will there be some [...] authenticationProviders already registered in the AuthenticationManagerBuilder
If you look at AuthenticationConfiguration, you'll see that by default, the InitializeUserDetailsBeanManagerConfigurer is applied to the AuthenticationManagerBuilder bean. As long as it finds a UserDetailsService bean in the context and no other provider has been added, it will add a DaoAuthenticationProvider. This is why in the Spring Security reference, only providing a #Bean UserDetailsService bean is sufficient.
But once you add an authentication provider as you did, the "default" provider is not registered.
Answer for 3:
Yes. The code of AuthenticationManagerBuilder adds your provider:
public AuthenticationManagerBuilder authenticationProvider(AuthenticationProvider authenticationProvider) {
this.authenticationProviders.add(authenticationProvider);
return this;
}
Answer for 4 is simple:
It means that, once you have one of that annotations, you can name your method as you wish:
#Configuration
#EnableWebSecurity //or #EnableWebMvcSecurity or #EnableGlobalMethodSecurity....
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void myCoolMethodName(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(MyAuthenticationProvider);
}
}
"Doing otherwise has unpredictable results"
If you keep the name but not the annotations, it may not work.

Enabling WebSecurityConfigurer via #Profile does not work

I have, what I think is, a very simple and basic setup for locally running a Spring Boot webapp with some authentication.
I would expect that when I run this application through Spring Boot, that my custom security settings would override the default behavior when I specify the local profile.
mvn -Dspring.profiles.active="local" spring-boot:run
Maybe I'm specifying the profiles.active wrong, but when the app runs, it still spits out a generated password to use, and doesn't seem to allow any access to the /login path without said authentication.
I'm also not seeing the active profiles under /env either, which may be a little telling.
I have a WebSecurityConfigurer overridden like so:
#Configuration
#EnableWebSecurity
#Profile("local")
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().fullyAuthenticated().and().formLogin().permitAll();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("admin").password("admin").roles("ADMIN", "USER")
.and().withUser("user").password("user").roles("USER");
}
}
My main #Configuration class is your standard Spring Java-style base config:
#Configuration
#ComponentScan
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
I think I ran into the same issue. I wanted to use Spring profiles to select between none, basic, form, etc. auth. However, if I put the #Profile, #Configuration, and #EnableWebMvcSecurity on the public class WebSecurityConfig extends WebSecurityConfigurerAdapter class, like they show in the examples, basic auth was active at times when I wanted no auth. (This is with #SpringBootApplication on my Application class.
I achieved what I wanted with making beans out of WebSecurityConfigurerAdapter, instead of #Configurations (code snippet is in Groovy):
#Configuration
#EnableWebMvcSecurity
class SecurityConfig {
#Bean
#Profile('no-auth')
WebSecurityConfigurerAdapter noAuth() {
new WebSecurityConfigurerAdapter() {
#Override
void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().permitAll()
}
}
}
#Bean
#Profile('default')
WebSecurityConfigurerAdapter basic() {
new WebSecurityConfigurerAdapter() {
#Override
void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers('/').permitAll()
.anyRequest().authenticated()
.and()
.httpBasic()
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
}
}
}
Second attempt to provide better control of security settings. What's the high level options for controlling security auto configuration:
Switch off security completely and permanently:
remove Spring Security from the classpath
or exlude security auto config - #EnableAutoConfiguration(exclude = SecurityAutoConfiguration.class)
Switch off default basic auth security by setting security.basic.enabled=false
It is pretty easy to control different security settings if you have a total control of how security settings, security auto configuration and spring profiles are used.
#Configuration
#ComponentScan
public class Application {
public static void main(String[] args) throws Throwable {
SpringApplication.run(Application.class, args);
}
}
#Configuration
public class WebSecurityConfig {
#Configuration
#EnableAutoConfiguration(exclude = SecurityAutoConfiguration.class)
#ConditionalOnExpression("!${my.security.enabled:false}")
protected static class DefaultWebSecurityConfig {
}
#Configuration
#EnableAutoConfiguration
#EnableWebMvcSecurity
#Profile("local")
#ConditionalOnExpression("${my.security.enabled:false}")
protected static class LocalWebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/home").permitAll()
.anyRequest().authenticated();
http
.formLogin().loginPage("/login").permitAll().and()
.logout().permitAll();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
}
}
In above classes I basically removed #EnableAutoConfiguration from Application class order to use it conditionally. Created two config classes, DefaultWebSecurityConfig and LocalWebSecurityConfig which are chosen by my.security.enabled flag using a Boot #ConditionalOnExpression.
First config simply excludes SecurityAutoConfiguration if my security is not enabled. Second one enabled security and uses local profile. By creating yet another config with a different profile you can control what happens with different profiles. Then you could choose if security is enabled and which profile is used:
#java -jar build/libs/gs-securing-web-0.1.0.jar
#java -jar build/libs/gs-securing-web-0.1.0.jar --spring.profiles.active=local --my.security.enabled=true
If you have an option to use application.yml, different settings could be automatically applied per profile still defining a default values. This would be good if you just want to disable default basic authentication enabled by default security auto config.
security:
basic:
enabled: false
---
spring:
profiles: local
security:
basic:
enabled: true
---
There are probably a million different ways to do these and it's always case by case which works best for current use case.
maven will spawn a new process to run a boot app and it doesn't inherit your -Dspring.profiles.active="local" which you passed to mvn command itself.
Why don't you just build the boot fat jar and then run it manually as an executable jar and then you can control what command line parameters you pass to your program.
Other than that, Spring Boot reference doc mentions in a security chapter:
If Spring Security is on the classpath then web applications will be secure by default with “basic” authentication on all HTTP endpoints.
So I just tried this with Securing a Web Application Guide and if I added what you wrote in your question, app defaults to basic authentication when using a profile which is not active.
#EnableAutoConfiguration allows you to define excludes for autoconfiguration classes, but you need to find a way to disable this together with a profile. So possibly wrapping #EnableAutoConfiguration in two different #Configuration classes enabled by different profiles so that other would exclude security auto-configuration.
What we do (in more sophisticated way) in framework itself is a usage of #Conditional which provides better way to enable/disable parts of auto-configuration.

How to disable spring-security login screen?

I'm using spring-boot-starter-security dependency, to make use of several classes that come with spring-security. But as I want to integrate it in an existing vaadin application, I only want to make use of the classes, and not of the default login/auth screen of spring.
How can I disable this screen?
I cannot make any configurations by extending WebSecurityConfigurerAdapter as my main entry class already extends SpringBootServletInitializer. Also, vaadin applications basically run on the same URL path all the time and use internal navigation.
#EnableAutoConfiguration
public class MyApp extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(MyApp.class);
}
public static void main(String[] args) {
SpringApplication.run(MyApp.class, args);
}
}
So, what could I do to disable the login screen, but though make use of spring security features?
you can use java based configuration like this :
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity security) throws Exception
{
security.httpBasic().disable();
}
}
and restart your application if it's refresh automatically.
The default security in Spring Boot is Basic. You could disable it by setting security.basic.enabled=false. More about this here and here.
Disable the default spring security by excluding it from the autoconfiguration. Add SecurityAutoConfiguration.class to the exclude property of the #SpringBootApplication annotation on your main class. Like follows:
#SpringBootApplication(exclude = { SecurityAutoConfiguration.class })
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
On the main spring-boot application class (the class which has #SpringBootApplication annotation)
#SpringBootApplication(exclude={SecurityAutoConfiguration.class})
There seems to be a simpler solution.
Simply put this annotationabove your main class or the same place as your SpingBootApplication annotation
#EnableAutoConfiguration(exclude = {org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class})
To completely disable the login route use Spring Security configuration object
The following snippet uses org.springframework.boot:2.1.6.RELEASE
#Configuration
#EnableWebSecurity
class SecurityConfig : WebSecurityConfigurerAdapter() {
override fun configure(security: HttpSecurity) {
super.configure(security)
security.httpBasic().disable()
security.cors().and().csrf().disable().authorizeRequests()
.anyRequest().authenticated()
.and().formLogin().disable() // <-- this will disable the login route
.addFilter(JWTAuthorizationFilter(authenticationManager()))
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
}
#Bean
fun corsConfigurationSource(): CorsConfigurationSource {
val source = UrlBasedCorsConfigurationSource()
val config = CorsConfiguration().applyPermitDefaultValues()
config.addExposedHeader("Authorization")
source.registerCorsConfiguration("/**", config)
return source
}
}
This worked for me
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity security) throws Exception
{
//security.httpBasic().disable(); // Did work only for GET
security.csrf().disable().authorizeRequests().anyRequest().permitAll(); // Works for GET, POST, PUT, DELETE
}
}
You can use this code in new versions of spring boot (3.0.0-m4) and reactive model ( webflux )
#Configuration
#EnableWebFluxSecurity
public class SecurityConfig {
#Bean
public SecurityWebFilterChain filterChain(ServerHttpSecurity http) {
return http
.httpBasic().disable()
.build();
}
}
This is to help anyone else struggling to remove the default Spring Boot login screen and have some secured paths. This worked for me with Spring Boot 2.3.4 and the spring-boot-security starter and this article: https://www.toptal.com/spring/spring-security-tutorial helped me. This config allows a GET to /api/config-props and /actuator/health but requires auth on any other actuator path or any other api path. Then finally allows a GET for any other bit that might be served static content in /resources or /public etc.
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity security) throws Exception {
// Enable CORS and disable CSRF
security = security.cors().and().csrf().disable();
// Set session management to stateless
security = security
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and();
// Set permissions on endpoints
security.authorizeRequests()
// Our public endpoints, secured endpoints and then open everything else that is static resource stuff
.antMatchers(HttpMethod.GET, "/api/config-props").permitAll()
.antMatchers(HttpMethod.GET, "/actuator/health").permitAll()
.antMatchers("/actuator**").authenticated()
.antMatchers("/api/**").authenticated()
.antMatchers(HttpMethod.GET, "/**").permitAll();
}
}
Please note that the use of WebSecurityConfigurerAdapter has been deprecated in the recent Spring versions instead you should be using the SecurityFilterChain as per Spring Documentation
https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter
sharing the below code for the same
#Configuration
public class SecurityConfiguration {
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authz) -> authz
.anyRequest().authenticated()
)
.httpBasic(withDefaults());
return http.build();
}
#Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return (web) -> web.ignoring().requestMatchers("/*");
}
}
In the RequestMatchers I have allowed all endpoints without Spring Security whereas you can specify the only endpoints which needs to be exposed without Spring Security
If someone still needs the solution, put a method in the REST controller like this:
#RestController
public class myRestController{
#GetMapping("/login")
public String redirectTo(){
return "yourRedirectLink";
}
}
This solution is very good to work with spring and react packed in a jar
Just remove/comment out below Dependencies from your Project's POM.xml files:
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-security</artifactId>-->
<!-- </dependency>-->
and
<!-- <dependency>-->
<!-- <groupId>org.springframework.security</groupId>-->
<!-- <artifactId>spring-security-test</artifactId>-->
<!-- <scope>test</scope>-->
<!-- </dependency>-->
This code perfectly worked for me, just add it to the main Class of your project
#SpringBootApplication(exclude = { SecurityAutoConfiguration.class })
For me .httpBasic().disable() was not working, in browser, if unauthorised it still showed login form.
What helped for me (webflux):
security.exceptionHandling().authenticationEntryPoint { exchange, ex ->
exchange.response.statusCode = HttpStatus.UNAUTHORIZED
exchange.response.setComplete()
}
Behind the scene, WWW-Authenticate http header causes it, and Spring adds it in HttpBasicServerAuthenticationEntryPoint class.

Categories