Spring - registering ApplicationContextInitializer from WebApplicationInitializer - spring

how can i register a ApplicationContextInitializer from the WebApplicationInitializer class.

Assuming your goal is to use the ApplicationContextInitializer with the DispatcherServlet, all you have to do is invoke a setter
public class CustomInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>{
#Override
public void initialize(ConfigurableApplicationContext applicationContext) {
// do your thing
}
}
class WebInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext)
throws ServletException {
DispatcherServlet servlet = new DispatcherServlet();
servlet.setContextInitializers(new CustomInitializer());
// more logic
}
}
The DispatcherServlet will invoke your ApplicationContextInitializer before the context is refreshed.

Related

Spring Boot interceptor not called with custom handler mapping/adapter

In my Spring Boot 2 project I use a simple interceptor that was working fine. However after creating a custom HandlerAdapter and SimpleUrlHandlerMapping the interceptor never executed again.
public class RequestMonitoringInterceptor extends HandlerInterceptorAdapter {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
logger.debug("preHandle");
return super.preHandle(request, response, handler);
}
...
}
And registered in my WebConfig as:
#Configuration
public class WebConfig implements WebMvcConfigurer {
#Bean
public RequestMonitoringInterceptor requestMonitoringInterceptor() {
return new RequestMonitoringInterceptor();
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(requestMonitoringInterceptor());
}
}
Any idea what I have missed?

Spring Boot: Inject Bean into HttpServlet

I've got a Spring Boot where I've autoconfigured a Router bean.
This all works perfect but it becomes a problem when I want to inject that bean into a custom servlet:
public class MembraneServlet extends HttpServlet {
#Autowired
private Router router;
#Override
public void init() throws ServletException {
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
}
#Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
new HttpServletHandler(req, resp, router.getTransport()).run();
}
}
This should be the way to go, but
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
won't autowire the Router because the WebapplicationContext is always null. The application is running in an MVC environment.
Assuming you Spring Application Context is wired to the Servlet Context, you might want to pass ServletContext to SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext
public class MembraneServlet extends HttpServlet {
#Autowired
private Router router;
#Override
public void init() throws ServletException {
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this, getServletContext());
}
#Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
new HttpServletHandler(req, resp, router.getTransport()).run();
}
}
What about injecting 'Router' as constructor parameter.
So you would have this:
public class MembraneServlet extends HttpServlet {
private Router router;
public MembraneServlet(Router router){
this.router = router;
}
#Override
public void init() throws ServletException {
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
}
#Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
new HttpServletHandler(req, resp, router.getTransport()).run();
}
}
then you can programatically create servlet registration like this:
#Bean
public ServletRegistrationBean membraneServletRegistrationBean(){
return new ServletRegistrationBean(new MembraneServlet(),"/*");
}
Embedded server
You can annotate with #WebServlet your servlet class:
#WebServlet(urlPatterns = "/example")
public class ExampleServlet extends HttpServlet
And enable #ServletComponentScan on base class:
#ServletComponentScan
#EntityScan(basePackageClasses = { ExampleApp.class, Jsr310JpaConverters.class })
#SpringBootApplication
public class ExampleApp
But injection with #ServletComponentScan will work only with embedded server:
Enables scanning for Servlet components (filters, servlets, and
listeners). Scanning is only performed when using an embedded web
server.
More info: The #ServletComponentScan Annotation in Spring Boot
External server
When using external server, mark HttpServlet class as #Component:
#Component
public class ExampleServlet extends HttpServlet
And create configuration class:
#Configuration
#ComponentScan(value = "com.example.servlet.package")
public class ServletConfig {
#Autowired
private ExampleServlet exampleServlet;
#Bean
public ServletRegistrationBean resetServletRegistrationBean(){
return new ServletRegistrationBean(exampleServlet, "/example");
}
}

Spring Security Filter showing not found despite being defined in Java config

