Spring Interceptor is not compatible with #RepositoryRestResource - spring

#Component
public class TestInterceptor implements HandlerInterceptor {
#Override
public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3) throws Exception {
System.out.println("afterCompletion");
}
#Override
public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) throws Exception {
System.out.println("postHandle");
}
#Override
public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {
System.out.println("preHandle");
return true;
}
}
and
#SpringBootConfiguration
public class AnnotationSecurityConfiguration implements WebMvcConfigurer {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new TestInterceptor()).addPathPatterns("/api/**");
}
}
finally the entity is presented as REST using a #RepositoryRestController as follows:
#RepositoryRestResource(excerptProjection = UserSummaryProjection.class)
public interface UserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> {
}
Then calling
curl -X GET https://localhost:8080/api/v1/users
but the interceptor is not called.
Because the REST resource is managed Spring Data Rest with #RepositoryRestController the interceptor is not called. But if I write the REST resource with #RestController it will work.
How can I make interceptors work with #RepositoryRestController?

I got it working (on / context path, and a simple User class, spring-boot v2.4.2) with the following config/spring-boot app:
...
import org.springframework.web.servlet.handler.MappedInterceptor
...
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Bean
public org.springframework.web.servlet.handler.MappedInterceptor myInterceptor() {
return new MappedInterceptor(
new String[]{"/users/**"}, // null => maps to any repository/path
new MyInterceptorImpl()
);
}
private class MyInterceptorImpl implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle");
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle");
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion");
}
}
}
I used a simplified:
#RepositoryRestResource
public interface UserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> { }

you have to add TestInterceptor as a bean and anotate it with #Autowired just add these changes to register the Interceptor into Interceptor Registry :
#Configuration
#SpringBootApplication
public class DemoApplication extends WebMvcConfigurerAdapter {
#Autowired
private TestInterceptor testInterceptor;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(testInterceptor).addPathPatterns("/api/**");
}
HandlerInterceptor :
#Component
public class TestInterceptor implements HandlerInterceptor {
private static final Logger logger = LoggerFactory.getLogger(TestInterceptor.class);
#Override
public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3) throws Exception {
System.out.println("afterCompletion");
long startTime = Instant.now().toEpochMilli();
logger.info("Request URL::" + arg0.getRequestURL().toString() +
":: Start Time=" + Instant.now());
arg0.setAttribute("startTime", startTime);
}
#Override
public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) throws Exception {
System.out.println("postHandle");
}
#Override
public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {
System.out.println("preHandle");
return true;
}
}
as a sample i have changed the '/api/' to '/' and here a log sample as the below :
2021-02-13 00:36:31.075 INFO 14340 --- [on(2)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Completed initialization in 22 ms
preHandle
postHandle
afterCompletion
2021-02-13 00:36:35.300 INFO 14340 --- [nio-8080-exec-1] com.example.demo.TestInterceptor : Request URL::http://localhost:8080/time:: Start Time=2021-02-12T22:36:35.289832Z

It only worked for me, using Spring Boot 2.6.7, by adding a MappedInterceptor.
#Bean
public MappedInterceptor loggingMappedInterceptor(TestInterceptor testInterceptor) {
return new MappedInterceptor(
null, // => maps to any repository
testInterceptor);
}

Related

How do I get the rest path in a HandlerInterceptorAdapter without resolved path variables

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 am trying to add interceptors to spring project,my prehandle method is not getting called

I want my prehandle method to be called.On debugging i see the control going inside ProductServiceInterceptor class but none of the methods inside are getting called
#EnableWebMvc
#Configuration
public class ProductServiceInterceptorAppConfig extends WebMvcConfigurerAdapter {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new productServiceInterceptor()).addPathPatterns("/home/*"));
}
}
#Component
public class ProductServiceInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(
HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
#Override
public void postHandle(
HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception exception) throws Exception {}
}

Custom Spring Security Filter Test with MockMvc always returns 404

