How to test Spring HandlerInterceptor Mapping - spring-mvc

I am implementing a HandlerInterceptor that needs to execute business logic prior/after requests to several paths are handled. I want to test the execution of the Interceptor by mocking the request handle lifecycle.
Here is how the interceptor is registered:
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/test*"/>
<bean class="x.y.z.TestInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
I want to test not only the preHandle and postHandle methods, but the mapping to the path as well.

The following test can be written with the help of JUnit and spring-test:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath*:META-INF/spring.xml", ... })
public class InterceptorTest {
#Autowired
private RequestMappingHandlerAdapter handlerAdapter;
#Autowired
private RequestMappingHandlerMapping handlerMapping;
#Test
public void testInterceptor() throws Exception{
MockHttpServletRequest request = new MockHttpServletRequest();
request.setRequestURI("/test");
request.setMethod("GET");
MockHttpServletResponse response = new MockHttpServletResponse();
HandlerExecutionChain handlerExecutionChain = handlerMapping.getHandler(request);
HandlerInterceptor[] interceptors = handlerExecutionChain.getInterceptors();
for(HandlerInterceptor interceptor : interceptors){
interceptor.preHandle(request, response, handlerExecutionChain.getHandler());
}
ModelAndView mav = handlerAdapter. handle(request, response, handlerExecutionChain.getHandler());
for(HandlerInterceptor interceptor : interceptors){
interceptor.postHandle(request, response, handlerExecutionChain.getHandler(), mav);
}
assertEquals(200, response.getStatus());
//assert the success of your interceptor
}
HandlerExecutionChain is populated with all the mapped interceptors for the specific request. If the mapping is failing, the interceptor will not be present in the list and hence not executed and the assertion at the end will fail.

The Spring Framework provides dedicated support for testing your Spring MVC configuration and components in the form of the Spring MVC Test Framework, available since Spring Framework 3.2 (and as a separate spring-test-mvc project since Spring Framework 3.1).
In the first link you will find numerous examples of how to use the Spring MVC Test framework. In addition, you will also find slides by Rossen Stoyanchev (author of the Spring MVC Test Framework) in our Spring 3.1 and MVC Testing Support and Testing Web Apps with
Spring Framework 3.2 presentations from SpringOne 2GX.
I'm confident those resources should help you get your tests written concisely, but if you still need help, feel free to post back here.
Regards,
Sam (author of the Spring TestContext Framework)

Related

How to create a WebClient-object in a spring application with oauth2

I'm developing a spring application (client) that is secured with an OAuth2 provider. This application should do some REST calls to another spring application (resource server). For performing the REST calls, I will use spring's WebClient.
I therefore try to create a bean of type WebClient as can be found in several blogs.
#Configuration
public class AppConfig {
#Bean
public WebClient webClient(ReactiveClientRegistrationRepository clientRegistrations) {
ServerOAuth2AuthorizedClientExchangeFilterFunction oauth =
new ServerOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrations,
new UnAuthenticatedServerOAuth2AuthorizedClientRepository());
oauth.setDefaultClientRegistrationId("myprovider");
return WebClient.builder().filter(oauth).build();
}
}
When starting the application, I get the following error:
The following candidates were found but could not be injected:
- Bean method 'clientRegistrationRepository' in 'ReactiveOAuth2ClientAutoConfiguration' not loaded because NoneNestedConditions 1 matched 0 did not; NestedCondition on ReactiveOAuth2ClientAutoConfiguration.NonServletApplicationCondition.ServletApplicationCondition found 'session' scope
Action:
Consider revisiting the entries above or defining a bean of type 'org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository' in your configuration.
As several websites recommend exactly this code for generating a WebClient instance when using OAuth2 authentication, I'm wondering what I'm doing wrong?
Do you have any suggestions for me?
Thanks.
I got the same issue. I changed the code as provided in the video : https://www.youtube.com/watch?v=1N-xwmoN83w&t=1569s and that worked
#Bean
public WebClient webClient(ClientRegistrationRepository clientRegistrationRepository , OAuth2AuthorizedClientRepository authorizedClientRepository) {
ServletOAuth2AuthorizedClientExchangeFilterFunction oauth =
new ServletOAuth2AuthorizedClientExchangeFilterFunction (clientRegistrationRepository , authorizedClientRepository);
return WebClient.builder().apply(oauth.oauth2Configuration()).build();
}
Hope that helps.

Struts 1.2.9 to spring MVC migration - Handling Scope of ActionForms

