When using Spring Security, the CommonsMultipartResolver will not work. Why? Because the MultipartHttpServletRequest will be wrapped in a SecurityContextHolderAwareRequestWrapper, and will not be matched.
Of course, we don't want to fall back to just taking an HttpServletRequest as a parameter in our RequestMapping and parsing it out, we need to work smarter than that!
The best solution I could come up with is registering a custom WebArgumentResolver (below). But any readers out there have a better solution, please share!
Resolver:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| public class SecurityContextWrappedMultipartRequestArgumentResolver implements WebArgumentResolver { | |
| private final CommonsMultipartResolver commonsMultipartResolver; | |
| public SecurityContextWrappedMultipartRequestArgumentResolver(){ | |
| this.commonsMultipartResolver = new CommonsMultipartResolver(); | |
| } | |
| @Override | |
| public Object resolveArgument(MethodParameter methodParameter, NativeWebRequest webRequest) throws Exception { | |
| if (MultipartHttpServletRequest.class.equals(methodParameter.getParameterType())) { | |
| Object object = webRequest.getNativeRequest(); | |
| if (! (object instanceof SecurityContextHolderAwareRequestWrapper)) { | |
| return UNRESOLVED; | |
| } | |
| HttpServletRequest request = (HttpServletRequest) object; | |
| if (!ServletFileUpload.isMultipartContent(request)) { | |
| return UNRESOLVED; | |
| } | |
| SecurityContextHolderAwareRequestWrapper requestWrapper = (SecurityContextHolderAwareRequestWrapper) request; | |
| return commonsMultipartResolver.resolveMultipart(requestWrapper); | |
| } | |
| return UNRESOLVED; | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| //Then wire up the HandlerAdapter (your config may vary) | |
| @Bean | |
| public HandlerAdapter handlerAdapter() { | |
| final AnnotationMethodHandlerAdapter handlerAdapter = new AnnotationMethodHandlerAdapter(); | |
| handlerAdapter.setCustomArgumentResolver(new SecurityContextWrappedMultipartRequestArgumentResolver()); | |
| handlerAdapter.setAlwaysUseFullPath(true); | |
| List<HttpMessageConverter<?>> converterList = new ArrayList<HttpMessageConverter<?>>(); | |
| converterList.addAll(Arrays.asList(handlerAdapter.getMessageConverters())); | |
| converterList.add(jibxHttpMessageConverter); | |
| converterList.add(gsonHttpMessageConverter); | |
| converterList.add(csvLocalizationConverter); | |
| converterList.add(protobufLocalizationConverter); | |
| handlerAdapter.setMessageConverters(converterList.toArray(new HttpMessageConverter[converterList.size()])); | |
| return handlerAdapter; | |
| } |
Is it possible to find about this problem in Spring documentation?
ReplyDeleteI have found the same problem in my application, but I need more explanation for resolving it.
I use a lot of security filters and found more than one wrapper class around the request. Here's how I did it:
ReplyDelete// peeling wrappers because of Spring Security filters used
ServletRequest originalRequest = request;
MultipartHttpServletRequest multipartRequest = null;
while (originalRequest instanceof ServletRequestWrapper) {
log.debug("Unwrapping " + originalRequest.getClass().getName());
originalRequest = ((ServletRequestWrapper)originalRequest).getRequest();
}
log.debug("Original request type: " + originalRequest.getClass().getName());
if (originalRequest instanceof MultipartHttpServletRequest)
multipartRequest = (MultipartHttpServletRequest)(originalRequest);
else {
multipartRequest = commonsMultipartResolver.resolveMultipart((HttpServletRequest)originalRequest);
}