I have configured my Spring application using Java based config. When I start my application, I get the error NoSuchBeanDefinitionException: No bean named 'springSecurityFilterChain' is defined. I have defined all the configuration classes but this error still occurs. How do I fix this?
My main class:
public class SiteMain implements WebApplicationInitializer {
private void initSpringMvcServlet(ServletContext container) {
AnnotationConfigWebApplicationContext dispatcherContext = new AnnotationConfigWebApplicationContext();
dispatcherContext.register(MvcConfig.class);
ServletRegistration.Dynamic dispatcher = container.addServlet("dispatcher", new DispatcherServlet(dispatcherContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
}
private void initSpringRootContext(ServletContext container) {
XmlWebApplicationContext rootContext = new XmlWebApplicationContext();
rootContext.setConfigLocation("/WEB-INF/site.xml");
container.addListener(new ContextLoaderListener(rootContext));
}
#Override
public void onStartup(ServletContext container) throws ServletException {
initSpringRootContext(container);
initSpringMvcServlet(container);
}
}
My Security Initializer class is:
public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer { }
My MVC Config class is:
#Configuration
#EnableWebMvc
#ComponentScan
#Import(SecurityConfig.class)
public class MvcConfig extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/").setCachePeriod(31556926);
}
}
My Security Config class is:
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/resources/**").permitAll().anyRequest().authenticated()
.and().formLogin().loginPage("/").permitAll()
.and().logout().permitAll()
.and().rememberMe();
}
}
Try replacing this line:
dispatcherContext.register(MvcConfig.class);
with:
dispatcherContext.setConfigLocation("your.config.package");
add line below:
container.addListener(new ContextLoaderListener(dispatcherContext));
There is no need to #Import(SecurityConfig) since setConfigLocation will automatically detect all #Configuration annotated classes.

How to use a DelegatingFilterProxy in AbstractAnnotationConfigDispatcherServletInitializer

I want to register a DelegatingFilterProxy in the onStartup method of a AbstractAnnotationConfigDispatcherServletInitializer. Unfortunately, the delegate bean for the DelegatingFilterProxy has not been created when the servlet gets initialized:
#Order(1)
public class MvcWebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
...
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
FilterRegistration.Dynamic localeFilter = servletContext.addFilter("localeFilter", new DelegatingFilterProxy("localeFilter"));
localeFilter.addMappingForUrlPatterns(null, false, "/*");
}
...
}
#Configuration
#EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
...
#Bean
public LocaleFilter localeFilter() {
return new LocaleFilter();
}
...
}
How can you make sure the the localeFilter bean gets created before the servlet's startup?

Global method security in Spring Boot

I'm having some issues when trying to enable the global method security in a Spring Boot application.
More or less I've this configuration:
#ComponentScan
#Configuration
#EnableAutoConfiguration
#EnableConfigurationProperties
public class Main extends SpringBootServletInitializer {
public static void main(String[] args) throws Exception {
SpringApplication app = new SpringApplication(Main.class);
app.setShowBanner(false);
ApplicationContext context = app.run(args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Main.class);
}
}
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true, proxyTargetClass = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
...
}
#Override
protected void configure(HttpSecurity http) throws Exception {
...
}
}
#Controller
public class SampleController {
#RequestMapping("/api/hello")
#ResponseBody
String hello() {
return "Hello!";
}
#Secured(SecurityGrant.WRITE_PROJECT)
#RequestMapping("/api/bye")
#ResponseBody
String bye() {
return "Bye!";
}
}
The #Secure annotations are working OK at services, but not in controllers, so as I read here (http://docs.spring.io/spring-security/site/faq/faq.html#faq-method-security-in-web-context) I think is because method security is only configured in the root application context and not in the one for the servlet.
However, I can't find the way to set this via Java Configuration, instead of using a web.xml file.
Any ideas?
Update:
As pointed in the comments, methods should be public to be proxied.
The controller methods need to be public in order to be proxied for #Secured. Just doing that should fix it.
In XML you would have to define a second global-method-security in the servlet-context.xml file. This is because there are two contexts, the root context and the web context and security needs to be configured in each separately.
In Java config, try to create a separate web configuration class, and mark it with #EnableWebMvc:
#Configuration
#EnableWebMvc
#EnableGlobalMethodSecurity(securedEnabled = true, proxyTargetClass = true)
public class WebConfig {
...
}

Resources