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?
Related
I have a problem with my RestController interceptor.
My goal is to get the RestController path in a HandlerInterceptorAdapter and then use it to create metrics.
Via the interface HttpServletRequest I have access to the path, but it is resolved there.
Example of what I would like to get in my interceptor:
GET: object/123 // wrong
GET object/{id} // right
Is there any way to get the path without resolved variables?
Here is my implementation:
RestController:
#RestController
public class ObjectController
{
#GetMapping("object/{id}")
public String getObjectById(#PathVariable String id)
{
return id;
}
}
Config:
#Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter
{
#Override
public void addInterceptors(InterceptorRegistry registry)
{
registry.addInterceptor(new RequestInterceptor());
}
}
Interceptor:
public class RequestInterceptor extends HandlerInterceptorAdapter
{
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception
{
System.out.println(request.getRequestURI());
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
#Nullable ModelAndView modelAndView) throws Exception
{
System.out.println(request.getRequestURI());
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
#Nullable Exception ex) throws Exception
{
System.out.println(request.getRequestURI());
}
}
I have used Spring boot 4 version project with spring security. I have tried to implement logger interceptor in my project and that is not working.
Logger Interceptor Class
#Component
public class LoggerInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest requestServlet, HttpServletResponse responseServlet, Object handler) throws Exception
{
System.out.println("MINIMAL: INTERCEPTOR PREHANDLE CALLED");
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception
{
System.out.println("MINIMAL: INTERCEPTOR POSTHANDLE CALLED");
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception exception) throws Exception
{
System.out.println("MINIMAL: INTERCEPTOR AFTERCOMPLETION CALLED");
}
}
Config class
public class InterceptorConfig implements WebMvcConfigurer {
#Autowired
LoggerInterceptor logInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoggerInterceptor()).addPathPatterns("/**");
}
}
I have tried the above code in simple spring boot project it is working fine, but while tried on my project it is not calling the interceptor.
Please throw some light for this.
Let's say we have two interceptors "LogInterceptor" and "AuthInterceptor".
The first interceptor logs the incoming request and the second one authenticate it.
Aim: Chain LogIntercptor and AuthInterceptor. First I want the logInterceptor be called and after that AuthInterceptor should be executed.
Note: I know about "redirect" and returning false (Please don't suggest the one)
---------------------Log Interceptor---------------------------------------
//First Inteceptor
#Component
public class LogInterceptor extends HandlerInterceptorAdapter
{
private final Logger Logger =
LoggerFactory.getLogger(this.getClass());
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
//take action base on incoming IP
long startTime = System.currentTimeMillis();
request.setAttribute("startTime",startTime);
if(request.getRemoteAddr().startsWith("192"))
{
response.sendRedirect("/auth-failed"); //redirect to default
return false;
}
return true;
}
#Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler, #Nullable ModelAndView
modelAndView) throws Exception
{
}
#Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, #Nullable Exception
ex) throws Exception
{
}
}
//Second interceptor "Code is however not complete, I am just seeeking how this can be achieved."
class AuthInterceptor
{
}
You just need to make sure the Interceptor are added in your desired orders when configuring InterceptorRegistry:
#EnableWebMvc
#Configuration
public class WebConfig implements WebMvcConfigurer{
#Autowired
private LogInterceptor logInterceptor;
#Autowired
private AuthInterceptor authInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(logInterceptor);
registry.addInterceptor(authInterceptor);
}
}
In case you have multiple WebMvcConfigurer , you can simply use #Order to control which the execution order of WebMvcConfigurer (lower value has higher priority):
#EnableWebMvc
#Configuration
#Order(1)
public class FooWebConfig implements WebMvcConfigurer{
}
#EnableWebMvc
#Configuration
#Order(2)
public class BarWebConfig implements WebMvcConfigurer{
}
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);
}
}
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");
}
}