21. Gestione degli accessi tra domini
21.1. Architettura
Esamineremo ora la questione delle richieste cross-domain. Nel documento [Tutorial AngularJS / Spring 4], sviluppiamo un'applicazione client/server in cui il client è un'applicazione AngularJS:
![]() |
- le pagine HTML/CSS/JS dell'applicazione Angular provengono dal server [1];
- in [2], il servizio [dao] effettua una richiesta a un altro server, il server [2]. Ebbene, ciò è vietato dal browser che esegue l'applicazione Angular poiché costituisce una vulnerabilità di sicurezza. L'applicazione può interrogare solo il server da cui ha origine, ovvero il server [1];
In realtà, non è corretto dire che il browser impedisca all'applicazione Angular di interrogare il server [2]. Il browser, infatti, interroga il server [2] per determinare se questo consenta a un client che non proviene dal proprio dominio di interrogarlo. Questa tecnica di condivisione è chiamata CORS (Cross-Origin Resource Sharing). Il server [2] concede l'autorizzazione inviando specifici header HTTP.
Per dimostrare i problemi che possono sorgere, creeremo un'applicazione client/server in cui:
- il server sarà il nostro server web/JSON sicuro;
- il client sarà una semplice pagina HTML dotata di codice JavaScript che effettuerà richieste al server web/JSON;
Implementeremo la seguente architettura:
![]() |
- in [1], un'applicazione web fornisce pagine HTML/JS;
- in [2], il browser esegue il JavaScript incorporato nelle pagine HTML per interrogare il servizio web sicuro [3];
21.2. Il progetto [spring-cors-server-jdbc-generic]
21.2.1. Configurazione dell'ambiente di sviluppo
![]() |
- Scaricare i progetti sopra elencati. I progetti [spring-cors-*] si trovano nella cartella [<examples>\spring-database-generic\spring-cors];
- Premere Alt-F5 e ricompilare tutti i progetti Maven;
Quindi esegui la configurazione di esecuzione denominata [spring-cors-server-jdbc-generic] (il database MySQL deve essere in esecuzione), che avvia un servizio web sulla porta 8081:
![]() |
Popolare il database [dbproduitscategories] utilizzando la configurazione di runtime denominata [spring-jdbc-generic-04-fillDataBase]:
![]() |
Esegui la configurazione di runtime denominata [spring-cors-client-generic], che avvia una seconda applicazione web (su un'altra istanza di Tomcat) sulla porta 8082:
![]() |
Utilizzando un browser, richiedere l'URL [http://localhost:8082/client.html]:
![]() |
- In [1] richiediamo la versione breve di tutte le categorie;
- in [2], la risposta JSON del server;
21.2.2. Il progetto client [spring-cors-client-generic]
![]() |
![]() |
Il file [application.properties] ci permette di impostare la porta per l'applicazione web client. Il suo contenuto è il seguente:
server.port=8082
Quindi:
- il client è un'applicazione web disponibile all'URL [http://localhost:8082];
- il server è un'applicazione web disponibile all'URL [http://localhost:8081];
Poiché l'accesso al client non avviene tramite la stessa porta del server, si pone il problema delle richieste cross-domain. Infatti, [http://localhost:8081] e [http://localhost:8082] sono due domini diversi.
21.2.3. Configurazione di Maven
Il progetto è un progetto Maven con il seguente file [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 /> <!-- lookup parent from repository -->
</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>
- righe 14–19: questo è un progetto Spring Boot;
- righe 27–30: utilizziamo la dipendenza [spring-boot-starter-web], che include un server Tomcat e Spring MVC;
21.2.4. Nozioni di base su jQuery e JavaScript
![]() |
L'applicazione web restituisce la seguente pagina singola:
![]() |
Include codice JavaScript (JS) che viene eseguito nel browser. Tratteremo alcune nozioni di base su JavaScript per aiutarci a comprendere il codice. Il client effettuerà richieste HTTP utilizzando la libreria jQuery [https://jquery.com/], che fornisce numerose funzioni che semplificano lo sviluppo in JavaScript. Creiamo un file HTML statico [jQuery.html] e lo inseriamo nella cartella [static]:
![]() |
Questo file avrà il seguente contenuto:
<!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>
- riga 6: importazione di jQuery;
- righe 10–12: un elemento della pagina con l'ID [element1]. Lavoreremo con questo elemento.
Dobbiamo scaricare il file [jquery-2.1.3.min.js]. Puoi trovare l'ultima versione di jQuery all'URL [http://jquery.com/download/]:

Inseriremo il file scaricato nella cartella [static/js]:
![]() |
Una volta fatto ciò, apri la vista statica [jQuery.html] in Chrome [1-2]:
![]() |
In Google Chrome, premi [Ctrl-Shift-I] per aprire gli strumenti di sviluppo [3]. La scheda [Console] [4] ti permette di eseguire codice JavaScript. Di seguito, ti forniamo i comandi JavaScript da digitare e ti spieghiamo a cosa servono.
|
: restituisce la raccolta di tutti gli elementi con l'id [element1], quindi normalmente una raccolta di 0 o 1 elemento, poiché non è possibile avere due ID identici in una pagina HTML. | ![]() |
|
: assegna il testo [blabla] a tutti gli elementi della collezione. Questo modifica il contenuto visualizzato dalla pagina | ![]() |
|
nasconde gli elementi della collezione. Il testo [blabla] non viene più visualizzato. | ![]() |
|
: visualizza nuovamente la raccolta. Questo ci permette di vedere che l'elemento con l'ID [element1] ha l'attributo CSS style='display: none;', il che fa sì che l'elemento sia nascosto. | |
|
: visualizza gli elementi della collezione. Il testo [blabla] ricompare. È l' style='display: block;' che garantisce questa visualizzazione. | ![]() |
|
: imposta un attributo su tutti gli elementi della collezione. L'attributo in questo caso è [style] e il suo valore [color: red]. Il testo [blabla] diventa rosso. | ![]() |
![]() | |
![]() |
Si noti che l'URL del browser non è cambiato durante tutte queste operazioni. Non c'è stata alcuna comunicazione con il server web. Tutto avviene all'interno del browser. Ora, visualizziamo il codice sorgente della pagina:
<!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>
Questo è il testo originale. Non riflette le modifiche apportate all'elemento nelle righe 10–12. È importante tenerlo presente durante il debug di JavaScript. In questi casi, spesso non è necessario visualizzare il codice sorgente della pagina visualizzata.
21.2.5. Il codice JavaScript dell'applicazione
Torniamo al codice HTML della pagina dell'applicazione client che interrogherà il servizio web / JSON:
![]() |
<!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">
<!-- method 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 />
<!-- posted value -->
<br /> Chaîne jSON à poster : <input type="text" id="posted" size="50" />
<!-- validation button -->
<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>
- riga 6: importiamo la libreria jQuery;
- riga 7: importiamo il codice che scriveremo;
- Righe 11, 15, 17, 21: Notate gli identificatori [id] dei componenti della pagina. Il JavaScript fa riferimento a questi componenti tramite tali identificatori;
Il codice [client.js] è il seguente:
// global data
var url;
var posted;
var response;
var method;
function requestServer() {
// retrieve information from the form
var urlValue = url.val();
var postedValue = posted.val();
method = document.forms[0].elements['method'].value;
// make a manual Ajax call
if (method === "get") {
doGet(urlValue);
} else {
doPost(urlValue, postedValue);
}
}
function doGet(url) {
// make a manual Ajax call
$.ajax({
headers : {
'Authorization' : 'Basic YWRtaW46YWRtaW4='
},
url : 'http://localhost:8081' + url,
type : 'GET',
dataType : 'tex/plain',
beforeSend : function() {
},
success : function(data) {
// text result
response.text(data);
},
complete : function() {
},
error : function(jqXHR) {
// system error
response.text(jqXHR.responseText);
}
})
}
function doPost(url, posted) {
// make a manual Ajax call
$.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) {
// text result
response.text(data);
},
complete : function() {
},
error : function(jqXHR) {
// system error
response.text(jqXHR.responseText);
}
})
}
// document loading
$(document).ready(function() {
// retrieve page component references
url = $("#url");
posted = $("#posted");
response = $("#response");
});
- righe 71–75: codice JavaScript eseguito dopo che il documento ha terminato il caricamento nel browser;
- righe 73-75: recupera i riferimenti di tre elementi nel documento HTML;
- righe 2-5: variabili globali note in tutte le funzioni definite nel file JavaScript;
- riga 9: recupera l'URL inserito dall'utente;
- riga 10: recupera il valore che l'utente desidera inviare;
- riga 11: recupera il metodo [get] o [post] da utilizzare quando si richiede l'URL della riga 9:
- document si riferisce al documento caricato dal browser, noto come DOM (Document Object Model),
- document.forms[0] si riferisce al primo modulo nel documento; un documento può contenere più moduli. Qui ce n'è solo uno;
- document.forms[0].elements['method'] si riferisce all'elemento del modulo con l'attributo [name='method']. Ce ne sono due:
<input type="radio" id="get" name="method" value="get" checked="checked" />GET
<input type="radio" id="post" name="method" value="post" />POST
- (continua)
- document.forms[0].elements['method'].value è il valore che verrà inviato per il componente con l'attributo [name='method']. Sappiamo che il valore inviato è il valore dell'attributo [value] del pulsante di opzione selezionato. Qui, quindi, sarà una delle stringhe ['get', 'post'];
- Righe 13–18: A seconda del metodo HTTP da utilizzare, viene eseguito il metodo [doGet] o [doPost];
- Il metodo jQuery [$.ajax] effettua una richiesta HTTP;
- righe 23–25: stiamo comunicando con un server che richiede un'intestazione HTTP [Authorization: Basic code]. Creiamo questa intestazione per l'utente [admin / admin], che è l'unico autorizzato a interrogare il server;
- riga 26: l'utente inserirà URL del tipo [/getAllLongCategories, /saveCategories, ...]. Questi URL devono quindi essere completati;
- riga 27: metodo HTTP da utilizzare;
- riga 28: il server restituisce JSON. Specifichiamo il tipo [text/plain] come tipo di risposta in modo che venga visualizzato esattamente come ricevuto;
- riga 33: visualizza la risposta testuale del server;
- riga 39: visualizza eventuali messaggi di errore in formato testo;
- riga 44: il metodo [doPost] riceve un secondo parametro, che è il valore da inviare;
- riga 52: per indicare che il valore inviato sarà sotto forma di stringa JSON;
21.2.6. Esecuzione client
L'applicazione client è un'applicazione console avviata dalla seguente classe eseguibile [Client]:
![]() |
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);
}
}
- Riga 6: L'annotazione [@EnableAutoConfiguration] è un'annotazione del progetto [Spring Boot] (riga 4). Spring Boot esaminerà gli archivi presenti nel classpath del progetto. In questo caso, si tratterà di tutte le dipendenze Maven fornite dall'unica dipendenza nel file [pom.xml]:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
Questa dipendenza include un gran numero di librerie, in particolare Spring MVC e un server Tomcat. A causa di queste dipendenze, Spring Boot configurerà un progetto Spring MVC in esecuzione su Tomcat utilizzando i valori predefiniti. Il server Tomcat viene quindi configurato per l'esecuzione sulla porta 8080. Se si desidera sovrascrivere i valori predefiniti scelti da Spring Boot, è possibile utilizzare il file [application.properties] nella radice del classpath (tutto ciò che si trova in [src/main/resources] si trova nella radice del classpath):
![]() |
Specifichiamo che il server Tomcat debba essere in esecuzione sulla porta 8082 come segue:
server.port=8082
Un elenco dei parametri che possono essere utilizzati in [application.properties] è disponibile all'URL (giugno 2015) [http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html];
Torniamo al codice in [Client.java]:
- riga 10: il metodo [SpringApplication.run] distribuirà la pagina [client.html] sul server Tomcat presente nel classpath del progetto;
21.2.7. L'URL [/getAllShortCategories]
![]() |
Avviamo:
- il server web/JSON sicuro sulla porta 8081 (configurazione [spring-security-server-jdbc-generic]);
- il client per questo server sulla porta 8082 (configurazione [spring-cors-client-generic]);
quindi richiediamo l'URL [http://localhost:8082/client.html] [1]:
![]() |
- in [2], eseguiamo una richiesta GET sull'URL [http://localhost:8081/getAllShortCategories];
Non riceviamo alcuna risposta dal server. Quando controlliamo la console degli sviluppatori di Chrome (Ctrl-Shift-I), vediamo un errore:
![]() |
- in [1], ci troviamo nella scheda [Rete];
- In [2], vediamo che la richiesta HTTP effettuata non è [GET] ma [OPTIONS]. Nel caso di una richiesta cross-domain, il browser verifica con il server che determinate condizioni siano soddisfatte inviando una richiesta HTTP [OPTIONS]. In questo caso, le richieste sono quelle indicate dai puntini [5-6];
- In [5], il browser chiede se l'URL di destinazione sia raggiungibile con un GET. La richiesta [Access-Control-Request-Method] richiede una risposta con un'intestazione HTTP [Access-Control-Allow-Methods] che indichi che il metodo richiesto è accettato;
- in [6], il browser invia l'intestazione HTTP [Origin: http://localhost:8081]. Questa intestazione richiede una risposta in un'intestazione HTTP [Access-Control-Allow-Origin] che indichi che l'origine specificata è accettata;
- In [7], il browser chiede se le intestazioni HTTP [Accept] e [Authorization] sono accettate. La richiesta [Access-Control-Request-Headers] si aspetta una risposta con un'intestazione HTTP [Access-Control-Allow-Headers] che indichi che le intestazioni richieste sono accettate;
- si verifica un errore in [3]. Facendo clic sull'icona si ottiene l'errore [4];
- In [4], il messaggio indica che il server non ha inviato l'intestazione HTTP [Access-Control-Allow-Origin], che specifica se l'origine della richiesta è accettata;
- in [8], possiamo vedere che il server effettivamente non ha inviato questa intestazione. Di conseguenza, il browser ha rifiutato di effettuare la richiesta HTTP GET inizialmente richiesta;
Dobbiamo modificare il server web / JSON.
21.2.8. Un nuovo servizio web / JSON
Stiamo creando un nuovo progetto Maven [spring-cors-server-jdbc-generic]:
![]() |
La configurazione Maven per il nuovo servizio web è la seguente:
<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>
- Righe 30–32: recuperiamo tutti i dati relativi al lavoro svolto finora accedendo all'archivio JSON protetto sul server web;
Alla fine, le dipendenze sono le seguenti:
![]() |
La classe di configurazione [AppConfig] è la seguente:
![]() |
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 {
// cross-domain queries
@Bean
public boolean isCorsEnabled() {
return true;
}
...
}
- riga 12: la classe è una classe di configurazione Spring;
- riga 9: altri componenti Spring si trovano nel pacchetto [spring.cors.server.service];
- riga 14: importiamo i bean dal progetto [spring-security-server-jdbc-generic];
- righe 18–21: creiamo un componente Spring denominato [isCorsEnabled] che indica se i client al di fuori del dominio del server sono accettati o meno;
21.2.9. I controller
Il nuovo servizio web dispone di quattro controller:
![]() |
- [CorsCategorieController] gestisce gli URL relativi alle categorie. Si occupa esclusivamente delle intestazioni CORS provenienti dai client web. Per il resto, delega il lavoro al [CategorieController] presente nella dipendenza [spring-webjson-server-jdbc-generic];
- [CorsProductController] e [CorsAuthenticateController] fanno lo stesso delegando il lavoro al [ProductController] nella dipendenza [spring-webjson-server-jdbc-generic] e all'[AuthenticateController] nella dipendenza [spring-security-server-jdbc-generic];
- [CorsController] viene utilizzato per estrarre ciò che è comune ai tre controller precedenti;
21.2.9.1. Il [CorsController]
La classe [CorsController] è la seguente:
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;
// sending options to the customer
public void sendOptions(String origin, HttpServletResponse response) {
// Cors allowed ?
if (!isCorsEnabled || origin == null || !origin.startsWith("http://localhost")) {
return;
}
// set header CORS
response.addHeader("Access-Control-Allow-Origin", origin);
// certain headers are allowed
response.addHeader("Access-Control-Allow-Headers", "accept, authorization");
// we authorize GET
response.addHeader("Access-Control-Allow-Methods", "GET");
}
}
- riga 8: la classe [CorsController] è un controller Spring;
- righe 11-12: iniezione del bean [isCorsEnabled], che indica se gestire o meno le intestazioni CORS;
- righe 15–26: il metodo [sendOptions] gestisce le risposte ai client che inviano intestazioni CORS;
- righe 17-19: se l'applicazione è configurata per accettare richieste cross-domain, se il mittente ha inviato l'intestazione HTTP [Origin] e se tale origine inizia con [http://localhost], allora la richiesta cross-domain viene accettata; altrimenti, viene rifiutata;
- riga 21: se il client si trova nel dominio [http://localhost:port], inviamo l'intestazione HTTP:
Access-Control-Allow-Origin: http://localhost:port
il che significa che il server accetta l'origine del client;
- righe 22–25: abbiamo specificato due header HTTP specifici nella richiesta HTTP [OPTIONS]:
In risposta all'intestazione HTTP [Access-Control-Request-X], il server risponde con un'intestazione HTTP [Access-Control-Allow-X] che specifica ciò che è consentito. Le righe 22–25 ripetono semplicemente la richiesta del client per indicare che è stata accettata;
21.2.9.2. Il 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) {
// original method
return categorieController.getAllShortCategories();
}
...
}
- riga 19: l'annotazione [@RestController] rende la classe sia un componente Spring sia un controller MVC che invia le proprie risposte al client;
- Riga 20: la classe [CorsCategorieController] estende la classe [CorsController] che abbiamo appena visto;
- righe 22–23: iniezione del controller [CategorieController] dalla dipendenza [spring-webjson-server-jdbc-generic];
- righe 25–29: gestiscono l'URL [/cors-getAllShortCategories] quando viene richiesto con il metodo HTTP [OPTIONS]. Per convenzione, stabiliamo che i client web che desiderano chiamare l'URL [/U] del servizio web protetto debbano in realtà chiamare l'URL [/cors-U]. Il servizio web distribuito avrà quindi due tipi di URL:
- [/U]: per i client non web;
- [/cors-U]: per i client web;
- riga 25: il metodo [/cors-getAllShortCategories] accetta i seguenti parametri:
- l'oggetto [@RequestHeader(value = "Origin", required = false)], che recupera l'intestazione HTTP [Origin] dalla richiesta. Questa intestazione è stata inviata dall'origine della richiesta:
Specifichiamo che l'intestazione HTTP [Origin] è facoltativa [required = false]. In questo caso, se l'intestazione manca, il parametro [String origin] avrà un valore nullo. Con [required = true], che è il valore predefinito, viene generata un'eccezione se l'intestazione manca. Volevamo evitare questo scenario;
- (continua)
- l'oggetto [HttpServletResponse response] che verrà restituito al client che ha effettuato la richiesta;
Questi due parametri vengono iniettati da Spring;
- riga 28: deleghiamo la gestione della richiesta al metodo [sendOptions] della classe padre [CorsController];
- righe 31–36: il metodo [getAllShortCategories] gestisce l'URL [/cors-getAllShortCategories] quando viene richiesto con un GET;
- Riga 35: il lavoro viene delegato al metodo [CategorieController.getAllShortCategories] della dipendenza [spring-webjson-server-jdbc-generic];
Ora siamo pronti per ulteriori test. Lanciamo la nuova versione del servizio web e constatiamo che il problema persiste. Non è cambiato nulla. Se aggiungiamo un output della console alla riga 28 sopra, questo non viene mai visualizzato, il che indica che il metodo [corsGetAllShortCategories] alla riga 25 non viene mai chiamato.
Dopo alcune ricerche, scopriamo che Spring MVC gestisce autonomamente le richieste HTTP [OPTIONS] utilizzando la gestione predefinita. Pertanto, è sempre Spring a rispondere, e mai il metodo [corsGetAllShortCategories] alla riga 25. Questo comportamento predefinito di Spring MVC può essere modificato. Modifichiamo la classe [AppConfig] esistente:
![]() |
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 {
// cross-domain queries
@Bean
public boolean isCorsEnabled() {
return true;
}
@Autowired
private DispatcherServlet dispatcherServlet;
@PostConstruct
public void init(){
// the application processes requests itself HTTP [OPTIONS]
dispatcherServlet.setDispatchOptionsRequest(true);
}
}
- righe 23-24: iniettiamo il componente [DispatcherServlet dispatcherServlet], definito nella dipendenza [spring-webjson-server-jdbc-generic];
- righe 26-30: l'annotazione [@PostConstruct] garantisce che il metodo [init] venga eseguito dopo l'istanziazione della classe [AppConfig] e dopo che Spring ha eseguito le sue iniezioni;
- riga 29: configuriamo il servlet per inoltrare le richieste HTTP [OPTIONS] all'applicazione;
Eseguiamo nuovamente i test con questa nuova configurazione. Otteniamo il seguente risultato:
![]() |
- in [1], vediamo che ci sono due richieste HTTP all'URL [http://localhost:8080/getAllCategories];
- in [2], la richiesta [OPTIONS];
- in [3], le tre intestazioni HTTP che abbiamo appena configurato nella risposta del server;
Esaminiamo ora la seconda affermazione:
![]() |
- in [1], la richiesta in esame;
- in [2], si tratta della richiesta GET. Grazie alla prima richiesta [OPTIONS], il browser ha ricevuto le informazioni richieste. Ora esegue la richiesta [GET] inizialmente richiesta;
- in [3], la risposta del server;
- in [4], il server invia JSON;
- in [5], si è verificato un errore;
- in [6], il messaggio di errore;
È più difficile spiegare cosa sia successo in questo caso. La risposta del server [3] è normale [HTTP/1.1 200 OK]. Dovremmo quindi avere il documento richiesto. È possibile che il server abbia effettivamente inviato il documento ma che il browser ne impedisca l'utilizzo perché richiede che, anche per la richiesta GET, la risposta includa l'intestazione HTTP [Access-Control-Allow-Origin:http://localhost:8081].
Modifichiamo il metodo che gestisce la richiesta GET per l'URL [/cors-getAllShortCategories]:
@RequestMapping(value = "/cors-getAllShortCategories", method = RequestMethod.GET)
public Response<List<Categorie>> getAllShortCategories(
@RequestHeader(value = "Origin", required = false) String origin, HttpServletResponse response) {
// headers CORS
sendOptions(origin, response);
// original method
return categorieController.getAllShortCategories();
}
- riga 5: come per la richiesta HTTP [OPTIONS], il server invierà le intestazioni HTTP CORS per una richiesta HTTP [GET];
Dopo questa modifica, i risultati sono i seguenti:
![]() |
Abbiamo ottenuto con successo la versione abbreviata di tutte le categorie.
21.2.9.3. Gli URL [GET]
Nei controller [CorsCategorieController, CorsProduitController, CorsAuthenticateController], il codice delle azioni che gestiscono gli URL [GET] richiesti segue lo schema delle azioni che in precedenza gestivano l'URL [/cors-getAllShortArticles]. Il lettore può verificare il codice negli esempi forniti con questo documento. Ecco un esempio per l'URL [/cors-getAllLongProduits] nel controller [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) {
// headers CORS
sendOptions(origin, response);
// original method
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. URL [POST]
Consideriamo il seguente scenario:
![]() |
- Effettuiamo un POST [1] all'URL [2];
- in [3], il valore inviato. Questa è la stringa JSON per una categoria senza prodotti;
- In definitiva, vogliamo creare una categoria denominata [category[2]];
A questo punto non stiamo modificando alcun codice. Il risultato ottenuto è il seguente:
![]() |
- in [1], come per le richieste [GET], il browser effettua una richiesta [OPTIONS];
- in [2], richiede l'autorizzazione di accesso per una richiesta [POST]. In precedenza era [GET];
- In [3], richiede l'autorizzazione per inviare le intestazioni HTTP [accept, authorization, content-type]. In precedenza, avevamo solo le prime due intestazioni;
- in [4], il servizio web non concede tutte le autorizzazioni richieste, il che causa l'errore [5];
Modifichiamo il metodo [CorsController.sendOptions] come segue:
public void sendOptions(String origin, HttpServletResponse response) {
// Cors allowed ?
if (!isCorsEnabled || origin == null || !origin.startsWith("http://localhost")) {
return;
}
// set header CORS
response.addHeader("Access-Control-Allow-Origin", origin);
// certain headers are allowed
response.addHeader("Access-Control-Allow-Headers", "accept, authorization, content-type");
// we authorize GET and POST
response.addHeader("Access-Control-Allow-Methods", "GET, POST");
}
}
- riga 9: abbiamo aggiunto l'intestazione HTTP [Content-Type] (non fa differenza maiuscolo/minuscolo);
- riga 11: abbiamo aggiunto il metodo HTTP [POST];
Ciò significa che i metodi [POST] vengono gestiti allo stesso modo delle richieste [GET]. Ecco un esempio dell'URL [/cors-saveCategories] nel 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) {
// headers CORS
sendOptions(origin, response);
// original method
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);
}
Una volta apportate queste modifiche, il risultato è il seguente:
![]() |
La categoria [categorie[2]] è stata aggiunta con successo al database. Il DBMS le ha assegnato la chiave primaria 226. È possibile verificarlo utilizzando il metodo GET [/cors-getAllShortCategories]:
![]() |
21.2.10. Conclusione
La nostra applicazione ora supporta le richieste cross-domain. Queste possono essere abilitate o disabilitate tramite la configurazione nella classe [AppConfig]:
package spring.cors.server.config;
...
@Configuration
@ComponentScan(basePackages = { "spring.cors.server.service" })
@Import({ spring.security.config.AppConfig.class })
public class AppConfig {
// cross-domain queries
@Bean
public boolean isCorsEnabled() {
return true;
}
...
}
21.3. Il progetto Eclipse [spring-cors-server-jpa-generic]
Il servizio web CORS sarà ora implementato dal progetto [spring-cors-server-jpa-generic], basato sul progetto [spring-security-server-jpa-generic] che gestisce l'accesso al database utilizzando Spring Data JPA:
![]() |
Il progetto [spring-cors-server-jpa-generic] viene creato clonando il progetto [spring-cors-server-jdbc-generic] precedentemente studiato.
![]() |
Ora bisogna apportare due modifiche. La prima riguarda il file [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>
- righe 30–32: la dipendenza dal servizio web sicuro [spring-security-server-jpa-generic];
Alla fine, le dipendenze del progetto sono le seguenti:
![]() |
Nota: premere Alt-F5 e quindi rigenerare tutti i progetti
La seconda modifica consiste nell'aggiornare le importazioni nelle classi che segnalano errori [Alt-Shift-O].
Questo è tutto. Avviamo il servizio web CORS con la configurazione di runtime [spring-cors-server-jpa-generic-hibernate-eclipselink]:
![]() | ![]() |
Quindi avviamo il client generico:
![]() |
e, utilizzando un browser, richiedi l'URL [1] con un GET. In [2], vediamo che la versione abbreviata delle categorie restituite presenta il campo [entityType], che mancava nella precedente versione JDBC.
Ora tratteremo altre due architetture CORS:
- Architettura CORS / JPA EclipseLink / DB2;
- Architettura CORS / JPA OpenJpa / Firebird;
21.4. Architettura CORS / JPA EclipseLink / DB2
Implementeremo la seguente architettura:
![]() |
Caricare i seguenti progetti:
![]() |
Nota: premi Alt-F5 e rigenera tutti i progetti Maven.
Avvia il database DB2 e verifica che il database [dbproduitscategories] esista. In caso contrario, crealo (sezione 12.1.2).
Creiamo gli utenti nel database [dbproduitscategories] utilizzando la configurazione di runtime [spring-security-create-users-hibernate-eclipselink]:
![]() | ![]() |

Quindi avvia il servizio web CORS utilizzando la configurazione di runtime denominata [spring-cors-server-jpa-generic-hibernate-eclipselink] e il relativo client denominato [spring-cors-client-generic]:
![]() | ![]() |
Piena il database [dbproduitscategories] con i valori utilizzando la configurazione di runtime [spring-jdbc-generic-04-fillDataBase]:
![]() |
Infine, richiedere il seguente URL in un browser:
![]() |
21.5. CORS / JPA OpenJPA / Architettura Firebird
Ora implementeremo la seguente architettura:
![]() |
Caricare i seguenti progetti:
![]() |
Nota: premi Alt-F5 e rigenera tutti i progetti Maven.
Avvia il DBMS Firebird e verifica che il database [dbproduitscategories] esista. In caso contrario, crealo (sezione 14.1.2).
Creiamo gli utenti nel database [dbproduitscategories] utilizzando la configurazione di runtime [spring-security-create-users-openjpa]:
![]() | ![]() |

Quindi avviare il servizio web CORS con la configurazione di runtime denominata [spring-cors-server-jpa-generic-openjpa]:
![]() | ![]() |
Avvia il client CORS utilizzando la configurazione [spring-cors-client-generic]:
![]() |
Popolare il database [dbproduitscategories] con i valori utilizzando la configurazione di runtime [spring-jdbc-generic-04-fillDataBase]:
![]() |
Infine, richiedere il seguente URL in un browser:
![]() |


























