I am trying to test a custom filter using MockMvc and it always returns status 404 instead of the actual status returned from the filter class.
The filter class looks as follows:
public class CustomFilter extends GenericFilterBean {
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
// some logic
chain.doFilter(servletRequest, servletResponse);
}
}
This is registered in with the SpringBootApplication using FilterRegistrationBean
#SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
try {
ConfigurableApplicationContext ctx = SpringApplication.run(MyApplication.class, args);
} catch (Throwable e) {
}
#Bean
public FilterRegistrationBean<CustomFilter> customFilter() {
FilterRegistrationBean<CustomFilter> filterRegistration = new FilterRegistrationBean<>();
filterRegistration.setFilter(new CustomFilter());
filterRegistration.addUrlPatterns("/test/*");
return filterRegistration;
}
}
The test for this is written as follows:
#RunWith(SpringRunner.class)
#SpringBootTest(CustomFilter.class)
#EnableAutoConfiguration
#WebAppConfiguration
public class CustomFilterTest {
#Autowired
private CustomFilter filter;
#Autowired
private WebApplicationContext context;
private MockMvc mvc;
#Before
public void setup() {
this.mvc = MockMvcBuilders.webAppContextSetup(context)
.addFilters(filter)
.build();
}
#Test
public void testCustomFilter() throws Throwable {
MockHttpServletRequestBuilder request = MockMvcRequestBuilders
.request(HttpMethod.POST, "/resource/abc")
.header("SomeHeader", "xxx")
.content("{}");
MockHttpServletResponse response = mvc.perform(request).andReturn().getResponse();
assertEquals(response.getStatus(), HttpServletResponse.SC_CONTINUE);
}
}
The assertion always returns the status value of 404.
Am I missing something here?
Add urlPatterns to the addFilter(filter, "/resource/abc") and add a test controller for the above code snippet as follows:
#RestController
public class TestController {
#PostMapping("/resource/abc")
public void testSecureEndpoint() {
}
}

Do we have provision for interceptor chaining in Spring?

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{
}

Logging interceptor is not working

The issue is that it seems like interceptor is not called.
#Component
public class LoggingInterceptor extends HandlerInterceptorAdapter {
private static final Logger LOGGER = LogManager.getLogger(MethodHandles.lookup().lookupClass());
#Overridegre
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object object, Exception arg3)
throws Exception {
LOGGER.info("Request Completed!");
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object object, ModelAndView model)
throws Exception {
LOGGER.info("Method executed");
}
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object) throws Exception {
LOGGER.info("Before process request");
return true;
}
}
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Autowired
LoggingInterceptor loggingInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loggingInterceptor);
}
}
I've found examples but they are not working !
I'm trying to add start and end log and also performance log. Any idea please ?
#SpringBootApplication(scanBasePackages = { "com.sofelite.proj.controllers" })
public class ProjApplication extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(GrentyApplication.class);
}
public static void main(String[] args) throws Exception {
SpringApplication.run(GrentyApplication.class, args);
}
}
This is the Application class
Please note that in com.sofelite.proj I have all application packages such as controllers and interceptors.
Mine is working:
#Configuration
public class LoggingConfiguration extends WebMvcConfigurerAdapter {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoggingInterceptor());
}
}
and the LoggingInterceptor class:
#Component
public class LoggingInterceptor extends HandlerInterceptorAdapter {
private static final Logger LOGGER =
LoggerFactory.getLogger(LoggingInterceptor.class);
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler)
throws Exception {
long startTime = System.currentTimeMillis();
LOGGER.info("Request URL: " + request.getRequestURL());
LOGGER.info("Start Time: " + System.currentTimeMillis());
request.setAttribute("startTime", startTime);
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView) throws Exception {
StringBuilder sb = new StringBuilder();
sb.append("!Status: "+response.getStatus()+"\n");
sb.append("!URL: "+ request.getRequestURL());
sb.append("!Method: " + request.getMethod() + "\n");
LOGGER.info(sb.toString());
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
long endTime = System.currentTimeMillis();
System.out.println("URL Request Completed. End Time: "+ endTime);
}
}

Resources