Comment bien gérer upload de fichier MaxUploadSizeExceededException avec Ressort de Sécurité
Je suis à l'aide de Spring Web en php 4.0.5, le Printemps de Sécurité 3.2.4, Commons FileUpload 1.3.1, Tomcat 7 et je suis une vilaine MaxUploadSizeExceededException
lors de l'upload de la taille limite est dépassée, ce qui résulte en une "500 Internal Server Error". Je l'ai manipuler avec un joli générique de pop-up, mais je préfère avoir mon Contrôleur de prendre soin d'elle en remontant à l'origine de la forme avec le bon message d'explication.
J'ai vu une question similaire a demandé à plusieurs reprises, avec quelques solutions qui pourraient fonctionner lorsque vous n'utilisez pas le Printemps de Sécurité; aucun de ceux que j'ai essayé de travaillé pour moi.
Le problème pourrait être que lors de l'utilisation de Spring Security, la CommonsMultipartResolver
n'est pas ajouté comme un "multipartResolver" bean, mais comme un "filterMultipartResolver":
@Bean(name="filterMultipartResolver")
CommonsMultipartResolver filterMultipartResolver() {
CommonsMultipartResolver filterMultipartResolver = new CommonsMultipartResolver();
filterMultipartResolver.setMaxUploadSize(MAXSIZE);
return filterMultipartResolver;
}
Si j'ai mis filterMultipartResolver.setResolveLazily(true);
il ne fait aucune différence.
Si je sous-classe de la CommonsMultipartResolver
avec mon propre et remplacer le parseRequest()
méthode avec quelque chose que les pièges de la MaxUploadSizeExceededException
et renvoie un vide MultipartParsingResult
, je reçois un "403 Forbidden" erreur:
public class ExtendedCommonsMultipartResolver extends CommonsMultipartResolver {
protected MultipartParsingResult parseRequest(HttpServletRequest request) throws MultipartException {
String encoding = determineEncoding(request);
try {
return super.parseRequest(request);
} catch (MaxUploadSizeExceededException e) {
return parseFileItems(Collections.<FileItem> emptyList(), encoding);
}
}
}
Enfin, il n'y a aucun point dans la mise en œuvre de certains type de local ou global ExceptionHandler
parce qu'il n'est jamais appelée.
Si je n'ai pas trouver une meilleure solution, je vais juste supprimer la taille limite de téléchargement et le gérer moi-même dans le contrôleur, avec l'inconvénient d'avoir à l'utilisateur d'attendre jusqu'à ce que le téléchargement est terminé avant de voir le message d'erreur sur la taille du fichier.
De je peut même ignorer tout cela parce que, étant une image dans ce cas, je pourrais juste redimensionner à la bonne valeurs.
Encore, j'aimerais voir une solution à ce problème.
Merci
EDIT:
- Je ajouter de la trace de la pile comme demandé. C'est le cas où une 500 est généré.
May 30, 2014 12:47:17 PM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet [dispatcher] in context with path [/site] threw exception
org.springframework.web.multipart.MaxUploadSizeExceededException: Maximum upload size of 1000000 bytes exceeded; nested exception is org.apache.commons.fileupload.FileUploadBase$SizeLimitExceededException: the request was rejected because its size (3403852) exceeds the configured maximum (1000000)
at org.springframework.web.multipart.commons.CommonsMultipartResolver.parseRequest(CommonsMultipartResolver.java:162)
at org.springframework.web.multipart.commons.CommonsMultipartResolver.resolveMultipart(CommonsMultipartResolver.java:142)
at org.springframework.web.multipart.support.MultipartFilter.doFilterInternal(MultipartFilter.java:110)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:409)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1044)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:315)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
at java.lang.Thread.run(Thread.java:722)
Caused by: org.apache.commons.fileupload.FileUploadBase$SizeLimitExceededException: the request was rejected because its size (3403852) exceeds the configured maximum (1000000)
at org.apache.commons.fileupload.FileUploadBase$FileItemIteratorImpl.<init>(FileUploadBase.java:965)
at org.apache.commons.fileupload.FileUploadBase.getItemIterator(FileUploadBase.java:310)
at org.apache.commons.fileupload.FileUploadBase.parseRequest(FileUploadBase.java:334)
at org.apache.commons.fileupload.servlet.ServletFileUpload.parseRequest(ServletFileUpload.java:115)
at org.springframework.web.multipart.commons.CommonsMultipartResolver.parseRequest(CommonsMultipartResolver.java:158)
... 19 more
- Merci de poster votre plein stacktraces
- Certains complément d'enquête reveales que, lors de l'utilisation de la seule approche viable qui est le ExtendedCommonsMultipartResolver, le "403 Forbidden" est causée par le csrf fonction de Ressort de Sécurité. Si je désactive avec http.csrf().disable() dans mon WebSecurityConfigurerAdapter, la 403 est pas généré. Bien sûr, je préfère ne pas la désactiver, même partiellement avec un requireCsrfProtectionMatcher() car je ne voudrais pas que mes utilisateurs de télécharger quelqu'un d'autre données, à moins que le csrf truc ne peut pas être joué avec un multipart. La question d'origine tient toujours. Veuillez informer.
- J'ai le sentiment que la question ne peut être résolu, à moins que A) j'ai mis le _csrf sur l'url, comme expliqué ici docs.printemps.io/printemps-sécurité/site/docs/3.2.0.CI-SNAPSHOT/... ou B) j'écris une ExtendedCommonsMultipartResolver que les pièges de la MaxUploadSizeExceededException et répète le parseRequest après la désactivation de la vérification de la taille et d'une certaine façon les drapeaux de l'excès. Option B serait encore charger la totalité du fichier de sorte qu'il est inutile. Je suppose que nous avons vraiment une option.
Vous devez vous connecter pour publier un commentaire.
Vous pouvez gérer la MaxUploadSizeExceededException par l'ajout d'un Filtre supplémentaire pour intercepter l'exception et le rediriger vers une page d'erreur. Par exemple, vous pourriez créer un MultipartExceptionHandler Filtre comme suit:
NOTE: Cette redirection fait une hypothèse à propos de votre formulaire et de le télécharger. Vous devrez peut-être modifier où redirection. Spécifiquement si vous suivez le modèle de votre formulaire à OBTENIR et il est traité dans ce POST va travailler.
Vous pouvez faire en sorte d'ajouter ce Filtre avant MultipartFilter. Par exemple, si vous utilisez web.xml vous verrez quelque chose comme ceci:
Dans votre formulaire, vous pouvez alors détecter si l'erreur s'est produite par l'inspection si le paramètre HTTP erreur est présente. Par exemple, dans une page JSP, vous pouvez effectuer les opérations suivantes:
PS: j'ai créé SEC-2614 pour mettre à jour la documentation pour discuter de la gestion d'erreur
Je sais je suis en retard à la fête, mais j'ai trouvé beaucoup plus élégante solution à mon humble avis.
Au lieu de l'ajout d'un filtre pour le multipart de résolution, il suffit d'ajouter
throws MaxUploadSizeExceededException
sur votre contrôleur de méthode et ajouter le filtre de laDelegatingFilterProxy
dans votreweb.xml
et vous pouvez ajouter un gestionnaire d'exception dans votre contrôleur sans avoir à rediriger la demande.par exemple:
Méthode (Contrôleur):
Gestionnaire d'Exception (en même contrôleur):
Web.xml (merci à Rob Treuil):
Et c'est tout ce dont vous avez besoin.
La chose est
springSecurityFilterChain
doit être ajouté après multipart filtre. C'est pourquoi vous obtenez état 403. Ici:http://docs.spring.io/spring-security/site/docs/3.2.0.CI-SNAPSHOT/reference/html/csrf.html#csrf-multipartfilter
Je pense qu'après vous le faites, vous serez en mesure de prendre
FileUploadBase.SizeLimitExceededException
dans un @ControllerAdvice annoté de la classe contenant@ExceptionHandler
annoté méthodes.La solution je suis venu avec tandis que l'expérimentation est le suivant:
Étendre CommonsMultipartResolver afin d'avaler de l'exception. J'ai ajouter l'exception de la Demande juste au cas où vous souhaitez l'utiliser dans le Contrôleur, mais je ne pense pas que c'est nécessaire
Déclarer votre outil de résolution dans le WebSecurityConfigurerAdapter, à la place de CommonsMultipartResolver (vous devez déclarer un filterMultipartResolver en tout cas, donc rien de nouveau ici)
N'oubliez pas de définir le bon filtre priorité, dans l'AbstractSecurityWebApplicationInitializer comme indiqué dans les docs (vous devez le faire en tout cas)
Ajouter le _csrf jeton à l'action du formulaire URL (je suis en utilisant thymeleaf ici)
Dans le Contrôleur, il suffit de cocher pour nulle sur le MultipartFile, quelque chose comme (extrait pas vérifié pour les erreurs):
De cette façon, vous pouvez utiliser l'habituelle méthode de Contrôleur pour la manipulation de la soumission du formulaire, même en cas de taille dépassait.
Ce que je n'aime pas de cette approche est que vous avez à jouer avec une bibliothèque externe paquet (MultipartParsingResult est protégé) et que vous devez vous rappeler le réglage du jeton sur l'url du formulaire (qui est également moins sûr btw).
Ce que j'aime, c'est que vous manipulez le formulaire de soumission en un seul endroit dans le contrôleur.
Le problème d'un gros fichier téléchargé avant de retourner à l'utilisateur persiste aussi, mais je suppose que c'est abordée ailleurs déjà.