Spring boot oauth2 management httpbasic authentication - java

I've got a spring boot application that uses oauth2 for authentication.
The oauth2 mechanism is working and clients can authenticate and receive their access tokens.
I want to secure the actuators endpoints with httpbasic authentication, i.e. not requiring the user to first use oauth2 for authentication and then access the actuator endpoints.
What i've done so far is to set the following in properties file:
management.context-path=/admin/actuators
management.security.enabled=true
management.security.role=ADMIN
security.user.name=admin
security.user.password=password
I've tried various ways to set configuration with a ResourceServerConfigurerAdapter and WebSecurityConfigurerAdapter.
None of my attempts are working and it keeps on telling me
<oauth>
<error_description>
Full authentication is required to access this resource
</error_description>
<error>unauthorized</error>
</oauth>
What is the correct way to get OAUTH2 and the management endpoint to work?

The problem is that #EnableResourceServer imports ResourceServerConfiguration, which has an order of 3, far superior to ManagementServerProperties.ACCESS_OVERRIDE_ORDER.
See Spring Boot documentation on actuator security and ordering config classes : http://docs.spring.io/spring-boot/docs/1.4.3.RELEASE/reference/htmlsingle/#boot-features-security-actuator
The default actuator security config is a lot more clever than just allowing access to the /health endpoint and blocking the rest, it actually changes depending on management.port and management.contextPath, and it can get pretty hard to find the correct management endpoint URLs without leaving gaping holes in your security or messing with your own resources.
If you want to keep the benefit of the autoconfigured management security, two options :
EDIT : a) Lower ResourceServerConfiguration order using a BeanPostProcessor
This improvement has been suggested by #dsyer on the github thread :
#Component
#Slf4j
public class ResourceServerConfigurationPostProcessor implements BeanPostProcessor {
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof ResourceServerConfiguration) {
LOGGER.debug("Lowering order of ResourceServerConfiguration bean : {}", beanName);
ResourceServerConfiguration config = (ResourceServerConfiguration) bean;
config.setOrder(SecurityProperties.ACCESS_OVERRIDE_ORDER);
}
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
I just replaced my code below with this class, and it works perfectly.
EDIT : b) Manually overriding ResourceServerConfiguration order
If you don't like post processors for some reason, you can replace the #EnableResourceServer with another configuration class whose order will come after the default management security :
/**
* Extend the default resource server config class, and downgrade its order
*/
public class ResourceServerLowPrecedenceConfiguration extends ResourceServerConfiguration {
/**
* This is enough to override Spring Boot's default resource security,
* but it does not takes over the management.
*/
#Override
public int getOrder() {
return SecurityProperties.ACCESS_OVERRIDE_ORDER;
}
}
And your own configuration class :
/** #EnableResourceServer is replaced by #Import using the low precedence config */
#Configuration
#Import(ResourceServerLowPrecedenceConfiguration.class)
public class YourOwnOAuth2Config extends ResourceServerConfigurerAdapter {
#Override
public void configure(final HttpSecurity http) throws Exception {
// Secure your resources using OAuth 2.0 here
}
}
EDIT : You can also rewrite your own #EnableResourceServer annotation to shortcut the #Import :
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Import(ResourceServerLowPrecedenceConfiguration.class)
public #interface EnableResourceServer {
}
IMHO this should be the default behavior when spring-security-oauth is on the classpath.
See discussion on GitHub issue :
https://github.com/spring-projects/spring-boot/issues/5072

Ok, got it to work using the following java config.
The endpoint, /admin/actuators/health, is accessible by anyone and all other /admin/actuators/* endpoints are authenticated.
#Configuration
#Order(1)
protected static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/admin/actuators/health").permitAll()
.and()
.antMatcher("/admin/actuators/**")
.authorizeRequests()
.anyRequest()
.hasRole("ADMIN")
.and()
.httpBasic();
}
}

security.oauth2.resource.filter-order = 3 in application.yml will do the trick

With Spring-Security you can have Multiple HttpSecurity configuration.
<http pattern="/actuators/**/*" request-matcher="ant" authentication-manager-ref="basicAuthManager">
<security:intercept-url pattern="/**" access="isAuthenticated()" />
<http-basic />
<http>
<http use-expressions="false">
... your oauth config
</http>
<authentication-manager id="basicAuthManager">
<authentication-provider>
<user-service>
<user name="user1" password="user1Pass" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>
... your oath config stuff
(I prefere xml, but you can do this with java config too)
#See http://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#nsa-http
(But think that you could not do this by plain spring-boot configuration.)

