21. Etki alanları arası erişim yönetimi
21.1. Architecture
Şimdi etki alanları arası istekler sorununu inceleyeceğiz. [Tutoriel AngularJS / Spring 4] belgesinde, istemcinin AngularJS uygulaması olduğu bir istemci/sunucu uygulaması geliştirilmektedir:
![]() |
- Angular uygulamasının HTML / CSS / JS sayfaları, [1] sunucusundan gelmektedir;
- [2] sayfasında, [dao] hizmeti başka bir sunucuya, yani [2] sunucusuna bir istek gönderiyor. Ancak bu durum, Angular uygulamasını çalıştıran tarayıcı tarafından yasaklanmaktadır çünkü bu bir güvenlik açığıdır. Uygulama yalnızca geldiği sunucuya, yani [1] sunucusuna sorgu gönderebilir;
Aslında, tarayıcının Angular uygulamasının [2] sunucusuna sorgu göndermesini yasakladığını söylemek doğru değildir. Tarayıcı aslında [2] sunucusuna, kendisinden gelmeyen bir istemcinin kendisine sorgu göndermesine izin verip vermediğini sorar. Bu paylaşım tekniğine CORS (Cross-Origin Resource Sharing) denir. [2] sunucusu, belirli HTTP başlıklarını göndererek onay verir.
Karşılaşılabilecek sorunları göstermek için, aşağıdaki özelliklere sahip bir istemci/sunucu uygulaması oluşturacağız:
- sunucu, güvenli web sunucumuz / jSON olacaktır;
- istemci, web sunucusuna / jSON istekler gönderecek bir JavaScript koduyla donatılmış basit bir HTML sayfası olacaktır;
Aşağıdaki mimariyi kuracağız:
![]() |
- [1]'te bir web uygulaması, HTML / jS sayfalarını sunar;
- [2]'te, tarayıcı HTML sayfalarında yer alan JavaScript'i çalıştırarak güvenli web hizmeti [3]'e sorgu gönderir;
21.2. [spring-cors-server-jdbc-generic] projesi
21.2.1. Çalışma ortamının kurulması
![]() |
- Yukarıdaki projeleri yükleyin. [spring-cors-*] projeleri, [<exemples>\spring-database-generic\spring-cors] klasöründe bulunur;
- Alt-F5 tuşlarına basın ve tüm Maven projelerini yeniden oluşturun;
Ardından, 8081 numaralı bağlantı noktasında bir web hizmeti başlatan [spring-cors-server-jdbc-generic] adlı çalıştırma yapılandırmasını çalıştırın (SGBD ve MySQL'in de başlatılması gerekir):
![]() |
[dbproduitscategories] veritabanını, [spring-jdbc-generic-04-fillDataBase] adlı yürütme yapılandırmasıyla doldurun:
![]() |
8082 numaralı bağlantı noktasında (başka bir Tomcat üzerinde) ikinci bir web uygulamasını başlatan [spring-cors-client-generic] adlı yürütme yapılandırmasını çalıştırın:
![]() |
Bir tarayıcı kullanarak URL ve [http://localhost:8082/client.html] adreslerini çağırın:
![]() |
- [1] adresinde, tüm kategorilerin kısa versiyonu istenir;
- [2] ile sunucudan jSON yanıtı istenir;
21.2.2. Müşterinin projesi [spring-cors-client-generic]
![]() |
![]() |
[application.properties] dosyası, istemci web uygulamasının bağlantı noktasını ayarlamamızı sağlar. İçeriği şöyledir:
server.port=8082
Yani:
- İstemci, URL [http://localhost:8082] adresinde erişilebilen bir web uygulamasıdır;
- sunucu, URL ve [http://localhost:8081] adreslerinde erişilebilen bir web uygulamasıdır;
İstemci, sunucuyla aynı bağlantı noktasından erişilemediği için etki alanları arası istek sorunu ortaya çıkmaktadır. Nitekim, [http://localhost:8081] ve [http://localhost:8082] iki farklı etki alanıdır.
21.2.3. Maven Yapılandırması
Proje, aşağıdaki [pom.xml] dosyasını içeren bir Maven projesidir:
<?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 /> <!-- depo'dan üst öğeyi ara -->
</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>
<!-- eklentiler -->
<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>
- 14-19. satırlar: Bu bir Spring Boot projesidir;
- 27-30. satırlar: [spring-boot-starter-web] bağımlılığı kullanılıyor; bu bağımlılık, bir Tomcat sunucusu ve Spring MVC'i beraberinde getiriyor;
21.2.4. jQuery ve JavaScript'in temelleri
![]() |
Web uygulaması aşağıdaki tek sayfayı görüntüler:
![]() |
Bu sayfa, tarayıcıda çalıştırılan bir JavaScript kodu (jS) içerir. Kodu anlamamıza yardımcı olacak bazı temel JavaScript kavramlarını ele alacağız. İstemci, JavaScript geliştirmeyi kolaylaştıran birçok işlev sunan jQuery ve [https://jquery.com/] kütüphanelerini kullanarak HTTP çağrıları yapacaktır. HTML ve [jQuery.html] adlı statik dosyaları oluşturup bunları [static] klasörüne yerleştiriyoruz:
![]() |
Bu dosyanın içeriği şu şekilde olacaktır:
<!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>
- 6. satır: jQuery dosyasının içe aktarılması;
- 10-12. satırlar: [element1] kimliğine sahip bir sayfa öğesi. Bu öğeyle biraz oynayacağız.
[jquery-2.1.3.min.js] dosyasını indirmemiz gerekiyor. jQuery'in en son sürümünü URL ve [http://jquery.com/download/] dosyalarında bulabiliriz:

İndirdiğimiz dosyayı [static / js] klasörüne yerleştireceğiz:
![]() |
Bu işlem tamamlandıktan sonra, Chrome ile [jQuery.html] statik görünümünü çağırın:
![]() |
Google Chrome'da, geliştirme araçlarını görüntülemek için [Ctrl-Maj-I] komutunu girin: [3]. [Console] ve [4] sekmeleri, JavaScript kodunu çalıştırmanıza olanak tanır. Aşağıda, girmeniz gereken JavaScript komutlarını ve bunların açıklamalarını veriyoruz.
|
: id'si [element1], yani normalde 0 veya 1 öğeden oluşur; çünkü bir sayfada bir sayfada iki aynı id bulunamaz. HTML. | ![]() |
|
: [blabla] metnini koleksiyondaki tüm öğelere . Bu, sayfanın görüntülediği sayfanın görüntülediği içeriği değiştirir | ![]() |
|
koleksiyondaki öğeleri gizler. [blabla] artık görüntülenmiyor. | ![]() |
|
: koleksiyonu yeniden görüntüler. Bu sayede id'si [element1] olan öğenin CSS style='display: none;' özniteliğine sahip olduğunu ve bu özniteliğin bu öğenin gizlenmesine neden olur. | |
|
: koleksiyondaki öğeleri görüntüler. [blabla] metni yeniden görünür. Bunu sağlayan CSS style='display: block;' özniteliği bu görüntülemeyi sağlar. | ![]() |
|
: koleksiyondaki tüm öğelere bir öznitelik atar . Burada öznitelik [style]'tir ve değeri [color: red]'tir. [blabla] metni kırmızıya döner. | ![]() |
![]() | |
![]() |
Tüm bu işlemler sırasında tarayıcının URL değerinin değişmediğine dikkat edilmelidir. Web sunucusuyla herhangi bir veri alışverişi gerçekleşmemiştir. Her şey tarayıcının içinde gerçekleşmektedir. Şimdi sayfanın kaynak koduna bir göz atalım:
<!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>
Bu, başlangıçtaki metindir. 10-12. satırlardaki öğe üzerinde yaptığımız değişiklikleri hiçbir şekilde yansıtmamaktadır. JavaScript hata ayıklaması yaparken bunu akılda tutmak önemlidir. Bu nedenle, görüntülenen sayfanın kaynak kodunu görüntülemek çoğu zaman gereksizdir.
21.2.5. Uygulamanın jS kodu
Şimdi, /jSON web hizmetini sorgulayacak olan istemci uygulamasının sayfasındaki HTML koduna geri dönelim:
![]() |
<!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">
<!-- HTTP yöntemi -->
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 />
<!-- gönderilen değer -->
<br /> Chaîne jSON à poster : <input type="text" id="posted" size="50" />
<!-- onay düğmesi -->
<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>
- 6. satır: jQuery kütüphanesini içe aktarıyoruz;
- 7. satır: yazacağımız bir kodu içe aktarıyoruz;
- 11., 15., 17. ve 21. satırlar: Sayfanın bileşenlerinin [id] tanımlayıcılarını not ediyoruz. JavaScript, bu tanımlayıcılar aracılığıyla bu bileşenlere başvurur;
[client.js] kodu şöyledir:
// genel veriler
var url;
var posted;
var response;
var method;
function requestServer() {
// formdaki bilgiler alınır
var urlValue = url.val();
var postedValue = posted.val();
method = document.forms[0].elements['method'].value;
// Ajax çağrısı manuel olarak yapılır
if (method === "get") {
doGet(urlValue);
} else {
doPost(urlValue, postedValue);
}
}
function doGet(url) {
// Ajax çağrısı manuel olarak yapılır
$.ajax({
headers : {
'Authorization' : 'Basic YWRtaW46YWRtaW4='
},
url : 'http://localhost:8081' + url,
type : 'GET',
dataType : 'tex/plain',
beforeSend : function() {
},
success : function(data) {
// metin sonucu
response.text(data);
},
complete : function() {
},
error : function(jqXHR) {
// sistem hatası
response.text(jqXHR.responseText);
}
})
}
function doPost(url, posted) {
// Ajax çağrısı manuel olarak yapılıyor
$.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) {
// metin sonucu
response.text(data);
},
complete : function() {
},
error : function(jqXHR) {
// sistem hatası
response.text(jqXHR.responseText);
}
})
}
// belge yüklenirken
$(document).ready(function() {
// sayfanın bileşenlerinin referansları alınırken
url = $("#url");
posted = $("#posted");
response = $("#response");
});
- 71-75. satırlar: Belgenin tarayıcıya yüklenmesi tamamlandığında çalıştırılan jS kodundan;
- 73-75. satırlar: HTML belgesindeki üç alanın referansları alınır;
- 2-5. satırlar: jS dosyasında tanımlanan tüm işlevlerde bilinen küresel değişkenler;
- 9. satır: Kullanıcı tarafından girilen URL değeri alınır;
- 10. satır: Kullanıcının göndermek istediği değer alınır;
- satır 11: 9. satırdaki URL değerini istemek için kullanılacak [get] veya [post] yöntemini alınır:
- "document", tarayıcı tarafından yüklenen belgeyi ifade eder; buna DOM (Belge Nesne Modeli) denir,
- document.forms[0], belgedeki ilk formu ifade eder; bir belgede birden fazla form bulunabilir. Burada sadece bir tane vardır,
- document.forms[0].elements['method'], [name='method'] özniteliğine sahip form öğesini ifade eder. Bunlardan iki tane vardır:
<input type="radio" id="get" name="method" value="get" checked="checked" />GET
<input type="radio" id="post" name="method" value="post" />POST
- (devamı)
- document.forms[0].elements['method'].value, [name='method'] özniteliğine sahip bileşen için gönderilecek değerdir. Gönderilen değerin, işaretli radyo düğmesinin [value] özniteliğinin değeri olduğu bilinmektedir. Dolayısıyla burada, ['get', 'post'] dizelerinden biri olacaktır;
- 13-18. satırlar: Kullanılacak HTTP yöntemine göre, [doGet] veya [doPost] yöntemi yürütülür;
- jQuery [$.ajax] yöntemi, bir HTTP çağrısı gerçekleştirir;
- 23-25. satırlar: HTTP veya [Authorization: Basic code] başlığı gerektiren bir sunucuya bağlanılır. Sunucuyu sorgulayabilecek tek kullanıcı olan [admin / admin] için bu başlığı oluştururuz;
- 26. satır: Kullanıcı, [/getAllLongCategories, /saveCategories, ...] türünde URL değerlerini girecektir. Dolayısıyla bu URL değerlerini tamamlamak gerekir;
- 27. satır: HTTP yöntemi kullanılmalıdır;
- 28. satır: Sunucu, jSON'i geri gönderir. Alınan haliyle görüntülemek için sonuç türü olarak [text/plain] türü belirtilir;
- satır 33: sunucudan gelen metin yanıtının görüntülenmesi;
- satır 39: Olası hata mesajının metin biçiminde görüntülenmesi;
- satır 44: [doPost] yöntemi, gönderilecek değer olan ikinci bir parametre alır;
- satır 52: gönderilecek değerin jSON dizesi biçiminde olacağını belirtmek için;
21.2.6. İstemcinin çalıştırılması
İstemci uygulaması, aşağıdaki [Client] yürütülebilir sınıfı tarafından başlatılan bir konsol uygulamasıdır:
![]() |
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);
}
}
- 6. satır: [@EnableAutoConfiguration] anotasyonu, [Spring Boot] projesinin (4. satır) bir anotasyonudur. Spring Boot, projenin Classpath'inde bulunan arşivleri inceleyecektir. Burada, [pom.xml] dosyasının tek bağımlılığı tarafından getirilen tüm Maven bağımlılıkları olacaktır:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
Bu bağımlılık, özellikle Spring MVC ve bir Tomcat sunucusu olmak üzere çok sayıda arşivi beraberinde getirir. Bu bağımlılıkların varlığı nedeniyle Spring Boot, varsayılan değerlerle Tomcat üzerinde çalışan bir Spring MVC projesini yapılandıracaktır. Tomcat sunucusu, 8080 numaralı bağlantı noktasında çalışacak şekilde yapılandırılır. Spring Boot tarafından seçilen varsayılan değerlerden kurtulmak istersek, Classpath'in kök dizininde bulunan [application.properties] dosyasını kullanabiliriz ([src / main / resources] içindeki her şey Classpath'in kök dizinindedir):
![]() |
Tomcat sunucusunun 8082 numaralı bağlantı noktasında çalışmasını şu şekilde belirtiyoruz:
server.port=8082
Kullanılabilecek parametrelerin listesi, [application.properties]'ten URL'e (Haziran 2015) ve [http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html]'e kadar yer almaktadır;
[Client.java] koduna geri dönelim:
- 10. satır: [SpringApplication.run] yöntemi, [client.html] sayfasını projenin Classpath'inde bulunan Tomcat sunucusuna dağıtacaktır;
21.2.7. URL [/getAllShortCategories]
![]() |
Şunları başlatıyoruz:
- 8081 numaralı bağlantı noktasında güvenli web / json sunucusunu ([spring-security-server-jdbc-generic] yapılandırması);
- bu sunucunun istemcisini 8082 numaralı bağlantı noktasında ([spring-cors-client-generic] yapılandırması);
ardından URL, [http://localhost:8082/client.html] ve [1]'i çağırıyoruz:
![]() |
- [2]'te, URL ve [http://localhost:8081/getAllShortCategories] üzerinde GET işlemi yapıyoruz;
Sunucudan yanıt alamıyoruz. Chrome geliştirici konsoluna (Ctrl-Shift-I) baktığımızda bir hata görüyoruz:
![]() |
- [1]'te, [Network] sekmesindeyiz;
- [2] sekmesinde, yapılan HTTP isteğinin [GET] değil, [OPTIONS] olduğu görülüyor. Etki alanları arası bir istek durumunda, tarayıcı sunucuya HTTP [OPTIONS] isteği göndererek belirli koşulların karşılanıp karşılanmadığını kontrol eder. Bu durumda, istekler [5-6] işaretli olanlardır;
- [5]'te, tarayıcı, URL hedefine bir GET ile ulaşılamayacağını sorar. [Access-Control-Request-Method] isteği, istenen yöntemin kabul edildiğini belirten HTTP [Access-Control-Allow-Methods] başlığını içeren bir yanıt talep eder;
- [6]'te tarayıcı, HTTP [Origin: http://localhost:8081] başlığını gönderir. Bu başlık, belirtilen kaynağın kabul edildiğini belirten bir HTTP [Access-Control-Allow-Origin] başlığıyla yanıt verilmesini talep eder;
- [7]'te tarayıcı, HTTP, [accept] ve [authorization] başlıklarının kabul edilip edilmediğini sorar. [Access-Control-Request-Headers] isteği, istenen başlıkların kabul edildiğini belirten HTTP ve [Access-Control-Allow-Headers] başlıklarını içeren bir yanıt bekler;
- [3]'te bir hata oluşur. Simgeye tıklandığında [4] hatası görüntülenir;
- [4]'te, sunucunun isteğin kaynağının kabul edilip edilmediğini belirten HTTP [Access-Control-Allow-Origin] başlığını göndermediğini belirten bir mesaj görüntülenir;
- [8]'te, sunucunun bu başlığı gerçekten göndermediği görülebilir. Sonuç olarak tarayıcı, başlangıçta istenen HTTP GET isteğini reddetmiştir;
Web sunucusunu / jSON olarak değiştirmemiz gerekiyor.
21.2.8. Yeni bir web hizmeti / json
Yeni bir Maven projesi [spring-cors-server-jdbc-generic] oluşturuyoruz:
![]() |
Yeni web hizmetinin Maven yapılandırması şu şekildedir:
<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>
<!-- eklentiler -->
<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>
- 30-32. satırlar: Güvenli web / json sunucusunun arşivinden yararlanarak şimdiye kadar yapılan tüm çalışmaları devralıyoruz;
Sonuç olarak, bağımlılıklar şu şekildedir:
![]() |
[AppConfig] yapılandırma sınıfı şu şekildedir:
![]() |
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 {
// etki alanları arası istekler
@Bean
public boolean isCorsEnabled() {
return true;
}
...
}
- 12. satır: sınıf, bir Spring yapılandırma sınıfıdır;
- 9. satır: Diğer Spring bileşenleri [spring.cors.server.service] paketinde aranmalıdır;
- 14. satır: [spring-security-server-jdbc-generic] projesinden bean'ler içe aktarılır;
- 18-21. satırlar: Sunucu etki alanı dışındaki istemcileri kabul edip etmediğimizi belirten [isCorsEnabled] adlı bir Spring bileşeni oluşturuyoruz;
21.2.9. Denetleyiciler
Yeni web hizmetinde dört denetleyici bulunmaktadır:
![]() |
- [CorsCategorieController], kategori işleme URL'lerini yönetir. Yalnızca web istemcilerinin CORS başlıklarını yönetir. Aksi takdirde, işi [spring-webjson-server-jdbc-generic] bağımlılığındaki [CategorieController] denetleyicisine devreder;
- [CorsProduitController] ve [CorsAuthenticateController] de aynı şekilde, işi [spring-webjson-server-jdbc-generic] bağımlılığındaki [ProduitController] denetleyicisine ve [AuthenticateController] bağımlılığındaki[spring-security-server-jdbc-generic] bağımlılığındaki denetleyicilere devrederek aynı işlevi yerine getirir;
- [CorsController], önceki üç denetleyicide ortak olan unsurları faktörlere ayırmak için kullanılır;
21.2.9.1. [CorsController] denetleyicisi
[CorsController] sınıfı şu şekildedir:
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;
// seçeneklerin istemciye gönderilmesi
public void sendOptions(String origin, HttpServletResponse response) {
// CORS izinli mi?
if (!isCorsEnabled || origin == null || !origin.startsWith("http://localhost")) {
return;
}
// CORS başlığını ayarlıyoruz
response.addHeader("Access-Control-Allow-Origin", origin);
// belirli başlıklara izin veriliyor
response.addHeader("Access-Control-Allow-Headers", "accept, authorization");
// GET'e izin veriliyor
response.addHeader("Access-Control-Allow-Methods", "GET");
}
}
- 8. satır: [CorsController] sınıfı bir Spring denetleyicisidir;
- 11-12. satırlar: [isCorsEnabled] bean'inin enjeksiyonu; bu bean, CORS başlıklarının yönetilip yönetilmeyeceğini belirler;
- satır 15-26: [sendOptions] yöntemi, CORS başlıklarını gönderen istemcilere yanıt verme işini üstlenir;
- satır 17-19: Uygulama, etki alanları arası istekleri kabul edecek şekilde yapılandırılmışsa ve gönderen HTTP [Origin] başlığını göndermişse ve bu kaynak [http://localhost] ile başlıyorsa, o zaman etki alanları arası istek kabul edilir, aksi takdirde reddedilir;
- 21. satır: İstemci [http://localhost:port] etki alanında ise, HTTP başlığı gönderilir:
bu, sunucunun müşterinin kaynağını kabul ettiği anlamına gelir;
- 22-25. satırlar: HTTP ve [OPTIONS] isteklerinde iki özel HTTP başlığını belirttik:
HTTP ve [Access-Control-Request-X] başlıklarına karşılık olarak sunucu, neyin izin verildiğini belirten HTTP ve [Access-Control-Allow-X] başlıklarıyla yanıt verir. 22-25. satırlar, isteğin kabul edildiğini belirtmek için yalnızca istemcinin isteğini tekrarlar;
21.2.9.2. [CorsCategorieController] denetleyicisi
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) {
// kaynak yöntemi
return categorieController.getAllShortCategories();
}
...
}
- 19. satır: [@RestController] notu, sınıfı hem bir Spring bileşeni hem de yanıtlarını müşteriye kendisi gönderen bir MVC denetleyicisi haline getirir;
- 20. satır: [CorsCategorieController] sınıfı, az önce incelediğimiz [CorsController] sınıfını genişletir;
- 22-23. satırlar: [CategorieController categorieController] denetleyicisinin [spring-webjson-server-jdbc-generic] bağımlılığından enjeksiyonu;
- satır 25-29: URL, [/cors-getAllShortCategories] komutuyla çağrıldığında işlenir; Geleneksel olarak, güvenli web hizmetinin URL [/U]'ini çağırmak isteyen web istemcilerinin, aslında URL [/cors-U]'i çağırması gerektiğine karar verdik. Böylece, devreye alınan web hizmeti iki tür URL'e sahip olacaktır:
- [/U]: web dışı istemciler için;
- [/cors-U]: web istemcileri için;
- 25. satır: [/cors-getAllShortCategories] yöntemi şu parametreleri kabul eder:
- [@RequestHeader(value = "Origin", required = false)] nesnesi, isteğin HTTP [Origin] başlığını alır. Bu başlık, isteğin göndereni tarafından gönderilmiştir:
HTTP [Origin] başlığının isteğe bağlı olduğu belirtilmiştir [required = false]. Bu durumda, başlık yoksa, [String origin] parametresinin değeri null olacaktır. Varsayılan değer olan [required = true] ile başlık yoksa bir istisna atılır. Bu durumu önlemek istedik;
- (devam)
- isteği yapan müşteriye geri gönderilecek olan [HttpServletResponse response] nesnesi;
Bu iki parametre Spring tarafından eklenir;
- 28. satır: İsteğin işlenmesi, üst sınıf olan [CorsController]'in [sendOptions] yöntemine devredilir;
- 31-36. satırlar: [getAllShortCategories] yöntemi, bir GET ile çağrıldığında URL ve [/cors-getAllShortCategories]'i işler;
- satır 35: iş, [spring-webjson-server-jdbc-generic] bağımlılığının [CategorieController.getAllShortCategories] yöntemine devredilir;
Artık yeni testlere hazırız. Web hizmetinin yeni sürümünü başlatıyoruz ve sorunun hâlâ devam ettiğini görüyoruz. Hiçbir şey değişmemiş. Yukarıdaki 28. satıra bir konsol çıktısı eklesek bile, bu çıktı hiçbir zaman görüntülenmiyor; bu da 25. satırdaki [corsGetAllShortCategories] yönteminin hiçbir zaman çağrılmadığını gösteriyor.
Biraz araştırma yaptıktan sonra, Spring MVC'in HTTP ve [OPTIONS] komutlarını varsayılan bir işleme tabi tuttuğunu keşfediyoruz. Dolayısıyla yanıtı her zaman Spring veriyor ve 25. satırdaki [corsGetAllShortCategories] yöntemi hiçbir zaman çağrılmıyor. Spring MVC’in bu varsayılan davranışı değiştirilebilir. Mevcut [AppConfig] sınıfını şu şekilde değiştiriyoruz:
![]() |
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 {
// etki alanları arası istekler
@Bean
public boolean isCorsEnabled() {
return true;
}
@Autowired
private DispatcherServlet dispatcherServlet;
@PostConstruct
public void init(){
// uygulama istekleri kendisi işliyor HTTP [OPTIONS]
dispatcherServlet.setDispatchOptionsRequest(true);
}
}
- 23-24. satırlar: [spring-webjson-server-jdbc-generic] bağımlılığında tanımlanan [DispatcherServlet dispatcherServlet] bileşenini enjekte ediyoruz;
- 26-30. satırlar: [@PostConstruct] anotasyonu, [init] yönteminin, [AppConfig] sınıfının örneklendirilmesinden ve Spring tarafından yapılan enjeksiyonlardan sonra çalıştırılmasını sağlar;
- 29. satır: servlet'ten, HTTP ve [OPTIONS] komutlarını uygulamaya iletmesi istenir;
Bu yeni yapılandırma ile testleri tekrar yapıyoruz. Aşağıdaki sonucu elde ediyoruz:
![]() |
- [1]'te, HTTP'e yönelik iki istek olduğunu görüyoruz: URL ve [http://localhost:8080/getAllCategories];
- [2]'te, [OPTIONS] isteği;
- [3]'te ise, az önce sunucu yanıtında yapılandırdığımız üç HTTP başlığı;
Şimdi ikinci isteği inceleyelim:
![]() |
- [1]'e, incelenen istek;
- [2]'e dönüştü; bu, GET isteğidir. İlk istek olan [OPTIONS] sayesinde tarayıcı, istediği bilgileri aldı. Şimdi başlangıçta istenen [GET] isteğini gerçekleştiriyor;
- [3]'te sunucunun yanıtı;
- [4]'te, sunucu jSON'i gönderir;
- [5]'te bir hata meydana geldi;
- [6]'te, hata mesajı;
Burada ne olduğunu açıklamak daha zordur. Sunucunun [3] yanıtı, normal olan [HTTP/1.1 200 OK]'tir. Dolayısıyla istenen belgeye sahip olmamız gerekir. Sunucu belgeyi gerçekten göndermiş olabilir, ancak tarayıcı, GET isteği için de yanıtın HTTP ve [Access-Control-Allow-Origin:http://localhost:8081] başlıklarını içermesini istediği için belgenin kullanılmasını engelliyor olabilir.
GET'i işleyen yöntemi, URL ve [/cors-getAllShortCategories]'e göre değiştiriyoruz:
@RequestMapping(value = "/cors-getAllShortCategories", method = RequestMethod.GET)
public Response<List<Categorie>> getAllShortCategories(
@RequestHeader(value = "Origin", required = false) String origin, HttpServletResponse response) {
// başlıklar CORS
sendOptions(origin, response);
// kaynak yöntemi
return categorieController.getAllShortCategories();
}
- 5. satır: HTTP ve [OPTIONS] isteklerinde olduğu gibi, sunucu, HTTP [GET] isteği için HTTP CORS başlıklarını gönderecektir;
Bu değişiklikten sonra sonuçlar şu şekildedir:
![]() |
Tüm kategorilerin kısa versiyonlarını başarıyla elde ettik.
21.2.9.3. URL ve [GET]
[CorsCategorieController, CorsProduitController, CorsAuthenticateController] denetleyicilerinde, URL ile talep edilen [GET]'i işleyen eylem kodları, daha önce URL ve [/cors-getAllShortArticles]'i işleyen eylemlerin modelini takip etmektedir. Okuyucu, bu belgeyle birlikte sunulan örneklerde kodu inceleyebilir. Aşağıda, [CorsProduitController] denetleyicisine ait URL ve [/cors-getAllLongProduits] için bir örnek verilmiştir:
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) {
// başlıklar CORS
sendOptions(origin, response);
// kaynak yöntemi
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]
Şu durumu inceleyelim:
![]() |
- POST [1]'ten URL [2]'e;
- [3]'te, gönderilen değer. Bu, ürün içermeyen bir kategorinin jSON dizesidir;
- sonuç olarak, [categorie[2]] adlı bir kategori oluşturmaya çalışıyoruz;
Şu an için herhangi bir kod değişikliği yapmıyoruz. Elde edilen sonuç şöyledir:
![]() |
- [1]'te, [GET] isteklerinde olduğu gibi, tarayıcı tarafından bir [OPTIONS] isteği yapılır;
- [2] durumunda, [POST] isteği için erişim izni talep eder. Daha önce bu, [GET] idi;
- [3]'te, HTTP ve [accept, authorization, content-type] başlıklarını göndermek için izin talep eder. Daha önce sadece ilk iki başlık vardı;
- [4]'te, web hizmeti istenen tüm izinleri vermediğinden [5] hatası ortaya çıkıyor;
[CorsController.sendOptions] yöntemini şu şekilde değiştiriyoruz:
public void sendOptions(String origin, HttpServletResponse response) {
// Cors izinli mi?
if (!isCorsEnabled || origin == null || !origin.startsWith("http://localhost")) {
return;
}
// başlık ayarlanıyor CORS
response.addHeader("Access-Control-Allow-Origin", origin);
// bazı başlıklara izin verilir
response.addHeader("Access-Control-Allow-Headers", "accept, authorization, content-type");
// GET ve POST'e izin veriliyor
response.addHeader("Access-Control-Allow-Methods", "GET, POST");
}
}
- 9. satır: HTTP [Content-Type] başlığı eklendi (büyük/küçük harf fark etmez);
- 11. satır: HTTP [POST] yöntemi eklendi;
Böylece [POST] yöntemleri, [GET] istekleriyle aynı şekilde işlenir. Aşağıda, [CorsCategorieController] denetleyicisinde URL ve [/cors-saveCategories] yöntemlerine ilişkin bir örnek verilmiştir:
@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) {
// CORS başlıkları
sendOptions(origin, response);
// kaynak yöntemi
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);
}
Bu değişiklikler yapıldıktan sonra elde edilen sonuç şöyledir:
![]() |
[categorie[2]] kategorisi veritabanına başarıyla eklenmiştir. SGBD, bu kategoriye 226 birincil anahtar atamıştır. Bunu GET [/cors-getAllShortCategories] yöntemiyle doğrulayabiliriz:
![]() |
21.2.10. Sonuç
Uygulamamız artık etki alanları arası istekleri desteklemektedir. Bu istekler, [AppConfig] sınıfındaki yapılandırma ile izin verilebilir veya reddedilebilir:
package spring.cors.server.config;
...
@Configuration
@ComponentScan(basePackages = { "spring.cors.server.service" })
@Import({ spring.security.config.AppConfig.class })
public class AppConfig {
// etki alanları arası istekler
@Bean
public boolean isCorsEnabled() {
return true;
}
...
}
21.3. Eclipse projesi [spring-cors-server-jpa-generic]
CORS web hizmeti, artık [spring-cors-server-jpa-generic] projesi tarafından uygulanacaktır. Bu proje, Spring Data JPA ile veritabanına erişimi yöneten [spring-security-server-jpa-generic] projesine dayanmaktadır:
![]() |
[spring-cors-server-jpa-generic] projesi, daha önce incelenen [spring-cors-server-jdbc-generic] projesinin kopyalanmasıyla elde edilmiştir.
![]() |
Ardından yapılması gereken iki değişiklik vardır. İlki, [pom.xml] dosyasında yer almaktadır:
<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>
<!-- eklentiler -->
<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>
- 30-32. satırlar: [spring-security-server-jpa-generic] adlı güvenli web hizmetine olan bağımlılık;
Sonuç olarak, projenin bağımlılıkları şu şekildedir:
![]() |
Not: Alt-F5 tuşlarına basın ve ardından tüm projeleri yeniden oluşturun
İkinci değişiklik, [Alt-Maj-O] hatası veren sınıflardaki içe aktarmaları güncellemek.
Hepsi bu kadar. CORS web hizmetini, [spring-cors-server-jpa-generic-hibernate-eclipselink] çalıştırma yapılandırmasıyla başlatıyoruz:
![]() | ![]() |
Ardından genel istemciyi başlatıyoruz:
![]() |
ve bir tarayıcıyla URL, [1] ve GET'i talep ediyoruz. [2]'te, döndürülen kategorilerin kısa sürümünde, önceki sürümde (JDBC) bulunmayan [entityType] alanının yer aldığı görülüyor.
Şimdi iki başka CORS mimarisini ele alacağız:
- CORS / JPA ve EclipseLink / DB2 mimarileri;
- CORS / JPA OpenJpa / Firebird mimarisi;
21.4. Mimari CORS / JPA EclipseLink / DB2
Aşağıdaki mimariyi uygulayacağız:
![]() |
Aşağıdaki projeleri yükleyeceğiz:
![]() |
Not: Alt-F5 tuşlarına basın ve tüm Maven projelerini yeniden oluşturun.
SGBD ve DB2'i çalıştırın ve [dbproduitscategories] veritabanının mevcut olup olmadığını kontrol edin. Değilse, veritabanını oluşturun (12.1.2. paragraf).
Kullanıcılar, [spring-security-create-users-hibernate-eclipselink] yürütme yapılandırmasıyla [dbproduitscategories] veritabanında oluşturulur:
![]() | ![]() |

