在实施Spring Security和CSRF保护时,出现了一些新问题,尤其是多部分文件上传问题。阅读有关此问题的几个主题,我遇到了以下问题的以下解决方案:
首先,问题的原因是在启用CSRF后上传文件不再有效(实际上在Spring SEcurity中默认启用)。 因为在使用多部分请求时不发送CSRF令牌,所以必须在Spring Security过滤器之前指定MultipartFilter。 根据Spring Security(http://docs.spring.io/spring-security/site/docs/4.0.4.RELEASE/reference/htmlsingle/#csrf-multipartfilter)的文档,这可以通过在SecurityWebApplicationInitializer类中实现 beforeSpringSecurityFilterChain 方法来完成:
public class SecurityWebApplicationInitializer extends
AbstractSecurityWebApplicationInitializer {
@Override
protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {
// TODO Auto-generated method stub
insertFilters(servletContext, new MultipartFilter());
}
}
这不会立即起作用,而是抛出异常,经过一些研究后,似乎已配置的 MultipartFilter 是一个ServletFilter,通过在根应用程序上下文中查找MultipartResolver来解析多部分请求。默认名称“filterMultipartResolver”。
因此,必须修改以下声明并将其从MvcConfig移至AppConfig类:
// was moved from MvcConfig in to fix multipart exception
//THe bean MUST be has an id of filterMultipartResolver to be picked up by the
//MultipartFilter configured in the beforeSpringSecurityFilterChain
//(see SecurityWebApplicationInitializer)
@Bean(name="filterMultipartResolver")
public CommonsMultipartResolver multipartResolver() {
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
multipartResolver.setMaxUploadSize(2000000);
multipartResolver.setMaxInMemorySize(500000);
return multipartResolver;
}
现在文件上传按预期再次运行但稍后我发现当文件太大时抛出的MaxUploadSizeExceededException不再由我的ExceptionHandlingControllerAdvice类中的@ExceptionHandler方法处理,而是以丑陋的HTTP返回Tomcat的500状态页面。
原因是,由于MultipartFilter,在请求到达DispatcherServlet之前抛出异常,因此永远不会调用ControllerAdvice。
要继续这一系列问题和解决方法,在阅读有关此问题的各种帖子后,我实现了一个专用的Filter来捕获并处理异常并将其放入过滤器链中:
public class MultipartExceptionHandler extends OncePerRequestFilter {
private static Logger logger = LoggerFactory.getLogger(MultipartExceptionHandler.class);
@Override
protected void doFilterInternal(HttpServletRequest req,
HttpServletResponse res, FilterChain filterChain)
throws ServletException, IOException {
try {
filterChain.doFilter(req, res);
} catch(MaxUploadSizeExceededException me) {
handle(req, res, me);
} catch(ServletException se) {
if (se.getRootCause() instanceof MaxUploadSizeExceededException) {
handle(req, res, (MaxUploadSizeExceededException) se.getRootCause());
} else {
throw se;
}
}
}
private void handle(HttpServletRequest req, HttpServletResponse res,
MaxUploadSizeExceededException me) throws ServletException, IOException {
// TODO Auto-generated method stub
logger.info("MaxUploadSizeExceededException is handled in custom filter");
String redirect = UrlUtils.buildFullRequestUrl(req);
req.getSession().setAttribute(KeyConstants.FLASH_ERROR_KEY, "File is too big!");
res.sendRedirect(redirect);
}
}
将MultipartExceptionHandler过滤器添加到过滤器链中,方法是将其添加到insertFilters方法中:
@Override
protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {
// TODO Auto-generated method stub
insertFilters(servletContext, new MultipartExceptionHandler(), new MultipartFilter());
}
现在,MaxUploadSizeExceededException确实被过滤器捕获,并显示重定向的页面。 但是,问题是response.sendRedirect方法没有设置错误flash消息的功能。 在最初的@ExceptionHandler方法中,这是以下列方式进行的:
RedirectView rv = new RedirectView(redirectUrl);
FlashMap outputFlashMap = RequestContextUtils.getOutputFlashMap(req);
if (outputFlashMap != null) {
outputFlashMap.put(KeyConstants.FLASH_ERROR_KEY, "File is too large");
}
return rv;
但是在新的异常处理程序过滤器中,这不起作用。对RequestContextUtils.getOutputFlashMap(req)的调用返回null。
因此,我在执行重定向之前在会话中手动设置了Flash消息,现在它已显示,但它仍保留在会话中,因此必须在完成重定向后在某处明确删除。这对我来说似乎是不好的做法,但我还没有找到更好的解决方案。 在开始使用带有CSRF保护的SpringSecurity之后,经历了相当长时间的一系列问题,但我希望有更好的解决方案来设置flash消息。
答案 0 :(得分:0)
不显示Flash消息的问题可能是由于使用:
FlashMap outputFlashMap = RequestContextUtils.getOutputFlashMap(req);
根据文档,RequestContextUtilsUtility是一个类"用于轻松访问由DispatcherServlet设置的特定于请求的状态。" 因为我们在到达DispatcherServlet之前处理异常,所以getOutputFlashMap方法可能返回null。
使用以下代码时,会生成flash消息!
FlashMapManager flashMapManager = new SessionFlashMapManager();
FlashMap fm = new FlashMap();
fm.put(KeyConstants.FLASH_ERROR_KEY, "File is too large");
flashMapManager.saveOutputFlashMap(fm, req, res);