0
votes

Erreur de demande incorrecte de près de 50% http 400 sur Spring Boot 2.1 MultipartFile - téléchargement de fichier lors de l'activation de SSL (https)

J'ai un serveur API qui a été construit avec Spring Boot 2.1 sur le domaine public qui sert également les API et le téléchargement de fichiers.

Ces derniers jours, nous souhaitons mettre à niveau ce serveur Spring Boot pour utiliser SSL (https). Avant de configurer les paramètres SSL dans Spring Boot. L'API pour le téléchargement de fichiers fonctionne très bien (100% de téléchargement réussi).

Après avoir configuré les paramètres SSL dans Spring Boot. L'API pour le téléchargement de fichiers fonctionne mais seulement 50% du téléchargement réussi, les autres 50% ont reçu une mauvaise requête http 400. (Nous sommes sûrs que le problème n'est pas lié au Web frontal, car nous utilisons le Swagger qui fourni avec Spring Boot pour tester peut obtenir le même résultat)

Et nous recherchons les journaux du serveur de Spring Boot. Lorsque la mauvaise requête http 400 se produit, il n'y avait aucun journal concernant la mauvaise requête http 400. Nous étudions plusieurs jours et sondons sur Internet mais ne pouvons toujours pas résoudre ce problème. Veuillez donner de l'aide.

Nous essayons déjà de désactiver csrf (soit dans le fichier de propriétés ou via la classe de configuration) et de nombreuses autres solutions fournies sur Internet mais qui ne fonctionnent toujours pas.

Environnement: Spring Boot 2.1.13 (qui est la dernière version de Spring Boot 2.1)

Paramètres dans le fichier de propriétés: (seulement la section de configuration SSL ajoutée dans le fichier de propriétés, et le SSL (https) est activé avec succès)

HttpEntityMethodProcessor : No match for [application/json;charset=UTF-8], supported: []
ExceptionHandlerExceptionResolver : Resolved [org.springframework.web.multipart.support.MissingServletRequestPartException: Required request part 'file' is not present]
DispatcherServlet : Completed 400 BAD_REQUEST

Mon contrôleur pour le téléchargement de fichiers:

Request URL: https://example.com:8443/v1/fileupload/dataimport
Request Method: POST
Status Code: 400 
Remote Address: 111.222.111.222:8443
Referrer Policy: no-referrer-when-downgrade

**Response http header from Spring Boot**
Connection: close
Content-Length: 0
Date: Mon, 20 Apr 2020 13:13:02 GMT

**Request http header from Swagger**
accept: application/json;charset=UTF-8
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-TW,zh;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6,ja;q=0.5
Connection: keep-alive
Content-Length: 484098
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryiXmuHnaNthhXowmb
Cookie: _ga=GA1.2.1299976434.1580821082; JSESSIONID=2C157019D6560405CC75A5F5083DE0AE
Host: example.com:8443
Origin: https://example.com:8443
Referer: https://example.com:8443/swagger-ui.html
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.113 Safari/537.36

Résultat du test Swagger:

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.SwaggerDefinition;
import io.swagger.annotations.Tag;
import java.util.Collections;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

@Slf4j
@RestController
@RequestMapping(value = "/v1/fileupload")
@Api(tags = {"fileupload api"}, value = "fileupload")
@SwaggerDefinition(tags = {
    @Tag(name = "fileupload api", description = "apis for file upload")
})
public class FileUploadController {

    @Autowired
    private FileUploadService fileUploadService;

    private ApiUtilHelper helper = new ApiUtilHelper();