Related

spring security custom inject AuthenticationProvider inside AuthenticationManager

Might be a newbie question. I want to inject CustomAuthenticationProviderInside Spring AuthenticationManager. I found a lot of examples online doing that:
<authentication-manager>
<authentication-provider ref="CustomAuthenticationProvider"/>
</authentication-manager>
How can I do that using Java Config class?
Spring offers one default implementation of AuthenticationManager which is ProviderManager. ProviderManager has a constructor which takes an array of Authentication providers
public ProviderManager(List<AuthenticationProvider> providers) {
this(providers, null);
}
If you want you can just play with it by extending ProviderManager
public class MyAuthenticationManager extends ProviderManager implements AuthenticationManager{
public MyAuthenticationManager(List<AuthenticationProvider> providers) {
super(providers);
providers.forEach(e->System.out.println("Registered providers "+e.getClass().getName()));
}
}
And then i Java security configuration you can add your custom authentication manager.
#Override
protected AuthenticationManager authenticationManager() throws Exception {
return new MyAuthenticationManager(Arrays.asList(new CustomAuthenticationProvider()));
}

SpringMVC: How to filter the client's IP?

I use the spring-boot, and I have a #Controller. For security, I want to only accept 127.0.0.1's request (#RequestMapping). How can I do this?
Option 1.
Since you are using spring-boot, I assume you'll prefer to use Spring's auto configuration classes.Use a WebSecurityConfigurerAdapter and configure your access rules there.
#EnableWebSecurity
#Configuration
public class CustomWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeUrls()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/**").access("hasIpAddress('127.0.0.1/24')")
.anyRequest().authenticated();
}
}
Option 2
If you are using tomcat, you can customize Tomcat’s proxy configuration on your application.properties. Reference here.
server.tomcat.internal-proxies=192\\.168\\.\\d{1,3}\\.\\d{1,3}
you can do this is to use Spring Security's Web Security Expressions. For example:
<http use-expressions="true">
<intercept-url pattern="/admin*"
access="hasIpAddress('127.0.0.1/24')"/>
...
</http>

Spring Boot Actuator - Cannot disable /info endpoint

I tried disabling all actuator endpoints for production environment in application.yml configuration file:
endpoints.enabled: false
It works for all endpoints except for /info.
How can I turn off all endpoints for given environment?
UPDATE:
Project I am working on is also acting as Eureka client.
In documentation for Spring Cloud Netflix in section Status Page and Health Indicator (http://cloud.spring.io/spring-cloud-netflix/spring-cloud-netflix.html) it says that "Eureka instance default to "/info" and "/health" respectively".
Is there any solution to disable those endpoints?
I was able to disable /health endpoint with endpoints.enabled: false, but not the /info endpoint.
Finally I managed to solve my problem. I enabled only /info and /health endpoints in actuator. And to allow access to /info endpoint only to users with role ADMIN I needed to mix actuator management security and spring security configuration.
So my application.yml looks like this:
endpoints.enabled: false
endpoints:
info.enabled: true
health.enabled: true
management.security.role: ADMIN
And spring security configuration like this (where I needed to change order of ManagementSecurityConfig to have higher priority):
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration {
#Configuration
protected static class AuthenticationSecurity extends GlobalAuthenticationConfigurerAdapter {
#Autowired
private AuthenticationProvider authenticationProvider;
public AuthenticationSecurity() {
super();
}
#Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("admin").password("secret").roles("ADMIN");
}
}
#Configuration
#Order(Ordered.HIGHEST_PRECEDENCE + 2)
public static class ManagementSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.requestMatchers()
.antMatchers("/info/**")
.and()
.authorizeRequests()
.anyRequest().hasRole("ADMIN")
.and()
.httpBasic();
}
}
#Configuration
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
// API security configuration
}
}
}
Your sample configuration looks suspicious to me. I guess you meant
endpoints:
enabled: true
In any case, I just tried to add this to a vanilla Spring Boot app (using 1.3.1 and all endpoints were disabled (as expected).

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