Spring doesn't add interceptors to SimpleUrlHandlerMapping - spring

I am adding inceprtors by exposing a bean in a following way:
#Bean
public WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurer() {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(cacheClearanceInterceptor()).addPathPatterns("/*")
.order(OFFSET + 1);
registry.addInterceptor(pageNotFoundInterceptor()).addPathPatterns("/*")
.excludePathPatterns("/static/*").order(OFFSET + 2);
}
};
}
I am using SimpleUrlHandlerMapping for resolving url to handler methods. However, I don't see above interceptors added. I was expecting spring will add interceptors but it's not and request hits to handler method directly without interceptors in the middle. Do I need to call setInteceptors method manually to register inceptors on SimpleUrlHandlerMapping ?

Related

Camel REST and Spring Security Java Configuration

I'm trying to set up Camel REST to use basic auth with a simple username/password from my application.properties and can't for the life of me seem to configure Camel Spring Security to do that. I'm trying to follow the Spring Security component documentation which seems to be missing the example of configuring the required beans. I found the missing example here under 'Controlling access to Camel routes' but this only shows the xml configuration.
How do I set up the required SpringSecurityAuthorizationPolicy bean? It needs an AuthenticationManager and an AccessDecisionManager and it also seems to require that I set its SpringSecurityAccessPolicy which I have no idea how to do.
I haven't gotten to test these yet, because I can't get my beans set up, but my rest route looks like:
rest("/ingest")
.post("/json").consumes("application/json")
.route()
.process(authProcessor)
.policy(authPolicy) // this is the bean I don't know how to configure
.to("direct:ingest")
.endRest();
and my AuthProcessor (taken from the camel component doc) looks like:
#Component
public class AuthProcessor implements Processor {
public void process(Exchange exchange) {
String userpass = new String(Base64.decodeBase64(exchange.getIn().getHeader("Authorization", String.class)));
String[] tokens = userpass.split(":");
// create an Authentication object
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(tokens[0], tokens[1]);
// wrap it in a Subject
Subject subject = new Subject();
subject.getPrincipals().add(authToken);
// place the Subject in the In message
exchange.getIn().setHeader(Exchange.AUTHENTICATION, subject);
}
}
and here's my broken bean configuration for what it's worth:
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
public SpringSecurityAuthorizationPolicy springSecurityAuthorizationPolicy(
AuthenticationManager authenticationManager, AccessDecisionManager accessDecisionManager) {
SpringSecurityAuthorizationPolicy policy = new SpringSecurityAuthorizationPolicy();
SpringSecurityAccessPolicy springSecurityAccessPolicy = new SpringSecurityAccessPolicy();
policy.setAuthenticationManager(authenticationManager);
policy.setAccessDecisionManager(accessDecisionManager);
policy.setSpringSecurityAccessPolicy(????);
return policy;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("user").password("pass").roles("USER");
}
#Bean(name = BeanIds.AUTHENTICATION_MANAGER)
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public AccessDecisionManager accessDecisionManager() {
AffirmativeBased affirmativeBased = new AffirmativeBased(ImmutableList.of(
new RoleVoter()
));
affirmativeBased.setAllowIfAllAbstainDecisions(true);
return affirmativeBased;
}
}
I've been banging my head against the wall trying to understand this so an example of how to do this would be amazing. It looks like the xml configuration for what I want to do (in the second link) is simple enough but I can't seem to replicate it in Java configuration.
I know it's an old topic, but I ran into similar questions. I managed to get it working. Not by overriding the accessDecisionManager() method within the WebSecurityConfigurerAdapter class, but by constructing a new instance while building my SpringSecurityAuthorizationPolicy:
#Bean
public Policy adminPolicy(AuthenticationManager authenticationManager) {
RoleVoter roleVoter = new RoleVoter();
SpringSecurityAuthorizationPolicy policy = new SpringSecurityAuthorizationPolicy();
policy.setAuthenticationManager(authenticationManager);
policy.setAccessDecisionManager(new UnanimousBased(List.of(roleVoter)));
policy.setSpringSecurityAccessPolicy(new SpringSecurityAccessPolicy(roleVoter.getRolePrefix() + "<ROLE_NAME>");
return policy;
}

Spring Cloud Gateway pass bean to custom filter

We are attempting to use Spring Cloud Gateway to setup a microservice based architecture. Currently, we have defined a route programatically:
#ServletComponentScan
#SpringBootApplication
public class GatewayApplication {
// to be passed to and used by custom filter
#Autowired
RestTemplate restTemplate;
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
#Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("status", r -> r
.method(HttpMethod.GET)
.and()
.path("/status")
.filters(f -> f.rewritePath("/status", "/v2/status")
.filter(new AuthorizationFilter(restTemplate).apply(new Config(""))))
.uri("http://localhost:8081/"))
.build();
}
}
The above would route an incoming request /status via GET to another endpoint. We would like to apply a custom filter, which we have implemented in AuthorizationFilter. This filter, as the name implies, is another microservice which will either allow or deny an incoming request based on credentials and permissions.
Currently, the pattern we are following, which works, is to inject a Spring RestTemplate into the gateway class above, and then to pass this RestTemplate to the constructor of the filter.
However, how can this be done if we wanted to switch to using a YAML file for defining all the routes? Presumably in both cases Spring would be constructing a new filter for each incoming request. But in the case of YAML, how can we pass something in the construtor? If this cannot be done, is there any other way to inject a RestTemplate, or any other resource into a custom Spring gateway filter?
You can register your own custom GatewayFilterFactory. This allows you to provide a custom configuration, and within that configuration, you can use SpEL to reference a bean.
For example:
#Component
public class AuthenticationGatewayFilterFactory extends AbstractGatewayFilterFactory<AuthenticationGatewayFilterFactory.Config> {
public AuthenticationGatewayFilterFactory() {
super(Config.class);
}
#Override
public GatewayFilter apply(Config config) {
// TODO: Implement
}
public static class Config {
private RestTemplate restTemplate;
// TODO: Getters + Setters
}
}
Now you can use SpEL to properly reference a RestTemplate bean:
spring:
cloud:
gateway:
routes:
- id: status
uri: http://localhost:8081/
filters:
- name: Authentication
args:
restTemplate: "#{#nameOfRestTemplateBean}"
predicates:
- Path=/status
Alternatively, you could inject a RestTemplate bean within your gateway filter. For example:
#Component
public class AuthenticationGatewayFilterFactory extends AbstractGatewayFilterFactory<AuthenticationGatewayFilterFactory.Config> {
private RestTemplate restTemplate;
public AuthenticationGatewayFilterFactory(RestTemplate restTemplate) {
super(Config.class);
this.restTemplate = restTemplate;
}
#Override
public GatewayFilter apply(Config config) {
// TODO: Implement
}
public static class Config {
// TODO: Implement
}
}
The code/configuration necessary to do the inject is less complex, but it also makes it more difficult if you ever decide to put AuthenticationGatewayFilterFactory in a separate library, as the "consumers" of this library won't have any control over which RestTemplate is being injected.

