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

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

Related

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

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 Websocket Configuration: using WebSocketMessageBrokerConfigurationSupport and WebSocketConfigurer together - how?

I am configuring currently my Spring Websocket using the class
public class WebSocketConfig extends WebSocketMessageBrokerConfigurationSupport
now I came across the advice Spring STOMP Websockets: any way to enable permessage-deflate on server side?
that makes use of
public class SampleJettyWebSocketsApplication implements WebSocketConfigurer
and overrides
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry)
and offers
#Bean
public DefaultHandshakeHandler handshakeHandler()
Question, what is the relation between WebSocketConfigurer and WebSocketMessageBrokerConfigurationSupport? In other words, can I possibly somehow add configuration from WebSocketConfigurer implementation via API of the first class, WebSocketMessageBrokerConfigurationSupport, so all configuration remains in one single file?
The WebSocketMessageBrokerConfigurationSupport implementation is DelegatingWebSocketMessageBrokerConfiguration which is configured via #EnableWebSocketMessageBroker. All you need in your custom code is WebSocketMessageBrokerConfigurer implementation. And that one is injected into DelegatingWebSocketMessageBrokerConfiguration:
#Autowired(required = false)
public void setConfigurers(List<WebSocketMessageBrokerConfigurer> configurers) {
This is a sample config from my test-cases:
#Configuration
#EnableWebSocketMessageBroker
static class ServerConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Bean
public DefaultHandshakeHandler handshakeHandler() {
return new DefaultHandshakeHandler(new TomcatRequestUpgradeStrategy());
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws")
.setHandshakeHandler(handshakeHandler())
.setAllowedOrigins("http://foo.com")
.addInterceptors(new HandshakeInterceptor() {
#Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
return request.getHeaders().getOrigin() != null;
}
#Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler wsHandler, Exception exception) {
}
})
.withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry configurer) {
configurer.setApplicationDestinationPrefixes("/app")
.enableSimpleBroker("/topic", "/queue");
}
}

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

Resources