21. Gestione degli accessi tra domini
21.1. Architecture
Esamineremo ora il problema delle richieste tra domini. Nel documento [Tutoriel AngularJS / Spring 4] viene sviluppata 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 perché costituisce una falla di sicurezza. L’applicazione può interrogare solo il server da cui proviene, ovvero il server [1];
In realtà, non è esatto affermare che il browser impedisca all’applicazione Angular di interrogare il server [2]. Il browser, infatti, interroga il server [2] per sapere se autorizza un client che non proviene dalla sua stessa origine a interrogarlo. Questa tecnica di condivisione è denominata CORS (Cross-Origin Resource Sharing). Il server [2] dà il proprio consenso inviando specifici header HTTP.
Per illustrare i problemi che si possono incontrare, creeremo un'applicazione client/server in cui:
- il server sarà il nostro server web / jSON protetto;
- il client sarà una semplice pagina HTML dotata di un codice JavaScript che effettuerà richieste al server web / jSON;
Implementeremo la seguente architettura:
![]() |
- in [1], un'applicazione web fornisce le pagine HTML / jS;
- in [2], il browser esegue il JavaScript incorporato nelle pagine HTML per interrogare il servizio web protetto [3];
21.2. Il progetto [spring-cors-server-jdbc-generic]
21.2.1. Configurazione dell’ambiente di lavoro
![]() |
- caricare i progetti sopra indicati. I progetti [spring-cors-*] si trovano nella cartella [<exemples>\spring-database-generic\spring-cors];
- premere Alt-F5 e rigenerare tutti i progetti Maven;
Eseguire quindi la configurazione di esecuzione denominata [spring-cors-server-jdbc-generic] (devono essere avviati anche SGBD e MySQL), che avvia un servizio web sulla porta 8081:
![]() |
Compilare la base [dbproduitscategories] con la configurazione di esecuzione denominata [spring-jdbc-generic-04-fillDataBase]:
![]() |
Eseguire la configurazione di esecuzione denominata [spring-cors-client-generic] che avvia una seconda applicazione web (su un altro Tomcat) sulla porta 8082:
![]() |
Con un browser, richiedete l'URL [http://localhost:8082/client.html]:
![]() |
- in [1], si richiede la versione breve di tutte le categorie;
- con [2], si ottiene la risposta jSON dal server;
21.2.2. Il progetto del cliente [spring-cors-client-generic]
![]() |
![]() |
Il file [application.properties] ci permette di impostare la porta dell'applicazione web client. Il suo contenuto è il seguente:
server.port=8082
Quindi:
- il client è un'applicazione web disponibile all'indirizzo URL [http://localhost:8082];
- il server è un'applicazione web disponibile all'indirizzo URL [http://localhost:8081];
Poiché il client non viene raggiunto dalla stessa porta del server, si presenta il problema delle richieste interdominio. Infatti, [http://localhost:8081] e [http://localhost:8082] sono due domini diversi.
21.2.3. Configurazione 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 /> <!-- ricerca del genitore dal 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>
<!-- plugin -->
<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: si tratta di un progetto Spring Boot;
- righe 27-30: si utilizza 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 unica:
![]() |
Include codice JavaScript (jS) eseguito nel browser. Presenteremo alcune nozioni di base di JavaScript che ci permetteranno di comprendere il codice. Il client effettuerà chiamate HTTP utilizzando la libreria jQuery [https://jquery.com/], che offre numerose funzioni che facilitano lo sviluppo in JavaScript. Creiamo un file statico HTML [jQuery.html] che 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 id [element1]. Ora sperimenteremo con questo elemento.
Dobbiamo scaricare il file [jquery-2.1.3.min.js]. Troveremo l’ultima versione di jQuery in URL [http://jquery.com/download/]:

Il file scaricato va inserito nella cartella [static / js]:
![]() |
Fatto ciò, si richiederà la visualizzazione statica [jQuery.html] con Chrome [1-2]:
![]() |
Con Google Chrome, digitare [Ctrl-Maj-I] per visualizzare gli strumenti di sviluppo [3]. La scheda [Console] [4] consente di eseguire codice JavaScript. Di seguito forniamo alcuni comandi JavaScript da digitare e ne forniamo una spiegazione.
|
: restituisce la collezione di tutti gli elementi con id [element1], quindi normalmente una collezione 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. Ciò ha l’effetto di modificare il contenuto visualizzato dalla pagina | ![]() |
|
nasconde gli elementi della collezione. Il testo [blabla] non viene più visualizzato. | ![]() |
|
: visualizza nuovamente la collezione. Questo ci permette di vedere che l'elemento con id [element1] ha l'attributo CSS style='display: none;', che fa che l'elemento sia nascosto. | |
|
: visualizza gli elementi della collezione. Il testo [blabla] riappare. È l'attributo CSS style='display: block;' che garantisce questa visualizzazione. | ![]() |
|
: assegna un attributo a 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 il codice URL del browser non è cambiato durante tutte queste operazioni. Non ci sono stati scambi 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 iniziale. Non riflette in alcun modo le modifiche apportate all’elemento nelle righe 10-12. È importante tenerlo presente quando si esegue il debug di JavaScript. Spesso, quindi, è inutile visualizzare il codice sorgente della pagina visualizzata.
21.2.5. Il codice jS 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">
<!-- metodo 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 />
<!-- valore inserito -->
<br /> Chaîne jSON à poster : <input type="text" id="posted" size="50" />
<!-- pulsante di conferma -->
<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: si importa la libreria jQuery;
- riga 7: si importa un codice che scriveremo noi;
- righe 11, 15, 17, 21: si annotano gli identificatori [id] dei componenti della pagina. Il codice JavaScript fa riferimento a questi componenti tramite tali identificatori;
Il codice [client.js] è il seguente:
// dati generali
var url;
var posted;
var response;
var method;
function requestServer() {
// si recuperano le informazioni dal modulo
var urlValue = url.val();
var postedValue = posted.val();
method = document.forms[0].elements['method'].value;
// si effettua manualmente una richiesta Ajax
if (method === "get") {
doGet(urlValue);
} else {
doPost(urlValue, postedValue);
}
}
function doGet(url) {
// si effettua una chiamata Ajax manualmente
$.ajax({
headers : {
'Authorization' : 'Basic YWRtaW46YWRtaW4='
},
url : 'http://localhost:8081' + url,
type : 'GET',
dataType : 'tex/plain',
beforeSend : function() {
},
success : function(data) {
// risultato testo
response.text(data);
},
complete : function() {
},
error : function(jqXHR) {
// errore di sistema
response.text(jqXHR.responseText);
}
})
}
function doPost(url, posted) {
// si effettua manualmente una chiamata Ajax
$.ajax({
headers : {
'Autorizzazione: 'Basic YWRtaW46YWRtaW4='
},
url : 'http://localhost:8081 ' + url,
type : 'POST',
contentType : 'application/json',
data : posted,
dataType : 'tex/plain',
beforeSend : function() {
},
success : function(data) {
// risultato testo
response.text(data);
},
complete : function() {
},
error : function(jqXHR) {
// errore di sistema
response.text(jqXHR.responseText);
}
})
}
// durante il caricamento del documento
$(document).ready(function() {
// si recuperano i riferimenti dei componenti della pagina
url = $("#url");
posted = $("#posted");
response = $("#response");
});
- righe 71-75: codice jS eseguito al termine del caricamento del documento nel browser;
- righe 73-75: si recuperano i riferimenti di tre delle aree del documento HTML;
- righe 2-5: variabili globali note in tutte le funzioni definite nel file jS;
- riga 9: si recupera il valore URL digitato dall'utente;
- riga 10: si recupera il valore che l'utente desidera inviare;
- riga 11: si recupera il metodo [get] o [post] da utilizzare per richiedere il valore URL della riga 9:
- "document" indica il documento caricato dal browser, ovvero il DOM (Document Object Model),
- document.forms[0] indica il primo modulo del documento; un documento può contenerne diversi. In questo caso ce n’è solo uno,
- document.forms[0].elements['method'] indica l’elemento del modulo che possiede 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 che possiede l’attributo [name='method']. Si sa che il valore inviato è il valore dell’attributo [value] del pulsante di opzione selezionato. In questo caso, sarà quindi una delle stringhe ['get', 'post'];
- righe 13-18: a seconda del metodo HTTP da utilizzare, si esegue il metodo [doGet] o [doPost];
- il metodo jQuery [$.ajax] effettua una chiamata HTTP;
- righe 23-25: ci si rivolge a un server che richiede un'intestazione HTTP [Authorization: Basic code]. Creiamo questa intestazione per l'utente [admin / admin], l'unico autorizzato a interrogare il server;
- riga 26: l'utente inserirà dei URL del tipo [/getAllLongCategories, /saveCategories, ...]. È quindi necessario completare questi URL;
- riga 27: metodo HTTP da utilizzare;
- riga 28: il server restituisce jSON. Si indica il tipo [text/plain] come tipo di risultato per visualizzarlo così come è stato ricevuto;
- riga 33: visualizzazione della risposta testuale del server;
- riga 39: visualizzazione di un eventuale messaggio 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 del client
L'applicazione client è un'applicazione da 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à i file presenti nel Classpath del progetto. In questo caso si tratterà di tutte le dipendenze Maven fornite dall'unica dipendenza del file [pom.xml]:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
Questa dipendenza introduce numerosi file, in particolare Spring MVC e un server Tomcat. A causa della presenza di queste dipendenze, Spring Boot configurerà, con i valori predefiniti, un progetto Spring MVC in esecuzione su Tomcat. Il server Tomcat viene quindi configurato per funzionare sulla porta 8080. Se si desidera ignorare 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):
![]() |
Indichiamo che il server Tomcat deve funzionare sulla porta 8082 nel modo seguente:
server.port=8082
L'elenco dei parametri utilizzabili è disponibile in [application.properties], URL (giugno 2015) e [http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html];
Torniamo al codice di [Client.java]:
- riga 10: il metodo [SpringApplication.run] distribuirà la pagina [client.html] sul server Tomcat presente nel Classpath del progetto;
21.2.7. URL [/getAllShortCategories]
![]() |
Avviamo:
- il server web/json protetto sulla porta 8081 (configurazione [spring-security-server-jdbc-generic]);
- il client di questo server sulla porta 8082 (configurazione [spring-cors-client-generic]);
quindi richiediamo URL [http://localhost:8082/client.html] [1]:
![]() |
- in [2], eseguiamo un GET su URL e [http://localhost:8081/getAllShortCategories];
Non riceviamo alcuna risposta dal server. Osservando la console di sviluppo di Chrome (Ctrl-Maiusc-I) si rileva un errore:
![]() |
- in [1], ci troviamo nella scheda [Network];
- in [2], si nota che la richiesta HTTP effettuata non è [GET] ma [OPTIONS]. Nel caso di una richiesta interdominio, il browser verifica con il server che siano soddisfatte una serie di condizioni inviandogli una richiesta HTTP [OPTIONS]. In questo caso, le richieste sono quelle indicate dai simboli [5-6];
- In [5], il browser chiede se la destinazione URL 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 con 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] attende una risposta con un'intestazione HTTP [Access-Control-Allow-Headers] che indichi che le intestazioni richieste sono accettate;
- si verifica un errore in [3]. Cliccando 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], si può constatare che il server non ha effettivamente inviato tale intestazione. Di conseguenza, il browser ha rifiutato di eseguire la richiesta HTTP GET inizialmente richiesta;
Dobbiamo modificare il server web / jSON.
21.2.8. Un nuovo servizio web / json
Creiamo un nuovo progetto Maven [spring-cors-server-jdbc-generic]:
![]() |
La configurazione Maven del 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>
<!-- plugin -->
<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 tutto il lavoro svolto finora attingendo all'archivio del server web / json protetto;
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 {
// richieste interdominio
@Bean
public boolean isCorsEnabled() {
return true;
}
...
}
- riga 12: la classe è una classe di configurazione Spring;
- riga 9: altri componenti Spring sono da ricercare nel pacchetto [spring.cors.server.service];
- riga 14: si importano i bean dal progetto [spring-security-server-jdbc-generic];
- righe 18-21: si crea un componente Spring denominato [isCorsEnabled] che indica se accettare o meno i client esterni al dominio del server;
21.2.9. I controller
Il nuovo servizio web dispone di quattro controller:
![]() |
- [CorsCategorieController] gestisce il URL relativo all’elaborazione delle categorie. Gestisce solo le intestazioni CORS dei client web. In caso contrario, delega il lavoro al controller [CategorieController] della dipendenza [spring-webjson-server-jdbc-generic];
- [CorsProduitController] e [CorsAuthenticateController] fanno lo stesso, delegando il lavoro ai controller [ProduitController] della dipendenza [spring-webjson-server-jdbc-generic] e [AuthenticateController] della dipendenza[spring-security-server-jdbc-generic];
- [CorsController] serve a estrarre ciò che è comune ai tre controllori precedenti;
21.2.9.1. Il controller [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;
// invio delle opzioni al client
public void sendOptions(String origin, HttpServletResponse response) {
// CORS consentito?
if (!isCorsEnabled || origin == null || !origin.startsWith("http://localhost")) {
return;
}
// si imposta l'header CORS
response.addHeader("Access-Control-Allow-Origin", origin);
// si autorizzano determinati header
response.addHeader("Access-Control-Allow-Headers", "accept, authorization");
// si autorizza il 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] si occupa di rispondere ai client che inviano le intestazioni CORS;
- righe 17-19: se l'applicazione è configurata per accettare le richieste interdominio e se il mittente ha inviato l'intestazione HTTP [Origin] e se tale origine inizia con [http://localhost], allora si accetta la richiesta inter-dominio, altrimenti la si rifiuta;
- riga 21: se il client si trova nel dominio [http://localhost:port], si invia l'intestazione HTTP:
il che significa che il server accetta l’origine del client;
- righe 22-25: abbiamo segnalato due intestazioni HTTP particolari nella richiesta HTTP [OPTIONS]:
All'intestazione HTTP [Access-Control-Request-X], il server risponde con un'intestazione HTTP [Access-Control-Allow-X] in cui indica ciò che è consentito. Le righe 22-25 si limitano a riprendere 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) {
// metodo di origine
return categorieController.getAllShortCategories();
}
...
}
- riga 19: l’annotazione [@RestController] rende la classe sia un componente Spring sia un controller MVC che invia a sua volta le risposte al cliente;
- riga 20: la classe [CorsCategorieController] estende la classe [CorsController] che abbiamo appena visto;
- righe 22-23: iniezione del controller [CategorieController categorieController] dalla dipendenza [spring-webjson-server-jdbc-generic];
- righe 25-29: gestiscono URL [/cors-getAllShortCategories] quando viene richiesto con il comando HTTP [OPTIONS]. Per convenzione, stabiliamo che i client web che desiderano richiamare l'URL [/U] del servizio web protetto, dovranno in realtà richiamare l'URL [/cors-U]. Il servizio web implementato 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 come parametri:
- l’oggetto [@RequestHeader(value = "Origin", required = false)] che recupererà l’intestazione HTTP [Origin] della richiesta. Tale intestazione è stata inviata dal mittente della richiesta:
Si precisa che l'intestazione HTTP [Origin] è facoltativa [required = false]. In questo caso, se l'intestazione è assente, il parametro [String origin] assumerà il valore null. Con [required = true], che è il valore predefinito, viene generata un'eccezione se l'intestazione è assente. Si è voluto evitare questo caso;
- (continua)
- l’oggetto [HttpServletResponse response] che verrà restituito al cliente che ha effettuato la richiesta;
Questi due parametri vengono inseriti da Spring;
- riga 28: si delega l’elaborazione della richiesta al metodo [sendOptions] della classe padre [CorsController];
- righe 31-36: il metodo [getAllShortCategories] elabora URL e [/cors-getAllShortCategories] quando viene richiamato con un GET;
- riga 35: l'operazione viene delegata al metodo [CategorieController.getAllShortCategories] della dipendenza [spring-webjson-server-jdbc-generic];
Ora siamo pronti per nuovi test. Lanciamo la nuova versione del servizio web e scopriamo che il problema persiste. Non è cambiato nulla. Se alla riga 28 sopra inseriamo un output di console, questo non viene mai visualizzato, dimostrando così che il metodo [corsGetAllShortCategories] della riga 25 non viene mai chiamato.
Dopo alcune ricerche, scopriamo che Spring MVC gestisce autonomamente i comandi HTTP e [OPTIONS] con un'elaborazione 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 {
// richieste tra domini
@Bean
public boolean isCorsEnabled() {
return true;
}
@Autowired
private DispatcherServlet dispatcherServlet;
@PostConstruct
public void init(){
// l'applicazione gestisce autonomamente le richieste HTTP [OPTIONS]
dispatcherServlet.setDispatchOptionsRequest(true);
}
}
- righe 23-24: si inietta il componente [DispatcherServlet dispatcherServlet] che è stato definito nella dipendenza [spring-webjson-server-jdbc-generic];
- righe 26-30: l'annotazione [@PostConstruct] fa sì che il metodo [init] venga eseguito dopo l'istanziazione della classe [AppConfig] e dopo le iniezioni effettuate da Spring;
- riga 29: si richiede che il servlet inoltri all'applicazione i comandi HTTP e [OPTIONS];
Ripetiamo i test con questa nuova configurazione. Si ottiene il seguente risultato:
![]() |
- in [1], si nota che ci sono due richieste HTTP verso URL e [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 richiesta:
![]() |
- in [1], la richiesta esaminata;
- in [2], ovvero la richiesta GET. Grazie alla prima richiesta [OPTIONS], il browser ha ricevuto le informazioni richieste. Ora esegue la richiesta [GET] richiesta inizialmente;
- 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 [3] del server è normale, [HTTP/1.1 200 OK]. Dovremmo quindi avere il documento richiesto. È possibile che il server abbia effettivamente inviato il documento, ma che sia il browser a impedirne l’utilizzo perché richiede che anche per la richiesta GET la risposta contenga l’intestazione HTTP [Access-Control-Allow-Origin:http://localhost:8081].
Modifichiamo il metodo che gestisce GET da URL a [/cors-getAllShortCategories]:
@RequestMapping(value = "/cors-getAllShortCategories", method = RequestMethod.GET)
public Response<List<Categorie>> getAllShortCategories(
@RequestHeader(value = "Origin", required = false) String origin, HttpServletResponse response) {
// intestazioni CORS
sendOptions(origin, response);
// metodo di origine
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 correttamente la versione abbreviata di tutte le categorie.
21.2.9.3. I URL [GET]
Nei controller [CorsCategorieController, CorsProduitController, CorsAuthenticateController], il codice delle azioni che gestiscono le richieste URL con un [GET] segue il modello delle azioni che in precedenza hanno gestito URL e [/cors-getAllShortArticles]. Il lettore può verificare il codice negli esempi forniti con il presente documento. Di seguito è riportato un esempio per i codici URL e [/cors-getAllLongProduits] del 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) {
// intestazioni CORS
sendOptions(origin, response);
// metodo di origine
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. I URL [POST]
Esaminiamo il seguente caso:
![]() |
- si effettua un trasferimento da POST [1] verso URL [2];
- in [3], il valore pubblicato. Si tratta della stringa jSON di una categoria senza prodotti;
- in definitiva, si cerca di creare una categoria denominata [categorie[2]];
Per il momento non modifichiamo alcun codice. Il risultato ottenuto è quindi il seguente:
![]() |
- in [1], come per le richieste [GET], il browser effettua una richiesta [OPTIONS];
- da [2], richiede un'autorizzazione di accesso per una richiesta [POST]. In precedenza era [GET];
- in [3], richiede l’autorizzazione a inviare le intestazioni HTTP e [accept, authorization, content-type]. In precedenza erano presenti solo le prime due intestazioni;
- in [4], il servizio web non concede tutte le autorizzazioni richieste, causando l'errore [5];
Modifichiamo il metodo [CorsController.sendOptions] nel modo seguente:
public void sendOptions(String origin, HttpServletResponse response) {
// Cors consentito?
if (!isCorsEnabled || origin == null || !origin.startsWith("http://localhost")) {
return;
}
// si imposta l'intestazione CORS
response.addHeader("Access-Control-Allow-Origin", origin);
// si autorizzano alcune intestazioni
response.addHeader("Access-Control-Allow-Headers", "accept, authorization, content-type");
// si autorizzano GET e POST
response.addHeader("Access-Control-Allow-Methods", "GET, POST");
}
}
- riga 9: è stata aggiunta l'intestazione HTTP [Content-Type] (le maiuscole e le minuscole non fanno differenza);
- riga 11: è stato aggiunto il metodo HTTP [POST];
In questo modo i metodi [POST] vengono trattati allo stesso modo delle richieste [GET]. Ecco un esempio dei metodi URL e [/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) {
// intestazioni CORS
sendOptions(origin, response);
// metodo di origine
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 ottenuto è il seguente:
![]() |
La categoria [categorie[2]] è stata correttamente aggiunta al database. Il codice SGBD le ha assegnato la chiave primaria 226. È possibile verificarlo con il metodo GET [/cors-getAllShortCategories]:
![]() |
21.2.10. Conclusione
La nostra applicazione ora supporta le richieste interdominio. Queste possono essere autorizzate o meno tramite 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 {
// richieste interdominio
@Bean
public boolean isCorsEnabled() {
return true;
}
...
}
21.3. Il progetto Eclipse [spring-cors-server-jpa-generic]
Il servizio web CORS verrà ora implementato dal progetto [spring-cors-server-jpa-generic], che si basa sul progetto [spring-security-server-jpa-generic], il quale gestisce gli accessi al database con Spring Data JPA:
![]() |
Il progetto [spring-cors-server-jpa-generic] è stato ottenuto tramite clonazione del progetto precedentemente esaminato, [spring-cors-server-jdbc-generic].
![]() |
Successivamente, occorre 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>
<!-- plugin -->
<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 protetto [spring-security-server-jpa-generic];
Alla fine, le dipendenze del progetto sono le seguenti:
![]() |
Nota: premere Alt-F5, quindi rigenerare tutti i progetti
La seconda modifica consiste nell'aggiornare le importazioni nelle classi che segnalano errori [Alt-Maj-O].
Questo è tutto. Si avvia il servizio web CORS con la configurazione di esecuzione [spring-cors-server-jpa-generic-hibernate-eclipselink]:
![]() | ![]() |
Quindi si avvia il client generico:
![]() |
e, tramite un browser, si richiedono i dati URL, [1] e GET. In [2], si nota che la versione abbreviata delle categorie restituite contiene il campo [entityType], che non era presente nella versione precedente JDBC.
Esamineremo 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:
![]() |
Carichiamo i seguenti progetti:
![]() |
Nota: premere Alt-F5 e rigenerare tutti i progetti Maven.
Avviare SGBD e DB2 e verificare che il database [dbproduitscategories] esista effettivamente. In caso contrario, crearlo (paragrafo 12.1.2).
Gli utenti vengono creati nel database [dbproduitscategories] con la configurazione di esecuzione [spring-security-create-users-hibernate-eclipselink]:
![]() | ![]() |

Avviare quindi il servizio web CORS con la configurazione di esecuzione denominata [spring-cors-server-jpa-generic-hibernate-eclipselink] e il relativo client denominato [spring-cors-client-generic]:
![]() | ![]() |
Compilare il database [dbproduitscategories] con i valori utilizzando la configurazione di esecuzione [spring-jdbc-generic-04-fillDataBase]:
![]() |
Infine, richiamare in un browser la seguente URL:
![]() |
21.5. Architettura CORS / JPA OpenJPA / Firebird
Ora implementeremo la seguente architettura:
![]() |
Carichiamo i seguenti progetti:
![]() |
Nota: premere Alt-F5 e rigenerare tutti i progetti Maven.
Avviare SGBD Firebird e verificare che il database [dbproduitscategories] esista effettivamente. In caso contrario, crearlo (paragrafo 14.1.2).
Si creano gli utenti nel database [dbproduitscategories] con la configurazione di esecuzione [spring-security-create-users-openjpa]:
![]() | ![]() |

Avviare quindi il servizio web CORS con la configurazione di esecuzione denominata [spring-cors-server-jpa-generic-openjpa]:
![]() | ![]() |
Avviare il client CORS con la configurazione [spring-cors-client-generic]:
![]() |
Compilare la base [dbproduitscategories] con i valori utilizzando la configurazione di esecuzione [spring-jdbc-generic-04-fillDataBase]:
![]() |
Infine, richiamare in un browser la seguente URL:
![]() |


























































