Senden einer GET-Anforderung mit Authentifizierungsheadern mithilfe von restTemplate

76

Ich muss Ressourcen von meinem Server abrufen, indem ich mithilfe von RestTemplate eine GET-Anforderung mit einigen Autorisierungsheadern sende.

Nachdem ich die Dokumente durchgesehen hatte, bemerkte ich, dass keine der GET-Methoden Header als Parameter akzeptiert und die einzige Möglichkeit, Header wie accept und Authorization zu senden, die Verwendung der Exchange- Methode ist.

Da es sich um eine sehr einfache Aktion handelt, frage ich mich, ob mir etwas fehlt und ob es einen anderen, einfacheren Weg gibt, dies zu tun.

special0ne
quelle

Antworten:

66

Du vermisst nichts. RestTemplate#exchange(..)ist die geeignete Methode zum Festlegen von Anforderungsheadern.

Hier ist ein Beispiel (mit POST, aber ändern Sie dies einfach in GET und verwenden Sie die gewünschte Entität).

Hier ist ein weiteres Beispiel.

Beachten Sie, dass bei einem GET Ihre Anforderungsentität nichts enthalten muss (es sei denn, Ihre API erwartet dies, dies würde jedoch gegen die HTTP-Spezifikation verstoßen). Es kann eine leere Zeichenfolge sein.

Sotirios Delimanolis
quelle
1
Aber was ist, wenn Sie möchten, dass Ihre get request-Methode einem Objekt zugeordnet wird, aber dennoch Header einsenden möchten?
user2725919
50

Sie können postForObjectmit einem verwenden HttpEntity. Es würde so aussehen:

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization", "Bearer "+accessToken);

HttpEntity<String> entity = new HttpEntity<String>(requestJson,headers);
String result = restTemplate.postForObject(url, entity, String.class);

In einer GET-Anfrage würden Sie normalerweise keinen Text senden (dies ist zulässig, hat aber keinen Zweck). Die Möglichkeit, Header hinzuzufügen, ohne die RestTemplate anders zu verkabeln, besteht darin, die Methoden exchangeoder executedirekt zu verwenden. Die get-Kurzschriften unterstützen keine Header-Modifikation.

Die Asymmetrie ist auf den ersten Blick etwas seltsam, vielleicht wird dies in zukünftigen Versionen von Spring behoben.

iwein
quelle
4
restTemplate.postForEntity(url, entity, String.class)funktioniert auch gut.
Abdull
Die Frage bezieht sich auf die GET-Anfrage, aber diese Antwort bezieht sich auf den POST. Es ist irreführend. Es gibt keine getForObjectmit einer solchen Unterschrift.
Zon
26

Hier ist ein supereinfaches Beispiel mit grundlegender Authentifizierung, Headern und Ausnahmebehandlung ...

private HttpHeaders createHttpHeaders(String user, String password)
{
    String notEncoded = user + ":" + password;
    String encodedAuth = "Basic " + Base64.getEncoder().encodeToString(notEncoded.getBytes());
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);
    headers.add("Authorization", encodedAuth);
    return headers;
}

private void doYourThing() 
{
    String theUrl = "http://blah.blah.com:8080/rest/api/blah";
    RestTemplate restTemplate = new RestTemplate();
    try {
        HttpHeaders headers = createHttpHeaders("fred","1234");
        HttpEntity<String> entity = new HttpEntity<String>("parameters", headers);
        ResponseEntity<String> response = restTemplate.exchange(theUrl, HttpMethod.GET, entity, String.class);
        System.out.println("Result - status ("+ response.getStatusCode() + ") has body: " + response.hasBody());
    }
    catch (Exception eek) {
        System.out.println("** Exception: "+ eek.getMessage());
    }
}
Dave
quelle
2
Ich bin mir nicht sicher, warum dies ursprünglich abgelehnt wurde. Wenn Sie als erster abstimmen, können Sie dies gerne in einem Kommentar erklären.
Sander Verhagen
2
Der "Basic" -Teil sollte nicht codiert sein, oder?
Wenneguen
1
Die Methode createHttpHeaders ist leicht falsch. String notEncoded = Benutzer + ":" + Passwort; ... headers.add ("Authorization", "Basic" + encodedAuth);
Milind
Ich habe auch gerade entdeckt, dass "Basic" nicht auch codiert werden sollte. Dies ist ein gutes Beispiel für die Authentifizierung, die ich selbst verwendet habe, aber die Korrektur der Codierung wäre gut.
Tim Holt
Die in den obigen Kommentaren erwähnte
Adriaan Koster
9