We are migrating a Struts 1.2.9 application to Spring MVC.
We are stuck on one point of scope of ActionForm defined to be "session". By default these are on "request" scope and understand on migrating to Spring, we can reuse these as Model Objects that are set in "request" scope by default.
But am lost on how to handle the "session" scope. Kindly advise.
struts-config.xml
<action path="/editSvc" scope="session"
type="com.xyz.myapp.actions.SvcCodeEditAction" name="svcCodeForm"
validate="false" parameter="reqCode">
<forward name="success" path="/WEB-INF/jsp/svccode_edit.jsp" />
</action>
Action Class
//Code in com.xyz.myapp.actions.SvcCodeEditAction
if (request.equals(mapping.getScope())) {
request.setAttribute(mapping.getAttribute(), form);
} else {
setSessionAttribute(session,mapping.getAttribute(), form);
}
You could get the mostly same functionality by using Spring #Scope("session") annotation above the bean class declaration.
It's well explained in reference guide to spring 3.0 version :
https://docs.spring.io/spring-framework/docs/3.0.0.M3/reference/html/ch04s04.html
In general, if you add a spring-webmvc and spring-web to your project you could use
#Bean
#Scope("session")
public SomeBean someBean() {
return new SomeBean();
}
Or, if you prefer to use xml instead of java config you could go with smth simmilar to this:
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>
Also there is a good tutorial about it:
http://www.baeldung.com/spring-bean-scopes
Then if you add the bean to the model, it will automatically set-up in session if the bean has #SessionScope or #Scope("session") or whenever else bean scope declaration. By default beans would be added to the request scope.

StrictHttpFirewall in spring security 4.2 vs spring MVC #MatrixVariable

Having upgraded to spring security 4.2.4 I discovered that StrictHttpFirewall is now the default.
Unfortunately it doesn't play well with spring MVC #MatrixVariable since ";" are not allowed anymore.
How to get around that?
Example:
#GetMapping(path = "/{param}")
public void example(#PathVariable String param,
#MatrixVariable Map<String, String> matrix) {
//...
}
This could be called like this:
mockMvc.perform(get("/someparam;key=value"))
And the matrix map would be populated.
Now spring security blocks it.
org.springframework.security.web.firewall.RequestRejectedException: The request was rejected because the URL contained a potentially malicious String ";"
at org.springframework.security.web.firewall.StrictHttpFirewall.rejectedBlacklistedUrls(StrictHttpFirewall.java:140)
I could use a custom HttpFirewall that would allow semicolons.
Is there a way to use #MatrixVariable without using forbidden characters?
BTW: the javadoc is incorrect https://docs.spring.io/autorepo/docs/spring-security/4.2.x/apidocs/index.html?org/springframework/security/web/firewall/StrictHttpFirewall.html
Since:
5.0.1
I guess it was backported?
You can dilute the default spring security firewall using your custom defined instance of StrictHttpFirewall (at your own risk)
#Bean
public HttpFirewall allowUrlEncodedSlashHttpFirewall() {
StrictHttpFirewall firewall = new StrictHttpFirewall();
firewall.setAllowUrlEncodedSlash(true);
firewall.setAllowSemicolon(true);
return firewall;
}
And then use this custom firewall bean in WebSecurity (Spring boot does not need this change)
#Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
// #formatter:off
web.httpFirewall(allowUrlEncodedSlashHttpFirewall());
...
}
That shall work with Spring Security 4.2.4+, but of-course that brings some risks!
As mentioned by Крис in a comment if you prefer to use a XML approach, you can add the following part to your securityContext.xml (or whatever your spring-security related xml-config is called):
<bean id="allowSemicolonHttpFirewall"
class="org.springframework.security.web.firewall.StrictHttpFirewall">
<property name="allowSemicolon" value="true"/>
</bean>
<security:http-firewall ref="allowSemicolonHttpFirewall"/>
The <bean> part defines a new StrictHttpFirewall bean with the id allowSemicolonHttpFirewall which is then set as default http-firewall in the <security> tag by referencing the id.
I used combination of following two
https://stackoverflow.com/a/48636757/6780127
https://stackoverflow.com/a/30539991/6780127
First one resolved the
The request was rejected because the URL contained a potentially malicious String ";"
Second one Resolved the
Spring MVC Missing matrix variable
As I am using Spring Security with Spring Web I had to do both And the issue is now Resolved.
I found using #MatrixVariable Following Pattern is useful. First in Url {num} has to be mentioned to use it as #MatrixVariable
#RequestMapping(method = RequestMethod.GET,value = "/test{num}")
#ResponseBody
public ResponseEntity<String> getDetail(#MatrixVariable String num){
return new ResponseEntity<>("test"+num, HttpStatus.OK);
}

Spring Web Flow Conversion Service in JavaConfig

