21. Verwaltung domänenübergreifender Zugriffe
21.1. Architektur
Wir werden uns nun mit dem Problem der domänenübergreifenden Anfragen befassen. Im Dokument [Tutoriel AngularJS / Spring 4] wird eine Client-Server-Anwendung entwickelt, bei der der Client eine Anwendung mit dem Namen AngularJS ist:
![]() |
- Die Seiten HTML / CSS / JS der Angular-Anwendung stammen vom Server [1];
- In [2] sendet der Dienst [dao] eine Anfrage an einen anderen Server, nämlich den Server [2]. Nun, genau das wird vom Browser, der die Angular-Anwendung ausführt, unterbunden, da es sich um eine Sicherheitslücke handelt. Die Anwendung darf nur den Server abfragen, von dem sie stammt, d. h. den Server [1];
Tatsächlich ist es unzutreffend zu sagen, dass der Browser der Angular-Anwendung verbietet, den Server [2] abzufragen. Der Browser fragt vielmehr den Server [2] ab, um zu erfahren, ob dieser einem Client, der nicht von ihm selbst stammt, die Abfrage gestattet. Diese Technik wird als CORS (Cross-Origin Resource Sharing) bezeichnet. Der Server [2] erteilt seine Zustimmung, indem er bestimmte HTTP-Header sendet.
Um die Probleme aufzuzeigen, die dabei auftreten können, erstellen wir eine Client-Server-Anwendung, bei der:
- der Server unser sicherer Webserver / jSON ist;
- der Client eine einfache Seite HTML ist, die mit einem JavaScript-Code ausgestattet ist, der Anfragen an den Webserver / jSON sendet;
Wir werden die folgende Architektur einrichten:
![]() |
- auf [1] liefert eine Webanwendung die Seiten HTML / jS aus;
- in [2] führt der Browser das in den Seiten HTML eingebettete JavaScript aus, um den gesicherten Webdienst [3] abzufragen;
21.2. Das Projekt [spring-cors-server-jdbc-generic]
21.2.1. Einrichtung der Arbeitsumgebung
![]() |
- Laden Sie die oben genannten Projekte. Die Projekte [spring-cors-*] befinden sich im Ordner [<exemples>\spring-database-generic\spring-cors];
- Drücken Sie Alt-F5 und regenerieren Sie alle Maven-Projekte;
Führen Sie anschließend die Laufkonfiguration mit dem Namen [spring-cors-server-jdbc-generic] aus (die Projekte SGBD und MySQL müssen gestartet sein), die einen Webdienst auf Port 8081 startet:
![]() |
Füllen Sie die Basis [dbproduitscategories] mit der Ausführungskonfiguration namens [spring-jdbc-generic-04-fillDataBase]:
![]() |
Führen Sie die Ausführungskonfiguration mit dem Namen [spring-cors-client-generic] aus, die eine zweite Webanwendung (auf einem anderen Tomcat) auf Port 8082 startet:
![]() |
Rufen Sie mit einem Browser die Seiten URL und [http://localhost:8082/client.html] auf:
![]() |
- In [1] wird die Kurzversion aller Kategorien angefordert;
- in [2] die Antwort jSON vom Server;
21.2.2. Das Projekt des Kunden [spring-cors-client-generic]
![]() |
![]() |
Die Datei [application.properties] ermöglicht es uns, den Port der Client-Webanwendung festzulegen. Ihr Inhalt lautet wie folgt:
server.port=8082
Somit gilt:
- ist der Client eine Webanwendung, die unter URL [http://localhost:8082] erreichbar ist;
- der Server ist eine Webanwendung, die unter den Ports URL und [http://localhost:8081] erreichbar ist;
Da der Client nicht über denselben Port wie der Server erreichbar ist, tritt das Problem der domänenübergreifenden Anfragen auf. Tatsächlich handelt es sich bei [http://localhost:8081] und [http://localhost:8082] um zwei verschiedene Domänen.
21.2.3. Maven-Konfiguration
Es handelt sich um ein Maven-Projekt mit der folgenden Datei [pom.xml]:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>dvp.spring.database</groupId>
<artifactId>spring-cors-client-generic</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>spring-cors-client-generic</name>
<description>Client cors for webjson server</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.3.RELEASE</version>
<relativePath /> <!-- Übergeordnetes Element aus dem Repository abrufen -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.7</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<!-- Plugins -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.18.1</version>
</plugin>
</plugins>
</build>
</project>
- Zeilen 14–19: Es handelt sich um ein Spring-Boot-Projekt;
- Zeilen 27–30: Es wird die Abhängigkeit [spring-boot-starter-web] verwendet, die einen Tomcat-Server und Spring MVC mitbringt;
21.2.4. Grundlagen von jQuery und JavaScript
![]() |
Die Webanwendung liefert die folgende einzige Seite aus:
![]() |
Sie enthält JavaScript-Code (jS), der im Browser ausgeführt wird. Wir werden einige Grundlagen von JavaScript vorstellen, die uns helfen werden, den Code zu verstehen. Der Client führt Aufrufe von HTTP mithilfe der Bibliothek jQuery [https://jquery.com/] durch, die zahlreiche Funktionen zur Erleichterung der JavaScript-Entwicklung bereitstellt. Wir erstellen eine statische Datei HTML [jQuery.html], die wir im Ordner [static] ablegen:
![]() |
Diese Datei hat folgenden Inhalt:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>JQuery-01</title>
<script type="text/javascript" src="/js/jquery-2.1.3.min.js"></script>
</head>
<body>
<h3>Rudiments de JQuery</h3>
<div id="element1">
Elément 1
</div>
</body>
</html>
- Zeile 6: Import von jQuery;
- Zeilen 10–12: ein Element der Seite mit der ID [element1]. Wir werden mit diesem Element experimentieren.
Wir müssen die Datei [jquery-2.1.3.min.js] herunterladen. Die neueste Version von jQuery finden wir unter URL und [http://jquery.com/download/]:

Die heruntergeladene Datei wird im Ordner „[static / js]“ abgelegt:
![]() |
Anschließend rufen wir die statische Ansicht [jQuery.html] mit Chrome auf: [1-2]:
![]() |
Mit Google Chrome geben Sie [Ctrl-Maj-I] ein, um die Entwicklertools [3] anzuzeigen. Über die Registerkarte [Console] [4] können Sie JavaScript-Code ausführen. Im Folgenden geben wir JavaScript-Befehle an, die Sie eingeben müssen, und erläutern diese.
|
: gibt die Sammlung aller Elemente mit der ID [element1], also normalerweise eine Sammlung aus 0 oder 1 Element, da es zwei identische IDs auf einer Seite haben kann: HTML. | ![]() |
|
: Weist allen Elementen der der Sammlung zu. Dies bewirkt eine Änderung des angezeigten Inhalt der Seite | ![]() |
|
blendet die Elemente der Sammlung aus. Der Text [blabla] wird nicht mehr angezeigt. | ![]() |
|
: Zeigt die Sammlung wieder an. Dadurch sehen, dass das Element mit der ID [element1] das Attribut CSS style='display: none;' besitzt, wodurch dazu führt, dass das Element ausgeblendet wird. | |
|
: Zeigt die Elemente der Sammlung an. Der Text [blabla] erscheint wieder. Es ist das Attribut CSS style='display: block;', das diese Anzeige. | ![]() |
|
: Weist allen Elementen der Kollektion. Das Attribut lautet hier [style] und sein Wert [color: red]. Der Text „[blabla]“ wird rot angezeigt. | ![]() |
![]() | |
![]() |
Es ist zu beachten, dass sich der Wert „URL“ des Browsers während all dieser Vorgänge nicht verändert hat. Es fand kein Datenaustausch mit dem Webserver statt. Alles geschieht innerhalb des Browsers. Sehen wir uns nun den Quellcode der Seite an:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>JQuery-01</title>
<script type="text/javascript" src="/js/jquery-1.11.1.min.js"></script>
</head>
<body>
<h3>Rudiments de JQuery</h3>
<div id="element1">
Elément 1
</div>
</body>
</html>
Dies ist der ursprüngliche Text. Er spiegelt in keiner Weise die Änderungen wider, die wir an dem Element in den Zeilen 10–12 vorgenommen haben. Das sollte man beim Debuggen von JavaScript unbedingt beachten. Oft ist es daher sinnlos, den Quellcode der angezeigten Seite einzusehen.
21.2.5. Der Code jS der Anwendung
Kehren wir zum Code HTML der Seite der Client-Anwendung zurück, die den Webdienst /jSON abfragen wird:
![]() |
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Spring MVC</title>
<script type="text/javascript" src="/js/jquery-2.1.1.min.js"></script>
<script type="text/javascript" src="/js/client.js"></script>
</head>
<body>
<h2>Client du service web / jSON</h2>
<form id="formulaire">
<!-- Methode HTTP -->
Méthode HTTP :
<!-- -->
<input type="radio" id="get" name="method" value="get" checked="checked" />GET
<!-- -->
<input type="radio" id="post" name="method" value="post" />POST
<!-- URL -->
<br /> <br />URL cible : <input type="text" id="url" size="30"><br />
<!-- gebuchter Wert -->
<br /> Chaîne jSON à poster : <input type="text" id="posted" size="50" />
<!-- Bestätigungsschaltfläche -->
<br /> <br /> <input type="submit" value="Valider" onclick="javascript:requestServer(); return false;"></input>
</form>
<hr />
<h2>Réponse du serveur</h2>
<div id="response"></div>
</body>
</html>
- Zeile 6: Wir importieren die Bibliothek jQuery;
- Zeile 7: Es wird ein Code importiert, den wir noch schreiben werden;
- Zeilen 11, 15, 17, 21: Notieren Sie sich die Bezeichner [id] der Seitenkomponenten. Das JavaScript verweist über diese Bezeichner auf diese Komponenten;
Der Code [client.js] lautet wie folgt:
// Globale Daten
var url;
var posted;
var response;
var method;
function requestServer() {
// Die Formularinformationen werden abgerufen
var urlValue = url.val();
var postedValue = posted.val();
method = document.forms[0].elements['method'].value;
// man führt manuell einen Ajax-Aufruf durch
if (method === "get") {
doGet(urlValue);
} else {
doPost(urlValue, postedValue);
}
}
function doGet(url) {
// Es wird manuell ein Ajax-Aufruf durchgeführt
$.ajax({
headers : {
'„Authorization“: „Basic YWRtaW46YWRtaW4=“
},
url : 'http://„localhost:8081“ + URL,
type : 'GET',
dataType : 'tex/plain',
beforeSend : function() {
},
success : function(data) {
// Ergebnis als Text
response.text(data);
},
complete : function() {
},
error : function(jqXHR) {
// Systemfehler
response.text(jqXHR.responseText);
}
})
}
function doPost(url, posted) {
// Ajax-Aufruf manuell
$.ajax({
headers : {
'„Authorization“: „Basic YWRtaW46YWRtaW4=“
},
url : 'http://localhost:8081 ' + url,
type : 'POST',
contentType : 'application/json',
data : posted,
dataType : 'tex/plain',
beforeSend : function() {
},
success : function(data) {
// Ergebnis als Text
response.text(data);
},
complete : function() {
},
error : function(jqXHR) {
// Systemfehler
response.text(jqXHR.responseText);
}
})
}
// beim Laden des Dokuments
$(document).ready(function() {
// die Referenzen der Seitenkomponenten werden abgerufen
url = $("#url");
posted = $("#posted");
response = $("#response");
});
- Zeilen 71–75: Code jS, der nach dem Laden des Dokuments im Browser ausgeführt wird;
- Zeilen 73–75: Es werden die Referenzen von drei Feldern des Dokuments HTML abgerufen;
- Zeilen 2–5: Globale Variablen, die in allen in der Datei jS definierten Funktionen bekannt sind;
- Zeile 9: Die vom Benutzer eingegebene URL wird abgerufen;
- Zeile 10: Der Wert, den der Benutzer senden möchte, wird abgerufen;
- Zeile 11: Es wird ermittelt, ob [get] oder [post] verwendet werden soll, um den Wert aus Zeile 9 abzufragen:
- „document“ bezeichnet das vom Browser geladene Dokument, das sogenannte DOM (Document Object Model),
- document.forms[0] bezeichnet das erste Formular des Dokuments; ein Dokument kann mehrere Formulare enthalten. Hier gibt es nur eines,
- document.forms[0].elements['method'] bezeichnet das Formularelement, das das Attribut [name='method'] hat. Es gibt zwei davon:
<input type="radio" id="get" name="method" value="get" checked="checked" />GET
<input type="radio" id="post" name="method" value="post" />POST
- (Fortsetzung)
- document.forms[0].elements['method'].value ist der Wert, der für die Komponente mit dem Attribut [name='method'] übermittelt wird. Es ist bekannt, dass der übermittelte Wert dem Wert des Attributs [value] des markierten Optionsfelds entspricht. In diesem Fall handelt es sich also um eine der Zeichenfolgen ['get', 'post'];
- Zeilen 13–18: Je nachdem, welche Methode HTTP verwendet werden soll, wird die Methode [doGet] oder [doPost] ausgeführt;
- die Methode jQuery [$.ajax] führt einen Aufruf von HTTP durch;
- Zeilen 23–25: Es wird eine Verbindung zu einem Server hergestellt, der einen Header HTTP [Authorization: Basic code] verlangt. Wir erstellen diesen Header für den Benutzer [admin / admin], der als Einziger den Server abfragen darf;
- Zeile 26: Der Benutzer gibt URL vom Typ [/getAllLongCategories, /saveCategories, ...] ein. Diese URL müssen daher vervollständigt werden;
- Zeile 27: Die Methode HTTP ist zu verwenden;
- Zeile 28: Der Server sendet jSON zurück. Als Ergebnistyp wird der Typ [text/plain] angegeben, um das Ergebnis so anzuzeigen, wie es empfangen wurde;
- Zeile 33: Anzeige der Textantwort des Servers;
- Zeile 39: Anzeige einer eventuellen Fehlermeldung im Textformat;
- Zeile 44: Die Methode [doPost] erhält einen zweiten Parameter, nämlich den zu übermittelnden Wert;
- Zeile 52: Angabe, dass der gesendete Wert in Form einer Zeichenkette „jSON“ übermittelt wird;
21.2.6. Ausführung des Clients
Die Client-Anwendung ist eine Konsolenanwendung, die von der folgenden ausführbaren Klasse [Client] gestartet wird:
![]() |
package spring.cors.client;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
@EnableAutoConfiguration
public class Client {
public static void main(String[] args) {
SpringApplication.run(Client.class, args);
}
}
- Zeile 6: Die Annotation [@EnableAutoConfiguration] ist eine Annotation des Projekts [Spring Boot] (Zeile 4). Spring Boot überprüft die im Classpath des Projekts vorhandenen Archive. Dies sind hier alle Maven-Abhängigkeiten, die durch die einzige Abhängigkeit der Datei [pom.xml] mitgebracht werden:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
Diese Abhängigkeit bringt sehr viele Archive mit sich, insbesondere Spring MVC und einen Tomcat-Server. Aufgrund dieser Abhängigkeiten konfiguriert Spring Boot mit Standardwerten ein Spring-Projekt MVC, das auf Tomcat ausgeführt wird. Der Tomcat-Server wird dann so konfiguriert, dass er auf Port 8080 läuft. Wenn man sich von den von Spring Boot gewählten Standardwerten lösen möchte, kann man die Datei [application.properties] im Stammverzeichnis des Classpaths verwenden (alles, was sich in [src / main / resources] befindet, befindet sich im Stammverzeichnis des Classpaths):
![]() |
Wir legen fest, dass der Tomcat-Server auf Port 8082 laufen soll, und zwar wie folgt:
server.port=8082
Eine Liste der verfügbaren Parameter finden Sie in [application.properties] bis URL (Juni 2015) [http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html];
Zurück zum Code von [Client.java]:
- Zeile 10: Die Methode [SpringApplication.run] stellt die Seite [client.html] auf dem Tomcat-Server bereit, der im Classpath des Projekts enthalten ist;
21.2.7. URL [/getAllShortCategories]
![]() |
Wir starten:
- den sicheren Web-/JSON-Server auf Port 8081 (Konfiguration [spring-security-server-jdbc-generic]);
- den Client dieses Servers auf Port 8082 (Konfiguration [spring-cors-client-generic]);
anschließend rufen wir die URL, [http://localhost:8082/client.html] und [1] auf:
![]() |
- in [2] führen wir einen GET auf URL und [http://localhost:8081/getAllShortCategories] durch;
Wir erhalten keine Antwort vom Server. Wenn man sich die Entwicklerkonsole von Chrome (Strg-Umschalt-I) ansieht, wird folgender Fehler angezeigt:
![]() |
- in [1], wir befinden uns auf der Registerkarte [Network];
- bei [2] sieht man, dass die gestellte Anfrage HTTP nicht [GET], sondern [OPTIONS] ist. Bei einer domänenübergreifenden Anfrage überprüft der Browser beim Server, ob eine Reihe von Bedingungen erfüllt sind, indem er ihm eine Anfrage HTTP [OPTIONS] sendet. In diesem Fall handelt es sich bei den Anfragen um diejenigen, auf die die Markierungen [5-6] verweisen;
- In [5] fragt der Browser ab, ob das Ziel URL mit einem GET erreicht werden kann. Die Anfrage [Access-Control-Request-Method] fordert eine Antwort mit einem Header HTTP [Access-Control-Allow-Methods] an, der angibt, dass die angeforderte Methode akzeptiert wird;
- In [6] sendet der Browser den Header HTTP [Origin: http://localhost:8081]. Dieser Header fordert eine Antwort in Form eines Headers HTTP [Access-Control-Allow-Origin] an, der angibt, dass die angegebene Herkunft akzeptiert wird;
- In [7] fragt der Browser, ob die Header HTTP, [accept] und [authorization] akzeptiert werden. Die Anfrage [Access-Control-Request-Headers] erwartet eine Antwort mit den Headern HTTP und [Access-Control-Allow-Headers], die angeben, dass die angeforderten Header akzeptiert werden;
- in [3] tritt ein Fehler auf. Beim Klicken auf das Symbol erscheint der Fehler [4];
- in [4] besagt die Meldung, dass der Server den Header HTTP [Access-Control-Allow-Origin] nicht gesendet hat, der angibt, ob die Herkunft der Anfrage akzeptiert wird;
- In [8] lässt sich feststellen, dass der Server diesen Header tatsächlich nicht gesendet hat. Daher hat der Browser die ursprünglich angeforderte Anfrage HTTP GET abgelehnt;
Wir müssen den Webserver anpassen / jSON.
21.2.8. Ein neuer Webdienst / json
Wir erstellen ein neues Maven-Projekt [spring-cors-server-jdbc-generic]:
![]() |
Die Maven-Konfiguration des neuen Webdienstes lautet wie folgt:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>dvp.spring.database</groupId>
<artifactId>spring-cors-server-jdbc-generic</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-cors-server-jdbc-generic</name>
<description>démo spring cors</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.3.RELEASE</version>
</parent>
<!-- Plugins -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.18.1</version>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>dvp.spring.database</groupId>
<artifactId>spring-security-server-jdbc-generic</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
- Zeilen 30–32: Wir nutzen alle bisherigen Arbeitsergebnisse, indem wir auf das Archiv des gesicherten Web-/JSON-Servers zurückgreifen;
Letztendlich lauten die Abhängigkeiten wie folgt:
![]() |
Die Konfigurationsklasse [AppConfig] lautet wie folgt:
![]() |
package spring.cors.server.config;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.web.servlet.DispatcherServlet;
@Configuration
@ComponentScan(basePackages = { "spring.cors.server.service" })
@Import({ spring.security.config.AppConfig.class })
public class AppConfig {
// domänenübergreifende Anfragen
@Bean
public boolean isCorsEnabled() {
return true;
}
...
}
- Zeile 12: Die Klasse ist eine Spring-Konfigurationsklasse;
- Zeile 9: Weitere Spring-Komponenten sind im Paket [spring.cors.server.service] zu suchen;
- Zeile 14: Die Beans aus dem Projekt [spring-security-server-jdbc-generic] werden importiert;
- Zeilen 18–21: Wir erstellen eine Spring-Komponente namens [isCorsEnabled], die angibt, ob Clients außerhalb der Serverdomäne akzeptiert werden oder nicht;
21.2.9. Die Controller
Der neue Webdienst verfügt über vier Controller:
![]() |
- [CorsCategorieController] verwaltet die URL zur Bearbeitung der Kategorien. Er verarbeitet ausschließlich die CORS-Header der Web-Clients. Ansonsten delegiert er die Arbeit an den Controller [CategorieController] der Abhängigkeit [spring-webjson-server-jdbc-generic];
- [CorsProduitController] und [CorsAuthenticateController] verfahren ebenso und delegieren die Arbeit an die Controller [ProduitController] der Abhängigkeit [spring-webjson-server-jdbc-generic] und [AuthenticateController] der Abhängigkeit[spring-security-server-jdbc-generic];
- [CorsController] dient dazu, die gemeinsamen Elemente der drei vorangegangenen Controller zu extrahieren;
21.2.9.1. Der Controller [CorsController]
Die Klasse [CorsController] lautet wie folgt:
package spring.cors.server.service;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class CorsController {
@Autowired
private boolean isCorsEnabled;
// Senden der Optionen an den Client
public void sendOptions(String origin, HttpServletResponse response) {
// CORS erlaubt?
if (!isCorsEnabled || origin == null || !origin.startsWith("http://localhost")) {
return;
}
// Der Header CORS wird festgelegt
response.addHeader("Access-Control-Allow-Origin", origin);
// Bestimmte Header werden zugelassen
response.addHeader("Access-Control-Allow-Headers", "accept, authorization");
// GET wird zugelassen
response.addHeader("Access-Control-Allow-Methods", "GET");
}
}
- Zeile 8: Die Klasse [CorsController] ist ein Spring-Controller;
- Zeilen 11–12: Injektion des Beans [isCorsEnabled], der angibt, ob die Header CORS verarbeitet werden sollen oder nicht;
- Zeilen 15–26: Die Methode [sendOptions] kümmert sich um die Beantwortung von Clients, die die Header CORS senden;
- Zeilen 17–19: Wenn die Anwendung so konfiguriert ist, dass sie domänenübergreifende Anfragen akzeptiert, und wenn der Absender den Header HTTP [Origin] gesendet hat und diese Herkunft mit [http://localhost] beginnt, dann wird die domänenübergreifende Anfrage akzeptiert, andernfalls wird sie abgelehnt;
- Zeile 21: Befindet sich der Client in der Domäne [http://localhost:port], wird der Header HTTP gesendet:
was bedeutet, dass der Server die Herkunft des Clients akzeptiert;
- Zeilen 22–25: Wir haben zwei spezielle Header HTTP in der Anfrage HTTP [OPTIONS] angegeben:
Auf die Header HTTP und [Access-Control-Request-X] antwortet der Server mit einem Header HTTP und [Access-Control-Allow-X], in dem er angibt, was zulässig ist. Die Zeilen 22–25 geben lediglich die Anfrage des Clients wieder, um anzuzeigen, dass sie akzeptiert wurde;
21.2.9.2. Der Controller [CorsCategorieController]
package spring.cors.server.service;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import spring.jdbc.entities.Categorie;
import spring.webjson.server.entities.CoreCategorie;
import spring.webjson.server.service.CategorieController;
import spring.webjson.server.service.Response;
@RestController
public class CorsCategorieController extends CorsController {
@Autowired
private CategorieController categorieController;
@RequestMapping(value = "/cors-getAllShortCategories", method = RequestMethod.OPTIONS)
public void corsGetAllShortCategories(@RequestHeader(value = "Origin", required = false) String origin,
HttpServletResponse response) {
sendOptions(origin, response);
}
@RequestMapping(value = "/cors-getAllShortCategories", method = RequestMethod.GET)
public Response<List<Categorie>> getAllShortCategories(
@RequestHeader(value = "Origin", required = false) String origin, HttpServletResponse response) {
// Ursprungsmethode
return categorieController.getAllShortCategories();
}
...
}
- Zeile 19: Die Anmerkung [@RestController] macht die Klasse sowohl zu einer Spring-Komponente als auch zu einem Controller MVC, der seine Antworten selbst an den Client sendet;
- Zeile 20: Die Klasse [CorsCategorieController] erweitert die soeben betrachtete Klasse [CorsController];
- Zeilen 22–23: Injektion des Controllers [CategorieController categorieController] aus der Abhängigkeit [spring-webjson-server-jdbc-generic];
- Zeilen 25–29: Behandeln die URL [/cors-getAllShortCategories], wenn sie mit dem Befehl HTTP [OPTIONS] aufgerufen wird. Gemäß einer Vereinbarung legen wir fest, dass Webklienten, die die URL [/U] des gesicherten Webdienstes aufrufen möchten, tatsächlich die URL [/cors-U] aufrufen müssen. Der bereitgestellte Webdienst verfügt somit über zwei Arten von URL:
- [/U]: für Nicht-Web-Clients;
- [/cors-U]: für Web-Clients;
- Zeile 25: Die Methode [/cors-getAllShortCategories] akzeptiert folgende Parameter:
- das Objekt [@RequestHeader(value = "Origin", required = false)], das den Header HTTP [Origin] aus der Anfrage abruft. Dieser Header wurde vom Absender der Anfrage gesendet:
Es wird angegeben, dass der Header HTTP [Origin] optional ist [required = false]. In diesem Fall erhält der Parameter [String origin] den Wert null, wenn der Header fehlt. Bei [required = true], dem Standardwert, wird eine Ausnahme ausgelöst, wenn der Header fehlt. Dies wollte man vermeiden;
- (Fortsetzung)
- das Objekt [HttpServletResponse response], das an den anfragenden Client zurückgegeben wird;
Diese beiden Parameter werden von Spring bereitgestellt;
- Zeile 28: Die Bearbeitung der Anfrage wird an die Methode [sendOptions] der übergeordneten Klasse [CorsController] delegiert;
- Zeilen 31–36: Die Methode [getAllShortCategories] verarbeitet die Objekte URL und [/cors-getAllShortCategories], wenn sie mit einem GET aufgerufen wird;
- Zeile 35: Die Aufgabe wird an die Methode [CategorieController.getAllShortCategories] der Abhängigkeit [spring-webjson-server-jdbc-generic] delegiert;
Wir sind nun bereit für weitere Tests. Wir starten die neue Version des Webdienstes und stellen fest, dass das Problem weiterhin besteht. Es hat sich nichts geändert. Wenn wir in Zeile 28 oben eine Konsolenausgabe einfügen, wird diese nie angezeigt, was zeigt, dass die Methode [corsGetAllShortCategories] in Zeile 25 nie aufgerufen wird.
Nach einigen Recherchen stellen wir fest, dass Spring MVC die Befehle HTTP und [OPTIONS] selbst mit einer Standardverarbeitung bearbeitet. Daher antwortet immer Spring und niemals die Methode [corsGetAllShortCategories] in Zeile 25. Dieses Standardverhalten von Spring MVC lässt sich ändern. Wir ändern die vorhandene Klasse [AppConfig]:
![]() |
package spring.cors.server.config;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.web.servlet.DispatcherServlet;
@Configuration
@ComponentScan(basePackages = { "spring.cors.server.service" })
@Import({ spring.security.config.AppConfig.class })
public class AppConfig {
// domänenübergreifende Anfragen
@Bean
public boolean isCorsEnabled() {
return true;
}
@Autowired
private DispatcherServlet dispatcherServlet;
@PostConstruct
public void init(){
// Die Anwendung verarbeitet die Anfragen selbst HTTP [OPTIONS]
dispatcherServlet.setDispatchOptionsRequest(true);
}
}
- Zeilen 23–24: Wir injizieren die Komponente [DispatcherServlet dispatcherServlet], die in der Abhängigkeit [spring-webjson-server-jdbc-generic] definiert wurde;
- Zeilen 26–30: Die Annotation [@PostConstruct] sorgt dafür, dass die Methode [init] nach der Instanziierung der Klasse [AppConfig] und nach den von Spring durchgeführten Einbindungen ausgeführt wird;
- Zeile 29: Es wird festgelegt, dass das Servlet die Befehle HTTP und [OPTIONS] an die Anwendung weiterleitet;
Wir führen die Tests mit dieser neuen Konfiguration erneut durch. Dabei erhalten wir folgendes Ergebnis:
![]() |
- Bei [1] sehen wir, dass zwei Anfragen HTTP an URL und [http://localhost:8080/getAllCategories] gerichtet sind;
- in [2] die Anfrage [OPTIONS];
- in [3] die drei Header HTTP, die wir soeben in der Serverantwort konfiguriert haben;
Betrachten wir nun die zweite Anfrage:
![]() |
- in [1], die untersuchte Anfrage;
- in [2], das ist die Anfrage GET. Dank der ersten Anfrage [OPTIONS] hat der Browser die angeforderten Informationen erhalten. Er führt nun die ursprünglich angeforderte Anfrage [GET] aus;
- in [3] die Antwort des Servers;
- in [4] sendet der Server jSON;
- in [5] ist ein Fehler aufgetreten;
- in [6] die Fehlermeldung;
Es ist schwieriger zu erklären, was hier passiert ist. Die Antwort [3] des Servers ist normal, [HTTP/1.1 200 OK]. Wir sollten also das angeforderte Dokument erhalten haben. Es ist möglich, dass der Server das Dokument zwar gesendet hat, der Browser jedoch dessen Verwendung verhindert, weil er verlangt, dass die Antwort auch für die Anfrage GET den Header HTTP [Access-Control-Allow-Origin:http://localhost:8081] enthält.
Wir ändern die Methode, die GET aus URL und [/cors-getAllShortCategories] verarbeitet:
@RequestMapping(value = "/cors-getAllShortCategories", method = RequestMethod.GET)
public Response<List<Categorie>> getAllShortCategories(
@RequestHeader(value = "Origin", required = false) String origin, HttpServletResponse response) {
// Header CORS
sendOptions(origin, response);
// Ursprungsmethode
return categorieController.getAllShortCategories();
}
- Zeile 5: Wie bei der Anfrage HTTP [OPTIONS] sendet der Server die Header HTTP CORS für eine Anfrage HTTP [GET];
Nach dieser Änderung ergeben sich folgende Ergebnisse:
![]() |
Wir haben tatsächlich die Kurzform aller Kategorien erhalten.
21.2.9.3. Die URL [GET]
In den Controllern [CorsCategorieController, CorsProduitController, CorsAuthenticateController] folgt der Code der Aktionen, die die mit einem [GET] angeforderten URL verarbeiten, dem Muster der Aktionen, die zuvor die URL und [/cors-getAllShortArticles] verarbeitet haben. Der Leser kann den Code in den mit diesem Dokument gelieferten Beispielen überprüfen. Hier ist ein Beispiel für die URL und [/cors-getAllLongProduits] des Controllers [CorsProduitController]:
package spring.cors.server.service;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import spring.jdbc.entities.Produit;
import spring.webjson.server.entities.CoreProduit;
import spring.webjson.server.service.ProduitController;
import spring.webjson.server.service.Response;
@RestController
public class CorsProduitController extends CorsController {
@Autowired
private ProduitController produitController;
@RequestMapping(value = "/cors-getAllLongProduits", method = RequestMethod.GET)
public Response<List<Produit>> getAllLongProduits(@RequestHeader(value = "Origin", required = false) String origin,HttpServletResponse response) {
// Kopfzeilen CORS
sendOptions(origin, response);
// Ursprungsmethode
return produitController.getAllLongProduits();
}
@RequestMapping(value = "/cors-getAllLongProduits", method = RequestMethod.OPTIONS)
public void corsGetAllLongProduits(@RequestHeader(value = "Origin", required = false) String origin,
HttpServletResponse response) {
sendOptions(origin, response);
}
...
}
![]() |
21.2.9.4. Die URL und [POST]
Betrachten wir den folgenden Fall:
![]() |
- Es wird ein POST [1] an das URL [2] gesendet;
- in [3], dem gebuchten Wert. Es handelt sich um die Zeichenfolge jSON einer Kategorie ohne Produkte;
- letztendlich möchten wir eine Kategorie mit dem Namen [categorie[2]] erstellen;
Wir ändern vorerst keinen Code. Das Ergebnis sieht dann wie folgt aus:
![]() |
- in [1]: Wie bei den Anfragen [GET] wird vom Browser eine Anfrage [OPTIONS] gestellt;
- bei [2] fordert er eine Zugriffsberechtigung für eine Anfrage [POST] an. Zuvor war es [GET];
- in [3] fordert er die Berechtigung zum Senden der Header HTTP und [accept, authorization, content-type] an. Zuvor gab es nur die ersten beiden Header;
- in [4] erteilt der Webdienst nicht alle angeforderten Berechtigungen, was den Fehler [5] verursacht;
Wir ändern die Methode [CorsController.sendOptions] wie folgt:
public void sendOptions(String origin, HttpServletResponse response) {
// Cors erlaubt?
if (!isCorsEnabled || origin == null || !origin.startsWith("http://localhost")) {
return;
}
// Der Header wird festgelegt CORS
response.addHeader("Access-Control-Allow-Origin", origin);
// bestimmte Header werden zugelassen
response.addHeader("Access-Control-Allow-Headers", "accept, authorization, content-type");
// GET und POST werden zugelassen
response.addHeader("Access-Control-Allow-Methods", "GET, POST");
}
}
- Zeile 9: Der Header HTTP [Content-Type] wurde hinzugefügt (Groß-/Kleinschreibung spielt keine Rolle);
- Zeile 11: Die Methode HTTP [POST] wurde hinzugefügt;
Damit werden die Methoden [POST] genauso behandelt wie die Abfragen [GET]. Hier ist ein Beispiel für die Methoden URL und [/cors-saveCategories] im Controller [CorsCategorieController]:
@RequestMapping(value = "/cors-saveCategories", method = RequestMethod.POST, consumes = "application/json; charset=UTF-8")
public Response<List<CoreCategorie>> saveCategories(HttpServletRequest request,
@RequestHeader(value = "Origin", required = false) String origin, HttpServletResponse response) {
// Header CORS
sendOptions(origin, response);
// Ursprungsmethode
return categorieController.saveCategories(request);
}
@RequestMapping(value = "/cors-saveCategories", method = RequestMethod.OPTIONS)
public void corsSaveCategories(@RequestHeader(value = "Origin", required = false) String origin,
HttpServletResponse response) {
sendOptions(origin, response);
}
Nach diesen Änderungen ergibt sich folgendes Ergebnis:
![]() |
Die Kategorie [categorie[2]] wurde erfolgreich in die Datenbank eingefügt. Der Schlüssel 226 wurde ihr durch den SGBD zugewiesen. Dies lässt sich mit der Methode GET [/cors-getAllShortCategories] überprüfen:
![]() |
21.2.10. Fazit
Unsere Anwendung unterstützt nun domänenübergreifende Abfragen. Diese können über die Konfiguration in der Klasse [AppConfig] zugelassen oder untersagt werden:
package spring.cors.server.config;
...
@Configuration
@ComponentScan(basePackages = { "spring.cors.server.service" })
@Import({ spring.security.config.AppConfig.class })
public class AppConfig {
// domänenübergreifende Anfragen
@Bean
public boolean isCorsEnabled() {
return true;
}
...
}
21.3. Das Eclipse-Projekt [spring-cors-server-jpa-generic]
Der Webdienst CORS wird nun durch das Projekt [spring-cors-server-jpa-generic] implementiert, das auf dem Projekt [spring-security-server-jpa-generic] basiert, welches den Zugriff auf die Datenbank mit Spring Data JPA verwaltet:
![]() |
Das Projekt [spring-cors-server-jpa-generic] wird durch Kopieren des zuvor untersuchten Projekts [spring-cors-server-jdbc-generic] erstellt.
![]() |
Anschließend sind zwei Änderungen vorzunehmen. Die erste befindet sich in der Datei [pom.xml]:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>dvp.spring.database</groupId>
<artifactId>spring-cors-server-jpa-generic</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-cors-server-jpa-generic</name>
<description>démo spring cors</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.3.RELEASE</version>
</parent>
<!-- Plugins -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.18.1</version>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>dvp.spring.database</groupId>
<artifactId>spring-security-server-jpa-generic</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
- Zeilen 30–32: die Abhängigkeit vom gesicherten Webdienst [spring-security-server-jpa-generic];
Letztendlich lauten die Abhängigkeiten des Projekts wie folgt:
![]() |
Hinweis: Drücken Sie Alt-F5 und generieren Sie anschließend alle Projekte neu
Die zweite Änderung besteht darin, die Importe in den Klassen zu aktualisieren, die Fehler bei [Alt-Maj-O] melden.
Das war’s. Wir starten den Webdienst CORS mit der Ausführungskonfiguration [spring-cors-server-jpa-generic-hibernate-eclipselink]:
![]() | ![]() |
Anschließend starten wir den generischen Client:
![]() |
und ruft man mit einem Browser die URL, [1] und GET auf. In [2] sieht man, dass die Kurzversion der zurückgegebenen Kategorien das Feld [entityType] enthält, das in der vorherigen Version JDBC noch nicht vorhanden war.
Wir werden zwei weitere Architekturen CORS behandeln:
- Architektur CORS / JPA EclipseLink / DB2;
- Architektur CORS / JPA OpenJpa / Firebird;
21.4. Architektur CORS / JPA EclipseLink / DB2
Wir werden die folgende Architektur implementieren:
![]() |
Wir laden die folgenden Projekte:
![]() |
Hinweis: Drücken Sie Alt-F5 und generieren Sie alle Maven-Projekte neu.
Starten Sie SGBD und DB2 und überprüfen Sie, ob die Datenbank [dbproduitscategories] tatsächlich vorhanden ist. Falls nicht, legen Sie sie an (Abschnitt 12.1.2).
Die Benutzer werden in der Datenbank [dbproduitscategories] mit der Ausführungskonfiguration [spring-security-create-users-hibernate-eclipselink] angelegt:
![]() | ![]() |

Starten Sie anschließend den Web-Service CORS mit der Ausführungskonfiguration [spring-cors-server-jpa-generic-hibernate-eclipselink] und dem zugehörigen Client [spring-cors-client-generic]:
![]() | ![]() |
Füllen Sie die Datenbank [dbproduitscategories] mithilfe der Ausführungskonfiguration [spring-jdbc-generic-04-fillDataBase] mit Werten:
![]() |
Rufen Sie schließlich in einem Browser die folgende URL auf:
![]() |
21.5. Architektur CORS / JPA OpenJPA / Firebird
Wir werden nun die folgende Architektur implementieren:
![]() |
Wir laden die folgenden Projekte:
![]() |
Hinweis: Drücken Sie Alt-F5 und generieren Sie alle Maven-Projekte neu.
Starten Sie SGBD Firebird und überprüfen Sie, ob die Datenbank [dbproduitscategories] tatsächlich vorhanden ist. Falls nicht, erstellen Sie sie (Abschnitt 14.1.2).
Die Benutzer werden in der Datenbank [dbproduitscategories] mit der Ausführungskonfiguration [spring-security-create-users-openjpa] angelegt:
![]() | ![]() |

Starten Sie anschließend den Web-Service CORS mit der Ausführungskonfiguration [spring-cors-server-jpa-generic-openjpa]:
![]() | ![]() |
Starten Sie den Client CORS mit der Konfiguration [spring-cors-client-generic]:
![]() |
Füllen Sie die Datenbank [dbproduitscategories] mithilfe der Ausführungskonfiguration [spring-jdbc-generic-04-fillDataBase] mit Werten:
![]() |
Rufen Sie schließlich in einem Browser die folgende URL auf:
![]() |


























