Alle diese Antworten scheinen unvollständig und / oder kludges zu sein. Wenn Sie sich die RestTemplate-Oberfläche ansehen, sieht es sicher so aus, als ob eine ClientHttpRequestFactoryInjektion in die Benutzeroberfläche vorgesehen ist. Anschließend wird diese requestFactory zum Erstellen der Anforderung verwendet, einschließlich aller Anpassungen von Headern, Text und Anforderungsparametern.

Sie benötigen entweder ein Universal ClientHttpRequestFactory, um es in eine einzelne Freigabe einzufügen, RestTemplateoder Sie müssen eine neue Vorlageninstanz über abrufen new RestTemplate(myHttpRequestFactory).

Leider ist es nicht trivial, eine solche Factory zu erstellen, selbst wenn Sie nur einen einzelnen Autorisierungsheader festlegen möchten. Dies ist ziemlich frustrierend, wenn man bedenkt, was für eine häufige Anforderung dies wahrscheinlich ist, aber es ermöglicht zumindest eine einfache Verwendung, wenn beispielsweise Wenn Ihr Authorization-Header aus Daten erstellt werden kann, die in einem Spring-Security- AuthorizationObjekt enthalten sind, können Sie eine Factory erstellen, die den ausgehenden AuthorizationHeader für jede Anforderung festlegt, indem Sie SecurityContextHolder.getContext().getAuthorization()den Header ausführen und dann mit entsprechenden Nullprüfungen füllen. Jetzt haben alle ausgehenden Restaufrufe, die mit diesem RestTemplate getätigt werden, den richtigen Autorisierungsheader.

Ohne mehr Gewicht auf den HttpClientFactory-Mechanismus zu legen, der einfach zu überladende Basisklassen für häufige Fälle wie das Hinzufügen eines einzelnen Headers zu Anforderungen bereitstellt, sind die meisten der nützlichen Methoden RestTemplateZeitverschwendung, da sie nur selten verwendet werden können .

Ich würde gerne sehen, dass so etwas Einfaches zur Verfügung gestellt wird

@Configuration
public class MyConfig {
  @Bean
  public RestTemplate getRestTemplate() {
    return new RestTemplate(new AbstractHeaderRewritingHttpClientFactory() {
        @Override
        public HttpHeaders modifyHeaders(HttpHeaders headers) {
          headers.addHeader("Authorization", computeAuthString());
          return headers;
        }
        public String computeAuthString() {
          // do something better than this, but you get the idea
          return SecurityContextHolder.getContext().getAuthorization().getCredential();
        }
    });
  }
}

Im Moment ist es schwieriger, mit der Schnittstelle der verfügbaren ClientHttpRequestFactory zu interagieren. Noch besser wäre ein abstrakter Wrapper für vorhandene Factory-Implementierungen, der sie wie ein einfacheres Objekt wie AbstractHeaderRewritingRequestFactory aussehen lässt, um nur diese eine Funktionalität zu ersetzen. Im Moment sind sie sehr allgemein gehalten, so dass selbst das Schreiben dieser Wrapper ein komplexes Stück Forschung ist.

Ideenbildhauer
quelle
2
Während Ihre "Antwort" sehr interessant ist, liest sie sich eher wie ein Kommentar als wie eine tatsächliche Antwort.
Martin Schröder
3

Heutzutage wird so etwas wie das Folgende ausreichen:

HttpHeaders headers = new HttpHeaders();
headers.setBearerAuth(accessToken);
restTemplate.exchange(RequestEntity.get(new URI(url)).headers(headers).build(), returnType);
AmanicA
quelle
0

Eine einfache Lösung wäre, statische http-Header zu konfigurieren, die für alle Aufrufe in der Bean-Konfiguration von RestTemplate benötigt werden:

@Configuration
public class RestTemplateConfig {

    @Bean
    public RestTemplate getRestTemplate(@Value("${did-service.bearer-token}") String bearerToken) {
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.getInterceptors().add((request, body, clientHttpRequestExecution) -> {
            HttpHeaders headers = request.getHeaders();
            if (!headers.containsKey("Authorization")) {
                String token = bearerToken.toLowerCase().startsWith("bearer") ? bearerToken : "Bearer " + bearerToken;
                request.getHeaders().add("Authorization", token);
            }
            return clientHttpRequestExecution.execute(request, body);
        });
        return restTemplate;
    }
}
userM1433372
quelle