Spring Boot: Inject Bean into HttpServlet - spring

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");
}
}

Related

Can't configure WebMvcConfigurer for interceptors addition in spring boot 2 application

I am trying to create an interceptor for the first time in my spring boot application, but somehow it is not created automatically, as described in the tutorials.
I've tried to create a WebConfig class that extends the WebMvcConfigurerAdapter class and annotated it as a #Component but it haven't worked. I also tried to create a WebConfig that implements the WebMvcConfigurer interface with #Configuration and #EnableWebMvc annotations but it hadn't worked either.
current WebConfig class:
#Configuration
#EnableWebMvc
#ComponentScan("com.*")
public class WebConfig implements WebMvcConfigurer {
public WebConfig() {
super();
}
#Autowired
HandlerInterceptor headerModifierInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
System.out.println("------------------hi");
registry.addInterceptor(headerModifierInterceptor);
}
}
Application class
#SpringBootApplication
#EnableWebSecurity
#ComponentScan(basePackages = {"com.*"})
#EntityScan("com")
public class CoreRestAPIApplication {
public static void main(String[] args) {
SpringApplication.run(CoreRestAPIApplication.class, args);
}
}
My interceptor class:
#Component
public class RestTemplateHeaderModifierInterceptor
implements HandlerInterceptor {
#Autowired
AuthUtil authUtil;
#Autowired
JwtTokenProvider jwtTokenProvider;
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
String resolvedToken = jwtTokenProvider.resolveToken(request);
if (!StringUtils.isEmpty(resolvedToken)) {
String updatedToken = jwtTokenProvider.createToken(jwtTokenProvider.getUsername(resolvedToken), jwtTokenProvider.getAuthentication(resolvedToken).getAuthorities());
response.addHeader(authUtil.AUTH_HEADER_NAME, updatedToken);
}
}
}
After some search, I've found that I have a registered WebMvcConfigurationSupport configuration. However, if someone is looking and wishes to modify headers using an interceptor, DO NOT use an interceptor for that, as for spring will not handle it well if you return a ResponseEntity or your controller method returns a #ResponseBody.
Instead(at least for my use which is filtering and renewing a token every time a valid request is received) use the doFilterInternal method to add the header to the response(or add a cookie if you wish..) here is an example of how I did it:
public class JwtTokenFilter extends OncePerRequestFilter {
private JwtTokenProvider jwtTokenProvider;
public JwtTokenFilter(JwtTokenProvider jwtTokenProvider) {
this.jwtTokenProvider = jwtTokenProvider;
}
#Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
String token = jwtTokenProvider.resolveToken(httpServletRequest);
try {
if (token != null && jwtTokenProvider.validateToken(token)) {
Authentication auth = jwtTokenProvider.getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(auth);
if(!jwtTokenProvider.isExpired(token)) {
httpServletResponse.setHeader("authKey", jwtTokenProvider.createToken(jwtTokenProvider.getUsername(token), auth.getAuthorities()));
}
}
} catch (ClientErrorException ex) {
//this is very important, since it guarantees the models is not authenticated at all
SecurityContextHolder.clearContext();
httpServletResponse.sendError(ex.getStatus().value(), ex.getMessage());
return;
}
filterChain.doFilter(httpServletRequest, httpServletResponse);
}
}

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?

PrincipalExtractor and AuthoritiesExtractor doesn't hit

I have a project with Spring security and Oauth2.
On the resource server I have the following configuration:
#Configuration
public class SecurityConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(final HttpSecurity http) throws Exception {
http.antMatcher("/**")
.authorizeRequests().antMatchers("/info", "/health", "/h2-console/**").permitAll()
.anyRequest().authenticated()
.and().headers().frameOptions().disable();
}
}
I have the following extractors:
#Component
public class InsurancePrincipalExtractor implements PrincipalExtractor {
#Override
public Object extractPrincipal(Map<String, Object> map) {
return map.get("username");
}
}
#Component
public class InsuranceAuthoritiesExtractor implements AuthoritiesExtractor {
#Override
public List<GrantedAuthority> extractAuthorities(Map<String, Object> map) {
//Logic
}
I set the user-info-uri: http://localhost:8081/uaa/v1/me
The problem is that it does not hit my extractor methods at runtime, so nothing happens. As I know I just need to annotate it with the #Component and the Spring boot and will use it auto.
UPDATE:
Solution founded.
I had to add this to my configuration as well:
#Bean
protected ResourceServerTokenServices resourceServerTokenServices(ResourceServerProperties sso,
OAuth2ClientContext oauth2ClientContext,
UserInfoRestTemplateFactory restTemplateFactory) {
UserInfoTokenServices services = new UserInfoTokenServices(sso.getUserInfoUri(), sso.getClientId());
services.setRestTemplate(restTemplateFactory.getUserInfoRestTemplate());
services.setTokenType(sso.getTokenType());
return services;
}

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.

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