How to Create or configure Rest Template using #Bean in Spring Boot

I want to define RestTemplate as an application bean using #Bean annotation in my configuration class in a spring boot application.
I am calling 4 rest services in different places in my application flow. Currently I am creating RestTemplate every time every request. Is there a way I can define that as application bean using #Bean and inject that using #Autowired?
Main reason for this question is I can able to define RestTemplate using #Bean but when I inject it with #Autowired I am loosing all defined interceptors (Interceptors are not getting called.)
Configuration Class
#Bean(name = "appRestClient")
public RestTemplate getRestClient() {
RestTemplate restClient = new RestTemplate(
new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()));
List<ClientHttpRequestInterceptor> interceptors = new ArrayList<ClientHttpRequestInterceptor>();
interceptors.add(new RestServiceLoggingInterceptor());
restClient.setInterceptors(interceptors);
return restClient;
}
Service Class
public class MyServiceClass {
#Autowired
private RestTemplate appRestClient;
public String callRestService() {
// create uri, method response objects
String restResp = appRestClient.getForObject(uri, method, response);
// do something with the restResp
// return String
}
}
It seems my Interceptors are not getting called at all with this configuration. But RestTemplate is able to make a call to the REST service and get a response.
Answer for Spring boot 2.*.* version.
I am using Spring boot 2.1.2.RELEASE and I also added RestTemplate in my project in a class where mail method exists.
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.setConnectTimeout(Duration.ofMillis(300000))
.setReadTimeout(Duration.ofMillis(300000)).build();
}
and Used in my service or other classes like this
#Autowired
RestTemplate res;
and in methods
HttpEntity<String> entity = new HttpEntity<>(str, headers);
return res.exchange(url, HttpMethod.POST, entity, Object.class);
Judging form the name of the interceptor, I'm guessing you're doing some logging in it? You could of missed logging level configuration. I created a small application to check weather your configuration works, using 1.3.6.RELEASE version.
In this class I define the RestTemplate bean and the interceptor with logging.
package com.example;
// imports...
#SpringBootApplication
public class TestApplication {
private static final Logger LOGGER = LoggerFactory.getLogger(TestApplication.class);
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
#Bean(name = "appRestClient")
public RestTemplate getRestClient() {
RestTemplate restClient = new RestTemplate(
new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()));
// Add one interceptor like in your example, except using anonymous class.
restClient.setInterceptors(Collections.singletonList((request, body, execution) -> {
LOGGER.debug("Intercepting...");
return execution.execute(request, body);
}));
return restClient;
}
}
For logging to work, I also have to set the correct debug level in application.properties.
logging.level.com.example=DEBUG
Then I create a service where I inject this RestTemplate.
#Service
public class SomeService {
private final RestTemplate appRestClient;
#Autowired
public SomeService(#Qualifier("appRestClient") RestTemplate appRestClient) {
this.appRestClient = appRestClient;
}
public String callRestService() {
return appRestClient.getForObject("http://localhost:8080", String.class);
}
}
And also an endpoint to test this out.
#RestController
public class SomeController {
private final SomeService service;
#Autowired
public SomeController(SomeService service) {
this.service = service;
}
#RequestMapping(value = "/", method = RequestMethod.GET)
public String testEndpoint() {
return "hello!";
}
#RequestMapping(value = "/test", method = RequestMethod.GET)
public String test() {
return service.callRestService();
}
}
By performing a GET request to http://localhost:8080/test I should expect to get the String hello! getting printed (the service makes a call to http://localhost:8080 which returns hello! and sends this back to me). The interceptor with logger also prints out Intercepting... in the console.
Edd's solution won't work if you're using Spring Boot 1.4.0 or later. You will have to use RestTemplateBuilder to get this working. Here is the example
#Bean(name="simpleRestTemplate")
#Primary
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder){
RestTemplate template = restTemplateBuilder.requestFactory(new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()))
.interceptors(logRestRequestInterceptor) //This is your custom interceptor bean
.messageConverters(new MappingJackson2HttpMessageConverter())
.build();
return template;
}
Now you can autowire the bean into your service class
#Autowired
#Qualifier("simpleRestTemplate")
private RestTemplate simpleRestTemplate;
Hope this helps