    @ApiOperation(value = "upload single data import file")
    @RequestMapping(
        value = "/dataimport",
        method = RequestMethod.POST,,
        consumes = { MediaType.MULTIPART_FORM_DATA_VALUE },
        produces = { MediaType.APPLICATION_JSON_UTF8_VALUE }
    )
    public ResponseEntity<?> uploadSingleFileForDataImport(@RequestParam("file") MultipartFile file) throws FileStorageException {
        log.info("Enter into uploadSingleFileForDataImport");
        FileUploadResponse fileUploadResponse = fileUploadService.storeFile(file, "dataImport");
        Map<String, Object> additionals = Collections.singletonMap("filupload", fileUploadResponse);
        BasicResponse br = helper.createSuccessBaseResponse(ApiSuccessCode.CreateSuccess, additionals);
        return new ResponseEntity<BasicResponse>(br, ApiSuccessCode.CreateSuccess.getHttpStatus());
    }

2020.04.20 13:44 (heure UTC) Complément d'information comme ci-dessous: Merci @nbalodi, Quand j'ai configuré logging.level.org.springframework.web = DEBUG. J'ai maintenant le journal des erreurs. Journaux joints comme ci-dessous:

# SSL setup
server.ssl.key-store=classpath:keystore.p12
server.ssl.key-store-password=abcdef
server.ssl.key-store-type=PKCS12
server.ssl.key-alias=tomcat
server.ssl.enabled-protocols=TLSv1.1,TLSv1.2
server.http2.enabled=true
security.basic.enabled=false
security.enable-csrf=false

## MULTIPART (MultipartProperties)
# Enable multipart uploads
spring.servlet.multipart.enabled = true
# Threshold after which files are written to disk.
spring.servlet.multipart.file-size-threshold=2KB
# Max file size.
spring.servlet.multipart.max-file-size=100MB
# Max Request Size
spring.servlet.multipart.max-request-size=115MB

La partie étrange est que cette situation d'erreur ne se produit que lorsque nous utilisons les paramètres SSL comme je l'ai décrit ci-dessus.


1 commentaires

salut @Tina. Je suis également confronté au même problème. Utilisez-vous également Spring Auth dans votre projet? Parce que sans SpringAuth, ma requête en plusieurs parties fonctionne correctement, mais lorsque je commence à utiliser Auth, la requête renvoie une erreur 400


3 Réponses :


0
votes

Est-il possible de changer votre niveau de journalisation en DEBUG? ie logging.level.org.springframework.web = DEBUG.


2 commentaires

Merci @nbalodi, quand j'ai configuré logging.level.org.springframework.web = DEBUG. J'ai maintenant le journal des erreurs. Journaux joints comme ci-dessous: HttpEntityMethodProcessor: Aucune correspondance pour [application / json; charset = UTF-8], pris en charge: [] ExceptionHandlerExceptionResolver: Resolved [org.springframework.web.multipart.support.MissingServletReq‌ uestPartException file: Obligatoire n'est pas présent] DispatcherServlet: Terminé 400 BAD_REQUEST La partie étrange est que cette situation d'erreur ne se produit que lorsque nous utilisons les paramètres ssl comme je l'ai décrit ci-dessus.


@Tina visite ce fil, il semble avoir le même problème que vous rencontrez. stackoverflow.com/questions/31178160/...



0
votes

Essayez ceci peut vous aider. Impossible de voir tout autre problème avec votre code.

@RequestParam("file") MultipartFile[] submissions

devrait être

@RequestBody MultipartFile[] submissions

Les fichiers ne sont pas le corps de la demande, ils en font partie et il n'y a pas de HttpMessageConverter qui puisse convertir la demande en un tableau de MultiPartFile .

Vous pouvez également utiliser MultipartHttpServletRequest , qui vous donne accès aux en-têtes des différentes parties.


0 commentaires

1
votes

Merci à tous la réponse. Je pense que c'est le bogue de SpringBoot ou son bogue intégré Tomcat dans la version SpringBoot v2.1.x. Lorsque la version officielle de SpringBoot v2.3.0 est sortie. J'utilise le même code pour passer à la v2.3.0 Tout est maintenant réalisable. J'utilise un test en masse ou appelé test de pression dans le téléchargement de fichiers. C'est 100% réussi maintenant.

Mise à jour: La cause première est que le composant Tomcat n'est pas stable avec le framework Spring - Multipart sous la sécurité https et / ou http2 ou Spring (comme OAuth2) activée.

Dans la plupart des cas, dans Spring Boot 2.3.0 - 2.3.2, le taux d'échec du téléchargement de fichiers est de 4 ~ 14 ~ 50%. Après la version Spring Boot 2.3.5 à 2.4.0, le taux d'échec du téléchargement de fichiers est proche de 50%. La clé différente est la version Tomcat et la version Spring Security est différente parmi ces versions Spring Boot.

Un autre problème est que le protocole http2 pris en charge par Tomcat n'est pas stable. Si vous activez http2 (par exemple, configurez le serveur.http2.enabled = true dans votre fichier de propriétés) et utilisez Multipart ou HttpServletRequest pour le téléchargement de votre fichier, le taux d'échec passe à près de 90% et la connexion est établie.

Conclusion: pour toute personne confrontée au problème de téléchargement de fichier instable et obtenir ERR_CONNECTION_CLOSED et trouver le journal des erreurs sur

org.apache.catalina.connector.ClientAbortException: org.apache.coyote.CloseNowException: connexion [{0}], flux [{1}], ce flux n'est pas accessible en écriture

Causé par: org.apache.coyote.CloseNowException: connexion [{0}], flux [{1}], ce flux n'est pas accessible en écriture

Causé par: org.apache.coyote.http2.StreamException: connexion [{0}], flux [{1}], ce flux n'est pas accessible en écriture

Vous pouvez essayer de mettre à niveau vers Spring Boot 2.4.0 et désactiver la prise en charge http2 de Tomcat dans Spring Boot en configurant le serveur.http2.enabled = false dans votre fichier de propriétés

Vous constaterez peut-être que la stabilité est améliorée même à un taux de réussite de 100%. (J'ai essayé et mis en œuvre à tous mes clients)

Si la désactivation de la prise en charge de http2 ne fonctionne pas pour vous. Vous pouvez essayer de revenir à la version Spring Boot 2.3.0 - 2.3.2 ou d'implémenter votre téléchargement de fichier par HttpServletRequest (n'oubliez pas de désactiver le Multipart dans Spring Boot en premier)

Vous pouvez trouver des documents de développement associés ici et


0 commentaires