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.

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.


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.


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());
    }
}

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.


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);

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;
    }
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.