【问题标题】:Exception when multipart form POST interrupted by session timeout results in an当多部分表单 POST 被会话超时中断时的异常导致
【发布时间】:2011-05-26 15:02:05
【问题描述】:

我们有 CAS 的 Spring 安全性(我认为 CAS 不是问题)。

问题不是会话超时,而是如何处理这个超时。

  1. 请求表单:GET /someform
  2. 填写多部分表单
  3. 重启服务器,或者删除JSESSIONID
  4. 提交表单:POST /someform (with multipart data)

  5. 用户被重定向到登录屏幕

  6. 登录后用户重定向到表单:GET /someform
  7. spring 尝试重新发布保存的表单(我认为它正在使用 DefaultSavedRequest)
  8. 它尝试调用映射到的控制器函数:POST /someform,但请求不是多部分的
  9. 我们得到一个异常:

调用处理方法失败 [上市 org.springframework.web.servlet.ModelAndView com.xxx.xxx.XXXController.xxxPost(org.springframework.web.multipart.MultipartHttpServletRequest)]; 嵌套异常是 java.lang.IllegalStateException: 当前请求不是类型 org.springframework.web.multipart.MultipartHttpServletRequest: com.secondmarket.web.UrlLowerCaseFilter$LowerCaseUrlServletRequestWrapper@77fb58b6

这是在 AccessDeniedException 时将请求保存到会话的代码,它位于 HttpSessionRequestCache(由 ExceptionTranslationFilter 调用)中:

public void saveRequest(HttpServletRequest request, HttpServletResponse response) {
    if (!justUseSavedRequestOnGet || "GET".equals(request.getMethod())) {
        DefaultSavedRequest savedRequest = new DefaultSavedRequest(request, portResolver);

        if (createSessionAllowed || request.getSession(false) != null) {
            // Store the HTTP request itself. Used by AbstractAuthenticationProcessingFilter
            // for redirection after successful authentication (SEC-29)
            request.getSession().setAttribute(WebAttributes.SAVED_REQUEST, savedRequest);
            logger.debug("DefaultSavedRequest added to Session: " + savedRequest);
        }
    }

}

如果是多部分请求,我如何编写 HttpSessionRequestCache 或 ExceptionTranslationFilter 以不保存请求?

【问题讨论】:

    标签: java spring spring-security session-timeout multipartform-data


    【解决方案1】:

    通过修改控制器方法签名解决了这个问题。

    以前,MultipartHttpServletRequest 在方法签名中。当 spring 从登录往返返回时,它尝试使用常规 HttpServletRequest 调用此方法,但失败了。

    @RequestMapping(value = "/xxx", method = RequestMethod.POST)
    public ModelAndView doAmlCheckPost(MultipartHttpServletRequest req) {
        UserInfo currentUserInfo = UserInfo.getCurrentUserInfo(req);
    
        MultipartFile someFile = req.getFile("someFile");
    

    解决方法是使用常规 RequestMapping,并从 URL 中提取文件。如果请求不是 MultipartHttpServletRequest 的 instanceof - 重定向到 GET 方法,该方法重新显示表单

    @RequestMapping(value = "/xxx", method = RequestMethod.POST)
    public ModelAndView doAmlCheckMultipartPost(HttpServletRequest req, @RequestParam(value = "someFile", required = false) MultipartFile someFile) {
    
        if(!(req instanceof MultipartHttpServletRequest)){
            return "redirect:/xxx";
        }
    

    所以现在的流程如下:

    1. 请求表单:GET /someform
    2. 填写多部分表单
    3. 重启服务器,或者删除JSESSIONID
    4. 提交表单:POST /someform(带有多部分数据)这里发生的情况是请求已保存在会话中,但无法保存多部分数据,它是二进制文件,无法序列化
    5. 用户被重定向到登录屏幕
    6. 登录后用户被重定向到表单:GET /someform
    7. spring 尝试重新发布保存的表单(我认为它使用的是 DefaultSavedRequest) SPRING 现在用 HttpServletRequest 调用 POST 方法,而不是 MultipartHttpServletRequest。我们检测到它不是 MultipartHttpServletRequest 并重定向到 GET 页面,将表单重新显示给用户

    【讨论】:

      最近更新 更多