I have in a given project the circumstance that I need to extend or overwrite the default AccessDeniedHandler of spring.security. The reason is not important, but I need this to be able to implement custom behaviour whenever a MissingCsrfTokenException or InvalidCsrfTokenException is thrown.
My question is: How to override the org.springframework.security.web.access.AccessDeniedHandler class properly?
Meanwhile I figured out 3 ways which seem to be promising:
Set my custom version of AccessDeniedHandlerImpl when configuring spring.security as instance of my class
Set my custom accessDeniedHandler when configuring spring.security and get it as a bean
Override AccessDeniedHandlerImpl with #Component named AccessDeniedHandler and set it as #Primary
I am new to spring framework and unsure how to solve this the best way.
From my understanding as a technician, I would prefer setting my custom AccessDeniedHandler through spring.security configuration (independent of the way I configure it there). Because using a component will require any other developer to look up for the annotations (but maybe I am wrong with this).
Implementing it through spring security configuration
// in the spring security configuration class
protected void configure(final HttpSecurity http) throws Exception {
http
.exceptionHandling().accessDeniedHandler(new CustomAccessDeniedHandler());
}
or
// in the spring security configuration class
#Bean
public AccessDeniedHandler accessDeniedHandler(){
return new CustomAccessDeniedHandler();
}
protected void configure(final HttpSecurity http) throws Exception {
http
.exceptionHandling().accessDeniedHandler(accessDeniedHandler());
}
As a component override
// in custom AccessDeniedHandler class
#Component
#Primary
public CustomAccessDeniedHandler implements AccessDeniedHandler {
// custom implementation
}
Which will automatically fetch my custom AccessDeniedHandler instead of the default AccessDeniedHandlerImpl defined in org.springframework.security.
Finally the point for me is to figure out, what is the better way to implement this, through configuration or #Component and #Primary.
In spring boot 1.x it was possible to resolve all actuator endpoints programatically. I have a bean that exposes all actuator endpoints paths
#Component
public class MyProjectActuatorEndpoints {
#Autowired
private MvcEndpoints endpoints;
public String[] getActuatorEndpointPaths() {
return endpoints.getEndpoints().stream()
.map(MvcEndpoint::getPath)
.map(path -> path + "/**")
.toArray(String[]::new);
}
}
Unfortunately in spring boot actuator 2.0.5.RELEASE there is no such class MvcEndpoints. Is there any replacement for this class in the new spring version?
Everything you need is in the org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints bean. This should set you on the right path, if you'll pardon the pun:
#Slf4j
#Component
public class ActuatorLogger {
public ActuatorLogger(#Autowired PathMappedEndpoints pme) {
log.info("Actuator base path: {}", pme.getBasePath());
pme.getAllPaths().forEach(p -> log.info("Path: {}", p));
}
}
org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest is available to help you set spring security rules for actuator endpoints when you need to do it from code. For example, in your WebSecurityConfigurerAdapter implementation, this fragment could be merged in to your existing rules:
http.authorizeRequests()
.requestMatchers(EndpointRequest.to(ShutdownEndpoint.class))
.hasAnyAuthority("ROLE_ADMIN", "ROLE_SUPPORT")
#Autowired
private HealthEndpoint healthEndpoint;
public Health getAlive() {
return healthEndpoint.health();
}
Spring Boot Actuator 2.x exposes /actuator endpoints as configurable environment variables.
Enabling Acutator Endpoints
You can enable these actuator endpoints in your application.properties
management.endpoints.web.exposure.include=info, health
or (with extreme caution) enable them all. Keep in mind that many of these are sensitive in nature.
management.endpoints.web.exposure.include=*
Securing Actuator Endpoints (reference)
The documentation specifies this as a strategy to secure all endpoints. The EndpointRequest itself would be the closest alternative to what you were looking for (MvcEndpoints)
#Configuration
public class ActuatorSecurity extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests()
.anyRequest().hasRole("ENDPOINT_ADMIN")
.and()
.httpBasic();
}
}
You may also set up a particular antmatcher in case you have a different strategy or role that you would like to assign just to these endpoints
httpRequest.authorizeRequests().antMatcher("/actuator/*").hasRole("ENDPOINT_ADMIN")
I want to use Spring Security for JWT authentication. But it comes with default authentication. I am trying to disable it, but the old approach of doing this - disabling it through application.properties - is deprecated in 2.0.
This is what I tried:
#Configuration
public class StackWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().disable();
// http.authorizeRequests().anyRequest().permitAll(); // Also doesn't work.
}
}
How can I simply disable basic security?
UPDATE
It might be nice to know that I am not using web mvc but web flux.
Screenshot:
According to the new updates in Spring 2.0, if Spring Security is on the classpath, Spring Boot will add #EnableWebSecurity.So adding entries to the application.properties ain't gonna work (i.e it is no longer customizable that way). For more information visit the official website Security changes in Spring Boot 2.0
Albeit not sure about your requirement exactly, I could think of one workaround like the following:-
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter{
#Override
protected void configure(HttpSecurity http) throws Exception{
http.authorizeRequests().antMatchers("/").permitAll();
}
}
Hope this helps.
From Spring Boot 2.1 on, if you include spring-boot-actuator, it does not suffice anymore to only exclude SecurityAutoconfiguration, you also need to exclude ManagementWebSecurityAutoConfiguration, like so:
#SpringBootApplication(exclude = { SecurityAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class })
According to the reference documentation, the Security configuration for allowing all requests with WebFlux should look like this:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
#Configuration
public class SecurityConfig {
#Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http.authorizeExchange().anyExchange().permitAll();
return http.build();
}
}
This worked for me:
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().anyRequest().permitAll();
}
}
You can add/modify the following to your Application class:
#SpringBootApplication(exclude = { SecurityAutoConfiguration.class })
public class MyApplication {
}
Adding some fresh answer, I assume all use actuator, if not I'd bet one class exclusion should be sufficient, I managed to disable through properties:
spring:
autoconfigure:
exclude: ${spring.autoconfigure.sac}, ${spring.autoconfigure.mwsas}
sac: org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
mwsas: org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration
I've referenced two auto-config classes through property to keep the length intact (note that IntelliJ Ultimate will cry if you reference it like that as it has no clue what are these placeholder values and if they are actually legit classes, so inline if that annoys you).
Application however does not fail to start as claimed by:
https://www.baeldung.com/spring-boot-security-autoconfiguration
if you just disable SecurityAutoConfiguration
If it did work, you will stop seeing auto generated password and it is a little bit less confusing than the accepted answer, as dev reading the log won't get confused by generated password for basic auth while security allows all.
Why just disabling main auto config class isn't enough is because of this fella:
#Configuration
class ManagementWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.requestMatchers(
EndpointRequest.to(HealthEndpoint.class, InfoEndpoint.class))
.permitAll().anyRequest().authenticated().and().formLogin().and()
.httpBasic();
}
}
There was tons of work made to split actuator and security config which confused us all, now its more straightforward but artifacts like these still exist. Spring devs will correct me if I am wrong :-).
I have leveraged #ConditionalOnProperty to load the following SecurityConfig.java class if I set spring.security.enabled property to false in my application.yml to disable spring security and it works like a charm.
#ConditionalOnProperty(name = "spring.security.enabled", havingValue = "false")
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests().antMatchers("/").permitAll();
}
}
If anyone is struggling with this in a WebFlux based application, or a Spring Cloud Gateway application, the below worked for me:
#EnableWebFluxSecurity
public class InsecurityConfiguration {
// #formatter:off
#Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http
.authorizeExchange()
.anyExchange().permitAll();
return http.build();
}
}
To disable default security for Spring Boot Reactive Web applications, use the following excludes when you have actuator also in the classpath.
#SpringBootApplication(exclude = {ReactiveSecurityAutoConfiguration.class, ReactiveManagementWebSecurityAutoConfiguration.class })
I think what you are looking for is to override the default authentication entry point which is set to BasicAuthenticationEntryPoint.
This entrypoint adds the
"WWW-Authenticate": "Basic realm=..."
header that tells your browser to use Basic Auth.
If you're extending WebSecurityConfigurerAdapter, you can pass in true to the super constructor to disable the defaults.
You may need to provide other beans if you do this.
/**
* Creates an instance which allows specifying if the default configuration should be
* enabled. Disabling the default configuration should be considered more advanced
* usage as it requires more understanding of how the framework is implemented.
*
* #param disableDefaults true if the default configuration should be disabled, else
* false
*/
protected WebSecurityConfigurerAdapter(boolean disableDefaults) {
this.disableDefaults = disableDefaults;
}
If you want to disable it just for testing purposes -
Rather than completely disabling the auto-configuration, I create an "InsecurityConfiguration" in addition to "SecurityConfiguration", and activate it with either a Spring Profile or Property value.
Technically security is still configured, but wide open.
#Configuration
#ConditionalOnProperty(prefix = "security", value = "disabled", havingValue = "true")
public class InsecurityConfiguration extends WebSecurityConfigurerAdapter {
private final static Logger log = LoggerFactory.getLogger(InsecurityConfiguration.class);
#Override
protected void configure(HttpSecurity http) throws Exception {
log.warn("configuring insecure HttpSecurity");
http.authorizeRequests().anyRequest().permitAll();
}
#Override
public void configure(WebSecurity web) throws Exception {
log.warn("configuring insecure WebSecurity");
web.ignoring().antMatchers("/**");
}
}
Note This is for mvc, not webflux. For Webflux you should create a SecurityWebFilterChain like Bryan mentioned.
This is how I generally disable basic auth in webflux, when using JWT -
#Bean
public SecurityWebFilterChain configure(ServerHttpSecurity http) {
http
.authorizeExchange().anyExchange().authenticated().and()
.httpBasic().disable()
.formLogin().disable()
.logout().disable()
.oauth2ResourceServer()
.jwt()
.and()
.and().exceptionHandling().accessDeniedHandler(problemSupport);
return http.build();
}
Only properties - works for me (sb2 - 2022):
spring:
autoconfigure:
exclude:
- org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
- org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration
Simple solution for Spring Boot 2.6
#SpringBootApplication(exclude = {SecurityAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class, UserDetailsServiceAutoConfiguration.class})
In Spring boot 2, there is no way to disable basic authentication by application.properties file. But the only thing is use annotation
#EnableAutoConfiguration(exclude = {SecurityAutoConfiguration.class})
in the main class.
It works
The problem is with org.springframework.security.web.server.authorization.ExceptionTranslationWebFilter
it has private ServerAuthenticationEntryPoint authenticationEntryPoint = new HttpBasicServerAuthenticationEntryPoint();
so to fix it during ServerHttpSecurity initialization add:
http.exceptionHandling().authenticationEntryPoint(HttpStatusServerEntryPoint(HttpStatus.FORBIDDEN))
Looks like vanilla (servlet) spring uses org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer#createDefaultEntryPoint
private AuthenticationEntryPoint createDefaultEntryPoint(H http) {
if (this.defaultEntryPointMappings.isEmpty()) {
return new Http403ForbiddenEntryPoint();
}
if (this.defaultEntryPointMappings.size() == 1) {
return this.defaultEntryPointMappings.values().iterator().next();
}
DelegatingAuthenticationEntryPoint entryPoint = new DelegatingAuthenticationEntryPoint(
this.defaultEntryPointMappings);
entryPoint.setDefaultEntryPoint(this.defaultEntryPointMappings.values().iterator()
.next());
return entryPoint;
}
Side note: mutable fields in builder style beans (like ExceptionTranslationWebFilter) make spring code hard to debug (too magic configuration as well)
You should add #EnableWebSecurity to enable a custom security configuration.
After that simply disable the form login
#Configuration
#EnableWebSecurity
public class StackWebSecurityConfigurerAdapter extends
WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin().disable();
}
}
This worked for me
#SpringBootApplication(exclude = {UserDetailsServiceAutoConfiguration.class})
class SpringApplication{
...
}
It is possible add a HandlerInterceptorAdapter with Zuul Configuration. I need to intercept a request to a specific resource but I suppose because I have Zuul filter configuration, the interceptor is never called.
Is possible to do so?
I have tried to achieve the same. We have a few Spring MVC controllers and Zuul proxying. But I still wanted the same Interceptor to be used.
The problem here is that zuul runs in its own ZuulServlet, and does not pick up the interceptors from your MVC servlet. Spring Cloud: ZuulConfiguration.java configures ZuulHandlerMapping, which is the only place interceptors could be set, but it's not configurable. Thus you need a InstantiationAwareBeanPostProcessorAdapter to interfere with the bean creation, to set your interceptors after instantiaton, but before initialization (before the interceptors are initialized).
This did the trick for me:
#Configuration
#RequiredArgsConstructor
public class ZuulHandlerBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter {
#NonNull
private final MyInterceptor myInterceptor;
#Override
public boolean postProcessAfterInstantiation(final Object bean, final String beanName) throws BeansException {
if (bean instanceof ZuulHandlerMapping) {
val zuulHandlerMapping = (ZuulHandlerMapping) bean;
zuulHandlerMapping.setInterceptors(myInterceptor);
}
return super.postProcessAfterInstantiation(bean, beanName);
}
}
I'm using Spring boot + Jersey + Spring security, I want to have public and private endpoints, I want an schema as follow:
/rest -- My root context
/public -- I want to place my public endpoints in this context, It must be inside of the root context like /rest/public/pings
/private -- I want to place my private endpoints in this context, It must be inside of the root context like /rest/private/accounts
I have my configuration as follow:
Jersey configuration:
#Configuration
#ApplicationPath("/rest")
public class RestConfig extends ResourceConfig {
public RestConfig() {
register(SampleResource.class);
}
}
Spring security configuration:
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
........
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/rest/public/**").permitAll();
http.antMatcher("/rest/**").authorizeRequests().anyRequest().fullyAuthenticated().and().httpBasic();
http.csrf().disable();
}
}
The question is how can I register two application paths inside of my /rest context, one for /public and the other one for /private ?
NOTE: I tried to create another ResourceConfig as follow:
#Configuration
#ApplicationPath("/rest/public")
public class RestPublicConfig extends ResourceConfig{
public RestPublicConfig() {
register(PingResource.class);
}
}
But I'm getting the next error:
No qualifying bean of type [org.glassfish.jersey.server.ResourceConfig] is defined: expected single matching bean but found 2: restConfig,restPublicConfig
Thanks for your help :)
In a servlet container, the Jersey runtime, runs as either a servlet or as a servlet filter. How spring boot configures servlets and filters is through ServletRegistrationBeans and FilterRegistrationBeans, respectively. To get an idea of how that configuration works behind scenes, you can look at the source code for the JerseyAutoConfiguration
In the JerseyAutoConfiguration, you can see that a ResourceConfig is injected, and that is the ResourceConfig used to create the Jersey servlet or Jersey filter (depending on your choice of configuration). So the reason for the error is that you can't have ambiguous beans, which you have two ResourceConfig beans. So Spring doesn't know which one to inject.
What you can do though, is use two different servlets for each ResourceConfig. The problem is that Spring Boot only hooks you up with one servlet for Jersey, so you need to configure the other one yourself. There are two options:
Use the Spring Boot auto-configuration for one of the Jersey applications, and add another ServletRegistrationBean for your other one. The one thing to note is that the ResourceConfig for your created ServletRegistrationBean should not be a Spring component (i.e. no #Component or #Configuration), or else you will still face the same error.
public class PublicConfig extends ResourceConfig {
public PublicConfig() {
register(PingResource.class);
}
}
...
// in your Spring Boot configuration class
#Bean
public ServletRegistrationBean publicJersey() {
ServletRegistrationBean publicJersey
= new ServletRegistrationBean(new ServletContainer(new PublicConfig()));
publicJersey.addUrlMappings("/rest/public/*");
publicJersey.setName("PublicJersey");
publicJersey.setLoadOnStartup(0);
return publicJersey;
}
Don't use the Spring Boot configuration at all. Just create two ServletRegistrationBeans. In this case, none of your ResourceConfig classes should be Spring beans.
#Bean
public ServletRegistrationBean publicJersey() {
ServletRegistrationBean publicJersey
= new ServletRegistrationBean(new ServletContainer(new PublicConfig()));
publicJersey.addUrlMappings("/rest/public/*");
publicJersey.setName("PublicJersey");
publicJersey.setLoadOnStartup(0);
return publicJersey;
}
#Bean
public ServletRegistrationBean privateJersey() {
ServletRegistrationBean privateJersey
= new ServletRegistrationBean(new ServletContainer(new PrivateConfig()));
privateJersey.addUrlMappings("/rest/private/*");
privateJersey.setName("PrivateJersey");
privateJersey.setLoadOnStartup(1);
return privateJersey;
}
Personally, I prefer the second option, as it is easier to reason about the configurations when they are all in one place.
Another thing to note is that the two Jersey applications will be completely independent, meaning you will need to register providers (like filters) for both applications
You won't be allowed to create two beans for your Resource Class. You can achieve what you are trying to achieve using a single Resource Class as well.
Here is an example:
#Path("rest")
public class SampleResourceClass {
#Path("/public/pings")
#GET
public Responce getPings(){
/* Code Here */
}
#Path("/private/accounts")
#GET
public Response getAccounts(){
/* Code Here */
}
}
The error you are seeing is not related to your security config, you may want to take a look at this ticket, https://github.com/spring-projects/spring-boot/issues/3260
If you want to permit all traffic to endpoints past /public you can add the RequestMatcher to the Spring Security ignore list.
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/rest/public/**");
}
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatcher("/rest/private/**")
.anyRequest().authenticated().and()
.httpBasic().and()
.csrf().disable()
}
}
http://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#jc