How to checkin interceptor whether a controller triggered a redirect

In my Spring MVC project I added an interceptor class, to check, whether a redirect has been triggered.
Here is my controller-class:
#Controller
public class RedirectTesterController {
#RequestMapping (value="/page1")
public String showPage1(){
return "page1";
}
#RequestMapping (value="/submit1")
public String submitPage1(){
return "redirect:/page2";
}
#RequestMapping (value="/page2")
public String showPage2(){
return "page2";
}
}
So if I call e.g.
localhost:8080/MyContext/submit1
the method "submitPage1" is executed.
Now - the server tells the client, to call
localhost:8080/MyContext/page2
which is also working.
So - I want to step into that process, after method "submitPage1"has been executed.
In my mind there should be some order/command in the httpResponse, which I could ask.
To check that, I made a breakpoint in my interceptor class in the method: "postHandle" - bit since then, I have no idea how to continue.
I tried to read the outputStream - but doing so crashes my application. (leads to an exception --> outputStream has already been called..).
Isn't there an easy solution for that ?
Following example shows how to test if a view is a redirect:
#Configuration
public class MvcConfig extends WebMvcConfigurerAdapter {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new HandlerInterceptorAdapter() {
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
if (modelAndView != null && StringUtils.startsWithIgnoreCase(modelAndView.getViewName(), "redirect:")) {
// handle redirect...
}
}
});
}
}
See: HandlerInterceptorAdapter, StringUtils
Spring MVC Documentation: Intercepting requests with a HandlerInterceptor

Java configuration of SimpleUrlHandlerMapping (Spring boot)

I have an existing Spring web application, that uses two controllers, which extend AbstractController. I want to integrate Spring Boot into the application, so that we can run it as a standalone application.
I am facing a problem, because Spring is not forwarding the calls to my controller. How can I map the controller to an URL pattern like "/app/*"?
SampleController.java
#Controller("myController")
public class SampleController extends AbstractController {
#Override
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
response.getWriter().print("Hello world!");
return null;
}
}
Application.java
#EnableAutoConfiguration
#Configuration
#ComponentScan
public class Application {
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
#Bean
public SimpleUrlHandlerMapping sampleServletMapping() {
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
Properties urlProperties = new Properties();
urlProperties.put("/index", "myController");
mapping.setMappings(urlProperties);
return mapping;
}
}
When I start the application I get the following message:
INFO [org.springframework.web.servlet.handler.SimpleUrlHandlerMapping] Mapped URL path [/index] onto handler 'myController'
But when I send a request to /index, I get following messages:
DEBUG [org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping] Looking up handler method for path /index
DEBUG [org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping] Did not find handler method for [/index]
DEBUG [org.springframework.web.servlet.handler.SimpleUrlHandlerMapping] Matching patterns for request [/index] are [/**]
DEBUG [org.springframework.web.servlet.handler.SimpleUrlHandlerMapping] URI Template variables for request [/index] are {}
DEBUG [org.springframework.web.servlet.handler.SimpleUrlHandlerMapping] Mapping [/index] to HandlerExecutionChain with handler [org.springframework.web.servlet.resource.ResourceHttpRequestHandler#11195d3e] and 1 interceptor
SimpleUrlHandlerMappings are ordered and, as described in the javadoc the default is Integer.MAX_VALUE which means that they have the lowest possible precedence. This causes ResourceHttpRequestHandler (which is mapped to /** and has an order of Integer.MAX_VALUE - 1 by default) to take precedence over the mapping for your controller.
Update your sampleServletMapping() method to set your mapping's order to a value that's less than Integer.MAX_VALUE - 1. For example:
#Bean
public SimpleUrlHandlerMapping sampleServletMapping() {
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
mapping.setOrder(Integer.MAX_VALUE - 2);
Properties urlProperties = new Properties();
urlProperties.put("/index", "myController");
mapping.setMappings(urlProperties);
return mapping;
}

Resources