Spring Boot 2.3.4 C'est l'histoire de l'environnement au moment de la sortie.
Spring WebFlux propose plusieurs façons de gérer les requêtes HTTP.
Pour générer des données de réponse HTTP à ce moment
Dans la plupart des cas, vous utiliserez probablement RouterFunction
ou @ Controller
.
ServerRequest
est passé à RouterFunction
, donc si vous appelezServerRequest # bodyToMono (Class <? Extends T>)
Vous pouvez analyser les données JSON du corps de la requête.
Dans Spring Boot, dans le cas de @ Controller
, si l'annotation @ RequestBody
est ajoutée
Il analyse le corps de la requête côté framework.
Dans la plupart des cas, cette méthode peut convertir le JSON du corps de la requête en un objet à utiliser dans l'application.
Cependant, l'authentification personnalisée Spring Security est traitée au stade WebFilter, donc «RouterFunction» etc. sont appelés.
À ce stade, ServerWebExchange
est passé à WebFilter
, mais la méthodeServerWebExchange # getRequest ()
est
Renvoie ServerHttpRequest
.
Puisque ServerHttpRequest
n'a pas de relation d'héritage avec ServerRequest
,
Il n'a pas de fonction pratique comme ServerRequest # bodyToMono (Class <? Extends T>)
.
Vous pouvez obtenir des données de formulaire avec ServerWebExchange # getFormData ()
, mais vous devez analyser vous-même le JSON du corps de la requête.
Pour rendre ce processus d'analyse similaire à Spring Boot, récupérez le ServerCodecConfigurer
avec ʻAutowired Obtenez
ListSE <HttpMessageReader <? >>avec
ServerCodecConfigurer # getReaders (), Le
HttpMessageReader pour analyser le corps de la requête HTTP doit être obtenu avec
Stream # filter ()`, et ainsi de suite.
Si cela est écrit dans ʻorg.springframework.security.web.server.authentication.ServerAuthenticationConverter` de Spring Security, ce sera à peu près comme suit.
import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import javax.naming.AuthenticationException;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.springframework.core.ResolvableType;
import org.springframework.http.MediaType;
import org.springframework.http.codec.HttpMessageReader;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.server.authentication.ServerAuthenticationConverter;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
public class JsonLoginServerAuthenticationConverter implements ServerAuthenticationConverter {
private final List<HttpMessageReader<?>> httpMessageReaders;
public JsonLoginServerAuthenticationConverter(ServerCodecConfigurer serverCodecConfigurer) {
this(serverCodecConfigurer.getReaders());
}
public JsonLoginServerAuthenticationConverter(List<HttpMessageReader<?>> httpMessageReaders) {
this.httpMessageReaders = Objects.requireNonNull(httpMessageReaders);
}
@Override
public Mono<Authentication> convert(ServerWebExchange exchange) {
ServerHttpRequest request = exchange.getRequest();
MediaType contentType = request.getHeaders().getContentType();
MediaType acceptType = MediaType.APPLICATION_JSON;
// Content-Vérification de type.
if (contentType == null || acceptType.isCompatibleWith(contentType)) {
return Mono.error(new AuthenticationException("Invalid Content-Type"));
}
ResolvableType resolvableType = ResolvableType.forClass(JsonParameter.class);
//JsonParameter est le nom d'utilisateur,Une classe qui stocke simplement le mot de passe dans le champ.
//JsonAuthentication est une sous-classe d'authentification qui ne stocke que JsonParameters.
//La définition est omise pour les deux.
return this.httpMessageReaders.stream()
// Content-Type: application/json demande des données à JsonParameter.Peut être converti en classe
//Recherchez une implémentation HttpMessageReader.
.filter(httpMessageReader ->
httpMessageReader.canRead(resolvableType, acceptType))
.findFirst()
.orElseThrow(() -> new IllegalStateException("Could not read JSON data."))
.readMono(resolvableType, request, Collections.emptyMap())
.cast(JsonParameter.class)
.map(JsonAuthentication::new);
}
@JsonIgnoreProperties(ignoreUnknown = true)
static class JsonParameter {
private final String username;
private final String password;
@JsonCreator
public JsonParameter(
@JsonProperty("username") String username,
@JsonProperty("password") String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return this.username;
}
public String getPassword() {
return this.password;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
JsonParameter that = (JsonParameter) o;
return Objects.equals(username, that.username) &&
Objects.equals(password, that.password);
}
@Override
public int hashCode() {
return Objects.hash(username, password);
}
@Override
public String toString() {
return "JsonParameter(" +
"username=" + this.username +
", password=" + this.password +
")";
}
}
}
Pour être honnête, je trouve cette implémentation assez lourde.
E / S asynchrones pour gérer les corps de requête qui ne savent pas quand ils seront envoyés, Parce qu'il n'est pas corrigé avant l'exécution, ce qui sera utilisé pour l'objet qui traite le corps du message dans Spring Boot Je peux comprendre cela.
Cependant, même si vous soustrayez qu'il s'agit d'un traitement de message HTTP unique qui est différent de l'API Servert Ecrire cette procédure à chaque fois semblait très ennuyeux.
Puisque «Mono» est utilisé, il est un peu gênant de préparer une fonction utilitaire pratique.
J'ai pensé, mais un jour j'ai remarqué que la fonction ServerRequest # create (ServerWebExchange, List <HttpMessageReader <? >>)
existe.
La ServerRequest
générée par cette fonction provient de la List <HttpMessageReader <? >>
Donner au traitement du corps du message.
Il récupérera et utilisera le HttpMessageReader
approprié.
Ce en quoi vous voulez convertir le corps du message est simplement de générer BodyExtractor
à partir de la fonction fournie dans BodyExtractors
.
La quantité de code peut être considérablement réduite même avec le code précédent.
La vérification de Content-Type
est laissée au cas où.
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.springframework.http.codec.HttpMessageReader;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.server.authentication.ServerAuthenticationConverter;
import org.springframework.web.reactive.function.BodyExtractors;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
public class JsonLoginServerAuthenticationConverter implements ServerAuthenticationConverter {
private final List<HttpMessageReader<?>> httpMessageReaders;
public JsonLoginServerAuthenticationConverter(ServerCodecConfigurer serverCodecConfigurer) {
this(serverCodecConfigurer.getReaders());
}
public JsonLoginServerAuthenticationConverter(List<HttpMessageReader<?>> httpMessageReaders) {
this.httpMessageReaders = Objects.requireNonNull(httpMessageReaders);
}
@Override
public Mono<Authentication> convert(ServerWebExchange exchange) {
ServerHttpRequest request = exchange.getRequest();
MediaType contentType = request.getHeaders().getContentType();
if (contentType == null || MediaType.APPLICATION_JSON.isCompatibleWith(contentType)) {
return Mono.error(new AuthenticationException("Invalid Content-Type"));
}
return ServerRequest.create(exchange, this.httpMessageReaders)
.body(BodyExtractors.toMono(JsonParameter.class))
.map(JsonAuthentication::new);
}
}
ServerRequest # create (ServerWebExchange, List <HttpMessageReader <? >>)
est une fonctionnalité utile de WebFilter
,
Pas souvent introduit.
Recommended Posts