I've started working on a web application in Spring Weblow. The idea is to write as much as possible in Java, rather than XML. So I started off with a JavaConfig file for both the MVC configuration and the Web Flow configuration. But I ran into a problem when needing converters for entering and submitting a form with Spring Web Flow.
I did a lot of research on ConversionService and Converters. I found plenty examples of implementing a custom ConversionService and custom Converters, but I found no examples to to add the ConversionService to the Web Flow configuration in JavaConfig (configuration was always XML).
I did try to reproduce the XML config in Java, which nearly worked. In a form page, a list of POJOs (Employee) was represented as a dropdownlist. The input was List<Employee> and the converter (subclass of StringToObject) worked to represent each Employee as a String. But when submitting the form, I got the error that no converter was found for String to Employee. So basically, the custom converter was found and used when rendering the page, but when submitting the form, the same converter could not be found for the reverse process.
I eventually got it fixed by rolling the JavaConfig back to XML config and adding a custom Formatter to the ConversionService of the MVC config. But I'd like to make this work in JavaConfig if it is at all possible.
I believe the problem is that a ConversionService bean (org.springframework.core.convert package) needs to be added to the MVC config, because this bean needs to be set as a delegate ConversionService in the ConversionService bean to be added to the Web Flow Config (the latter from the org.springframework.binding.convert package). But I don't know how to add this core ConversionService in JavaConfig like in the mvc:annotation-driven tag in the code below.
It all boils down to needing the JavaConfig version of the following code:
<mvc:annotation-driven conversion-service="typeConversionService" ... />
<bean id="typeConversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="formatters">
<list>
<bean class="some.package.holidays.formatter.EmployeeFormatter">
<constructor-arg ref="employeeService"/>
</bean>
<bean class="org.springframework.format.datetime.DateFormatter">
<constructor-arg value="dd/MM/yyyy"/>
</bean>
</list>
</property>
</bean>
If anyone would know about JavaConfig for Spring Webflow, especially about adding a ConversionService, please let me know, it would be a great help.
I had the same thing to do in a project and this is how I did it. I know it might be late for you, but maybe somebody else needs the answer to this:
#Configuration
public class WebFlowConfig extends AbstractFlowConfiguration {
#Autowired
private MvcConfig webMvcConfig;
#Bean
public FlowBuilderServices flowBuilderServices() {
return getFlowBuilderServicesBuilder()
.setViewFactoryCreator(mvcViewFactoryCreator())
.setValidator(this.webMvcConfig.validator())
.setConversionService(conversionService())
.setDevelopmentMode(true)
.build();
}
#Bean
DefaultConversionService conversionService() {
return new DefaultConversionService(conversionServiceFactoryBean().getObject());
}
#Bean
FormattingConversionServiceFactoryBean conversionServiceFactoryBean() {
FormattingConversionServiceFactoryBean fcs = new FormattingConversionServiceFactoryBean();
Set<Formatter> fmts = new HashSet<>();
fmts.add(this.webMvcConfig.dateFormatter());
fmts.add(this.webMvcConfig.employeeFormatter());
fcs.setFormatters(fmts);
return fcs;
} }
I upvoted the accepted answer but would also like to add this. I kept getting the below error.
'conversionService': Requested bean is currently in creation: Is there an unresolvable circular reference?
To fix this, remove conversionService bean like this. (note the setConversionService difference).
#Bean
public FlowBuilderServices flowBuilderServices() {
return getFlowBuilderServicesBuilder()
.setViewFactoryCreator(mvcViewFactoryCreator())
.setValidator(localValidatorFactoryBean)
.setConversionService(new DefaultConversionService(conversionServiceFactoryBean().getObject()))
.setDevelopmentMode(true)
.build();
}
#Bean
FormattingConversionServiceFactoryBean conversionServiceFactoryBean() {
FormattingConversionServiceFactoryBean fcs = new FormattingConversionServiceFactoryBean();
Set<Formatter> fmts = new HashSet<>();
fmts.add(this.webMvcConfig.dateFormatter());
fmts.add(this.webMvcConfig.employeeFormatter());
fcs.setFormatters(fmts);
return fcs;
}

Spring MVC 3.0 with Apache Tiles - Mutiple Forms in one page

I'm using spring mvc (3.0) with apache tiles in my project. I have multiple forms in a single page rendered through tiles.
The login form and search form are common to most pages. The “body” in the tile definition keeps changing.
So, as shown below, in all of my mvc controllers I have to explicitly set command object in the corresponding model.
1. model.put("userBO", userBO);
2. model.put("searchBO", searchBO);
Is there a way I can move this part of the code to a common place or a global controller, so that I don’t have to write these two lines in all the controllers that I write?
You can use an interceptor to do this in a postHandle:
public class DefaultModelInterceptor extends HandlerInterceptorAdapter {
#Override
public void postHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler,
final ModelAndView modelAndView) throws Exception {
modelAndView.addObject("userBO", userBO);
modelAndView.addObject("searchBO", searchBO);
super.postHandle(request, response, handler, modelAndView);
}
}
This can then be wired in your spring servlet config:
<mvc:interceptors>
<bean class="my.package.DefaultModelInterceptor"/>
</mvc:interceptors>

Resources