2016-04-19 21 views
0

Spring SecurityとCSRFの保護を実装する際に、マルチパートファイルのアップロードで特に問題が発生しました。サーブレットフィルタでMaxUploadSizeExceededExceptionを処理すると、リダイレクトでフラッシュメッセージを設定する方法は?

まず、問題の原因は、CSRFを有効にしてもファイルをアップロードできなくなったことです(実際にはSpring SEcurityでデフォルトで有効になっています)。この問題については、いくつかのスレッドを読んでいます。 )。 マルチパートリクエストを使用する場合、CSRFトークンは送信されないため、Spring Securityフィルタの前にMultipartFilterを指定する必要があります。春のセキュリティのドキュメント(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は、デフォルトの名前「filterMultipartResolver」を使用してルートアプリケーションコンテキストでMultipartResolverを検索してマルチパートリクエストを解決するServletFilterです。今、ファイルのアップロードが期待通りに再び仕事をしましたが、少し後で、私はMaxUploadSizeExceededExceptionのときにスローされることが判明

// 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; 
    } 

だから次の宣言は、変更する必要があったとのAppConfigクラスにMvcConfigから移動しましたファイルが大きすぎますが、私のExceptionHandlingControllerAdviceクラスの@ExceptionHandlerメソッドではもう処理されませんでしたが、代わりにTomcatの醜いHTTP 500ステータスページに返されました。

理由は、MultipartFilterのために、要求がDispatcherServletに到達する前に例外がスローされるため、ControllerAdviceは決して呼び出されません。

そして、私はキャッチして、例外を処理し、フィルター・チェーンにそれを置くために、専用のフィルターを実装の問題と解決策のこのカスケードを継続するには、この問題について、さまざまな記事を読んだ後:

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メソッドにエラーフラッシュメッセージを設定する機能がないことです。 は、元@ExceptionHandler法では、これは次のようにdonwた:

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を返します。

したがって、リダイレクトを実行する直前にセッション内で手動でフラッシュメッセージを設定して表示されますが、セッションに残っているので、リダイレクトの終了後に明示的に削除する必要があります。これは私にとっては悪い習慣と思われますが、私はもっと良い解決策をまだ見つけていません。 SpringSecurityをCSRF保護で使用した後、問題のカスケードがかなり時間がかかりましたが、フラッシュメッセージを設定するためのより良いソリューションがあることを願っています。

答えて

0

フラッシュメッセージが表示されない問題が原因の使用にはおそらくです:ドキュメントによると

FlashMap outputFlashMap = RequestContextUtils.getOutputFlashMap(req); 

、RequestContextUtilsUtilityがで設定されているリクエスト固有の状態に簡単にアクセスするための」クラスでありますDispatcherServlet」を参照してください。 DispatcherServletに到達する前に例外を処理するため、getOutputFlashMapメソッドはおそらくnullを返します。

次のコードを使用すると、フラッシュメッセージが生成されます。

FlashMapManager flashMapManager = new SessionFlashMapManager(); 
FlashMap fm = new FlashMap(); 
fm.put(KeyConstants.FLASH_ERROR_KEY, "File is too large"); 
flashMapManager.saveOutputFlashMap(fm, req, res); 
関連する問題