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);
}
}
Related
I have a service-client project which is in normal spring application , not spring boot .its used for mainly logging related things.which contains Interceptor , loggingservice impl class and some model classes for logging. I have added this module as a dependency to main application in pom.xml.and i was able to inject and use the loggingService beans within the service layers of the main application.
Am getting NullPointerException while auto-wiring loggingService within the interceptor .The bean is not available within the interceptor.but like i said it can be injected and used within the main application.
Also am not able to read properties using #Value within the interceptor.
This is my Interceptor class .
#Component
public class LoggingInterceptor extends HandlerInterceptorAdapter {
#Autowired
LoggingService loggingService;
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object) throws Exception {
loggingService.info("Am in prehandle");
return true;
}
}
This is my configuration class where i register the interceptor with the main application
#Component
public class LoggingConfig implements WebMvcConfigurer {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(getLoginInterceptor());
}
#Bean
public LoggingInterceptor getLoginInterceptor() {
return new LoggingInterceptor();
}
}
My question is almost similar to this post Cannot Autowire Service in HandlerInterceptorAdapter , but its different like am referring the interceptor from another module , and like they suggested i tried to create the bean from the application.
But the issues am facing right now is
getting NullPointerException while injecting loggingService within interceptor, but its working in main application
#Value annotation also return null, not able to read from properties
You have 2 possible solutions.
Mark your LoggingConfig as #Configuration instead of #Copmponent
Inject the LoggingInterceptor instead of referencing the #Bean method
Option 1: LoggingConfig as #Configuration
Your LoggingConfig is marked as an #Component whereas it should be marked as an #Configuration. The difference is that whilst it is allowed to have an #Bean method on an #Component it operates in a so-called lite mode. Meaning you cannot use method references to get the instance of a bean (this is due to no special proxy being created). This will lead to just a new instance of the LoggingInterceptor being created but it isn't a bean.
So in short what you are doing is equivalent to registry.addInterceptor(new LoggingInterceptor()); which just creates an instance without Spring knowing about it.
When marking the LoggingConfig as an #Configuration a special proxy will be created which will make the LoggingInterceptor a proper singleton bean, due to the method call being intercepted. This will register the bean in Spring and you will be able call the method.
NOTE: You actually endup with 2 instances of the LoggingInterceptor one due to the #Component on it the other through the #Bean. Remove the #Component.
Option 2: Inject the LoggingInterceptor.
As your LoggingInterceptor is marked as an #Component Spring will already create an instance (you actually have 2 instances of it created in your current setup). This instance you can inject into your LoggingConfig.
#Component
public class LoggingConfig implements WebMvcConfigurer {
private LoggingInterceptor loggingInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loggingInterceptor);
}
}
With this you can remove the #Bean method as you will get the proper one injected into your LoggingConfig class. The class can also remain an #Component in this case. Although I would recommend using #Configuration as to also properly stereotype it.
NOTE: If you are on a recent Spring version you can use #Configuration(proxyBeanMethods=false). This will make a lite-configuration (just like an #Component) but it is still marked properly as a configuration class.
I want to use #Autowire with a Filter. So I define my filter in the SecurityConfig as below:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(getA(), BasicAuthenticationFilter.class);
http.csrf().disable();
}
#Bean
public A getA(){
return new A();
}
This filter A extends Spring's GenericFilterBean.
I get below output when I invoke the controller, which shows the filter hits twice.
filter A before
filter A before
mycontroller invoke
filter A after
filter A after
My observation is, this extra invocation invoke with Spring container because if filter is not register as bean, it only get hits once. What is the reason and how can I fix it?
As you have observed, Spring Boot will automatically register any bean that is a Filter with the servlet container. One option is to not expose your filter as a bean and only register it with Spring Security.
If you want to be able to autowire dependencies into your Filter then it needs to be a bean. That means you need to tell Spring Boot not to register it as a filter. As described in the documentation, you do that using a FilterRegistrationBean:
#Bean
public FilterRegistrationBean registration(MyFilter filter) {
FilterRegistrationBean registration = new FilterRegistrationBean(filter);
registration.setEnabled(false);
return registration;
}
If you are on Spring 6.0.2 or plus version and if using OncePerRequestFilter,
Overriding shouldNotFilter method as follows will work.
#Override
protected boolean shouldNotFilter(HttpServletRequest request) {
return request.getServletPath().contains("/api/path/which/needs/to/exclude");
}
Removing #Component from the filter class helped me.
public class AuthTokenFilter extends OncePerRequestFilter {
}
I can't really find a neat way to do this in Spring - imagine I have a class which listens for messages:
#Component
public class MessageReceiver {
private List<MessageHandler> handlers = new ArrayList<>();
public void receiveMessage(Message m) {
for(MessageHandler handler: handlers) {
handler.process(m);
}
}
public void registerMessageHandler(MessageHandler handler) {
handlers.add(handler);
}
}
Basically, I want to be able to define multiple MessageHandler beans and have themselves register with the MessageReceiver automatically. I'd prefer not to have to maintain a list of MessageHandlers somewhere - I'd simply like Spring to find any in the context (maybe annotated #Bean or #Component) and have them call the registerMessageHandler() method as part of the wiring process.
What would be the neatest way to do that?
Have you considered using the event publishing mechanism already included in Spring?
ApplicationEventPublisher can be #Autowired, and Spring will connect any #Component that implements ApplicationListener<T> or method annotated with #EventListener if you use Spring 4.2 or newer.
This feature is used by various Spring components, like Spring security to broadcast events, for instance when users authenticate.
You can read about this here
Edit
If you don't like to use the built-in message mechanism you can just build your own using the same auto detection principle.
The current version of Spring allows you to #AutoWire a list of beans based on type, If all your MessageHandler instances are beans then It would look like this
#Component
public class MessageReceiver {
private final List<MessageHandler> handlers;
#Autowired
public MessageReceiver(List<MessageHandler> handlers) {
this.handlers = handlers;
}
public void receiveMessage(Message m) {
for (MessageHandler handler : handlers) {
handler.process(m);
}
}
}
If you are using a version of Spring that does not support autowiring collections, then you should implement ApplicationContextAware to get a reference to the ApplicationContext, so you can call getBeansOfType() to get all the MessageHandlers.
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
The application uses JDK 8, Spring Boot & Spring Boot Jersey starter and is packaged as a WAR (although it is locally run via Spring Boot Maven plugin).
What I would like to do is to get the documentation I generate on the fly (at build time) as a welcome page.
I tried several approaches:
letting Jersey serving the static contents by configuring in application.properties the proper init parameter as described here
introduce a metadata-complete=false web.xml in order to list the generated HTML document as a welcome-file.
None of that worked out.
I would like to avoid having to enable Spring MVC or creating a Jersey resource just for serving a static file.
Any idea?
Here is the Jersey configuration class (I unsuccessfully tried to add a ServletProperties.FILTER_STATIC_CONTENT_REGEX there):
#ApplicationPath("/")
#ExposedApplication
#Component
public class ResourceConfiguration extends ResourceConfig {
public ResourceConfiguration() {
packages("xxx.api");
packages("xxx.config");
property(ServerProperties.BV_DISABLE_VALIDATE_ON_EXECUTABLE_OVERRIDE_CHECK, true);
property(ServerProperties.BV_SEND_ERROR_IN_RESPONSE, true);
}
}
And here is Spring Boot application class (I tried adding an application.properties with spring.jersey.init.jersey.config.servlet.filter.staticContentRegex=/.*html but it didn't work, I'm not exactly sure what the property key should be here):
#SpringBootApplication
#ComponentScan
#Import(DataConfiguration.class)
public class Application extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Let me just first state, that the reason the static content won't be served is because of the default servlet mapping of the Jersey servlet, which is /*, and hogs up all the requests. So the default servlet that serves the static content can't be reached. Beside the below solution, the other solution is to simply change the servlet mapping. You can do that by either annotating your ResourceConfig subclass with #ApplicationPath("/another-mapping") or set the application.properties property spring.jersey.applicationPath.
In regards to your first approach, take a look at the Jersey ServletProperties. The property you are trying to configure is FILTER_STATIC_CONTENT_REGEX. It states:
The property is only applicable when Jersey servlet container is configured to run as a Filter, otherwise this property will be ignored
Spring Boot by default configures the Jersey servlet container as a Servlet (as mentioned here):
By default Jersey will be set up as a Servlet in a #Bean of type ServletRegistrationBean named jerseyServletRegistration. You can disable or override that bean by creating one of your own with the same name. You can also use a Filter instead of a Servlet by setting spring.jersey.type=filter (in which case the #Bean to replace or override is jerseyFilterRegistration).
So just set the property spring.jersey.type=filter in your application.properties, and it should work. I've tested this.
And FYI, whether configured as Servlet Filter or a Servlet, as far as Jersey is concerned, the functionality is the same.
As an aside, rather then using the FILTER_STATIC_CONTENT_REGEX, where you need to set up some complex regex to handle all static files, you can use the FILTER_FORWARD_ON_404. This is actually what I used to test. I just set it up in my ResourceConfig
#Component
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
packages("...");
property(ServletProperties.FILTER_FORWARD_ON_404, true);
}
}
For anyone who still can't get this to work, I followed the answer provided by #peeskillet, and had to make an additional change.
Previously I had created the following method in Application.java.
#Bean
public ServletRegistrationBean jerseyServlet() {
ServletRegistrationBean registration = new ServletRegistrationBean(new ServletContainer(), "/*");
registration.addInitParameter(ServletProperties.JAXRS_APPLICATION_CLASS, JerseyConfig.class.getName());
return registration;
}
The problem is that this registered the servlet for the /* path, and then setup the Jersey ResourceConfig configuration file.
Once I removed the above method, and placed the #Configuration annotation on my ResourceConfig class, I noticed the static resource could be retrieved via Spring Boot.
For completeness, this is a snippet of my ResourceConfig now.
#Configuration
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
// Application specific settings
property(ServletProperties.FILTER_FORWARD_ON_404, true);
}
}
This blog post was helpful in determining the difference approach for the ResourceConfig.
Below setup worked for me
Set
spring .jersey.type: filter
set FILTER_FORWARD_ON_404
#Configuration
public class MyResourceConfig extends ResourceConfig {
public MyResourceConfig () {
try {
register(XXX.class);
property(ServletProperties.FILTER_FORWARD_ON_404, true);
} catch (Exception e) {
LOGGER.error("Exception: ", e);
}
}
}
Note: Use #Configuration instead of #component