Ardından, [spring-cors-server-jpa-generic-hibernate-eclipselink] adlı yürütme yapılandırması ve [spring-cors-client-generic] adlı istemcisi ile CORS web hizmetini başlatın:
![]() | ![]() |
[spring-jdbc-generic-04-fillDataBase] adlı yürütme yapılandırmasını kullanarak [dbproduitscategories] tabanını değerlerle doldurun:
![]() |
Son olarak, bir tarayıcıda aşağıdaki URL adresini girin:
![]() |
21.5. CORS / JPA OpenJPA / Firebird mimarisi
Şimdi şu mimariyi uygulayacağız:
![]() |
Aşağıdaki projeleri yüklüyoruz:
![]() |
Not: Alt-F5 tuşlarına basın ve tüm Maven projelerini yeniden oluşturun.
SGBD Firebird'ü başlatın ve [dbproduitscategories] veritabanının mevcut olup olmadığını kontrol edin. Değilse, veritabanını oluşturun (paragraf 14.1.2).
[dbproduitscategories] veritabanında, [spring-security-create-users-openjpa] yürütme yapılandırmasıyla kullanıcılar oluşturulur:
![]() | ![]() |

Ardından, [spring-cors-server-jpa-generic-openjpa] adlı yürütme yapılandırmasıyla CORS web hizmetini başlatın:
![]() | ![]() |
CORS istemcisini, [spring-cors-client-generic] yapılandırmasıyla başlatın:
![]() |
[spring-jdbc-generic-04-fillDataBase] yürütme yapılandırmasını kullanarak [dbproduitscategories] veritabanını değerlerle doldurun:
![]() |
Son olarak, bir tarayıcıda aşağıdaki URL adresini girin:
![]() |


























































