18. [Cours]: Etki alanları arası erişim yönetimi
Anahtar kelimeler: CORS (Cross-Origin Resource Sharing).
Bu bölüm, TD'ten biraz farklıdır. Web programlama ve JavaScript programlamaya giriş yaptığı için bu bölümde yer almıştır. Burada, bu TD'in amaçlarından birinin, JEE geliştirme sürecinde sıkça kullanılan kavramları, yani Java çerçevelerine dayalı web geliştirmeyi tanıtmak olduğunu hatırlamak gerekir. Burada, ürün ve kategori veritabanı çalışmasında kullanılan web sunucusunu, etki alanları arası istekleri kabul edebilmesi için tamamlıyoruz.
[Tutoriel AngularJS / Spring 4] belgesinde, istemcinin bir AngularJS uygulaması olduğu bir istemci/sunucu uygulaması geliştirilmektedir:
![]() |
- Angular uygulamasının HTML / CSS / JS sayfaları, [1] sunucusundan gelmektedir;
- [2]'te, [dao] hizmeti başka bir sunucuya, yani [2] sunucusuna bir istek gönderir. Ancak bu durum, Angular uygulamasını çalıştıran tarayıcı tarafından yasaklanmıştı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 engellediğini söylemek doğru değildir. Uygulama, aslında bu sunucuya, kendi ağından gelmeyen bir istemcinin kendisine sorgu göndermesine izin verip vermediğini sormak için sorgu gönderir. Bu paylaşım tekniğine CORS (Cross-Origin Resource Sharing) denir. [2] sunucusu, belirli HTTP başlıklarını göndererek onay verir.
Şu mimariyi oluşturacağı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;
18.1. Support
![]() |
Bu bölümdeki projeler [support / chap-18] klasöründe bulunur.
18.2. Müşteri projesi
Aşağıdaki Eclipse projesini oluşturuyoruz:
![]() |
18.3. Maven Yapılandırması
Proje, aşağıdaki [pom.xml] dosyasını içeren bir Maven projesidir:
<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>istia.st.webjson</groupId>
<artifactId>intro-server-webjson-01</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>intro-server-webjson-01</name>
<description>démo spring mvc</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.7.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>istia.st.springdata</groupId>
<artifactId>intro-spring-data-01</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.18.1</version>
</plugin>
</plugins>
</build>
</project>
- 11-15. satırlar: Bu bir Spring Boot projesidir;
- 23-26. 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;
18.4. Spring Yapılandırması
![]() |
Spring projesini yapılandıran [WebConfig] sınıfı şöyledir:
package spring.cors.client.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.ServletRegistrationBean;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
// -------------------------------- [web] katmanı yapılandırması
@Autowired
private ApplicationContext context;
@Bean
public DispatcherServlet dispatcherServlet() {
DispatcherServlet servlet = new DispatcherServlet((WebApplicationContext) context);
return servlet;
}
@Bean
public ServletRegistrationBean servletRegistrationBean(DispatcherServlet dispatcherServlet) {
return new ServletRegistrationBean(dispatcherServlet, "/*");
}
@Bean
public EmbeddedServletContainerFactory embeddedServletContainerFactory() {
return new TomcatEmbeddedServletContainerFactory("", 8081);
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/*.html").addResourceLocations("classpath:/static/");
registry.addResourceHandler("/*.js").addResourceLocations("classpath:/static/js/");
}
}
- 15. satır: Sınıf, bir Spring projesini yapılandırır MVC;
- 16. satır: Sınıf, [WebMvcConfigurerAdapter] sınıfını genişleterek bazı yöntemlerini yeniden tanımlar;
- satır 18-36: Bu bean'lerle daha önce, örneğin 13.5.3.1. paragrafında karşılaşmıştık. Satır 35'te, web hizmetinin 8081 numaralı bağlantı noktasında çalışacağına dikkat edilmelidir;
- satır 38-42: [addResourceHandlers] yöntemi, statik kaynakları tanımlamaya olanak tanır; yani 23. satırdaki [DispatcherServlet] tarafından işlenmeyen kaynakları;
- 40. satır: .html uzantılı bir kaynağa yönelik her istek, istekle talep edilen ve projenin Classpath'indeki [static] klasöründe bulunan dosya ile yanıtlanacaktır;
- 41. satır: .js uzantılı bir kaynağa yönelik her istek, istekle talep edilen ve projenin Classpath'indeki [static/js] klasöründe bulunan JavaScript dosyasını yanıt olarak alacaktır;
![]() |
18.5. jQuery ve Javascript'in Temelleri
İstemcinin HTML sayfası şu şekilde olacaktır:
![]() |
Bu sayfa, tarayıcıda çalıştırılan Javascript kodunu (jS) içerecektir. 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="/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 dosyasının en son sürümünü URL ve [http://jquery.com/download/] dosyalarında bulabilirsiniz:

İndirdiğimiz dosyayı [static / js] klasörüne yerleştireceğiz ve HTML dosyasının 6. satırını, kurulu sürüme göre değiştireceğiz.
Bu işlem tamamlandıktan sonra, Chrome [1-2] ile [jQuery.html] statik görünümünü çağırın:
![]() |
Google Chrome'da, [Ctrl-Maj-I] komutunu kullanarak [3] geliştirme araçlarını görüntüleyin. [Console] [4] sekmesi, JavaScript kodunu çalıştırmanıza olanak tanır. Aşağıda, girmeniz gereken JavaScript komutlarını ve bunların açıklamalarını veriyoruz.
|
: [element1] kimliğine sahip tüm öğelerin koleksiyonunu oluşturur, yani normalde 0 veya 1 öğeden oluşan bir koleksiyon çünkü bir sayfada iki özdeş kimlik bulunamaz HTML. | ![]() |
|
: [blabla] metnini koleksiyondaki tüm öğelere uygular. Bu işlem, sayfanın görüntülediği içeriği değiştirir | ![]() |
|
koleksiyondaki öğeleri gizler. [blabla] metni artık görüntülenmiyor. | ![]() |
|
: koleksiyonu yeniden görüntüler. Bu sayede [element1] kimliğine sahip öğenin CSS style='display: none;' özniteliği, 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] ve değeri [color: red]'tir. [blabla] metni kırmızıya döner. | ![]() |
![]() | |
![]() |
Tarayıcının URL değerinin tüm bu işlemler sırasında 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 bakalım:
![]() | ![]() |
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.
18.6. Uygulamanın JavaScript kodu
/ jSON web hizmetini sorgulayacak olan istemci uygulamasının sayfasına geri dönelim:
![]() |
![]() |
Bu sayfanın HTML kodu şöyledir:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Spring MVC</title>
<script type="text/javascript" src="/jquery-2.1.3.min.js"></script>
<script type="text/javascript" src="/client.js"></script>
</head>
<body>
<h2>Client du service web / jSON</h2>
<form id="formulaire">
<!-- kimlik -->
Identifiant :
<!-- -->
<input type="text" id="identifiant" name="identifiant" value="" />
<!-- şifre -->
<br /> <br /> Mot de passe :
<!-- -->
<input type="text" id="password" name="password" value="" />
<!-- yöntem HTTP -->
<br /> <br /> 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 (commençant par /): <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="button" value="Valider"
onclick="javascript:requestServer()"></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 kod içe aktarılır;
- 15., 19., 26., 29. ve 31. 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 şu şekildedir:
// genel veriler
var url;
var posted;
var response;
var method;
var baseUrl = 'http://localhost:8080';
var identifiant;
var password;
var authorizationHeader;
function requestServer() {
// bilgileri alıyoruz
var urlValue = url.val();
var postedValue = posted.val();
var identifiantValue = identifiant.val();
var passwordValue = password.val();
var method = document.forms[0].elements['method'].value;
authorizationCode = btoa(identifiantValue + ':' + passwordValue);
// önceki yanıtı siliyoruz
response.text("");
// manuel olarak Ajax çağrısı yapılıyor
if (method === "get") {
doGet(urlValue);
} else {
doPost(urlValue, postedValue);
}
}
function doGet(url) {
// manuel olarak bir Ajax çağrısı yapılıyor
$.ajax({
headers : {
'Authorization':'Basic '+authorizationCode
},
url : baseUrl + url,
type : 'GET',
dataType : 'text',
beforeSend : function() {
},
success : function(data) {
// metin sonucu
response.text(data);
},
complete : function() {
},
error : function(jqXHR) {
// sistem hatası
response.text(JSON.stringify(jqXHR.statusCode()));
}
})
}
function doPost(url, posted) {
// Ajax çağrısı manuel olarak yapılıyor
$.ajax({
headers : {
'Yetkilendirme':'Basic '+authorizationCode
},
url : baseUrl + url,
type : 'POST',
contentType : 'application/json; charset=UTF-8',
data : posted,
dataType : 'text',
beforeSend : function() {
},
success : function(data) {
// metin sonucu
response.text(data);
},
complete : function() {
},
error : function(jqXHR) {
// sistem hatası
response.text(JSON.stringify(jqXHR.statusCode()));
}
})
}
// belge yüklenirken
$(document).ready(function() {
// sayfadaki bileşenlerin referanslarını alıyoruz
identifiant = $("#identifiant");
password = $("#password");
url = $("#url");
posted = $("#posted");
response = $("#response");
});
- 80-87. satırlar: Belgenin tarayıcıya yüklenmesi tamamlandığında çalıştırılan jS kodu;
- 81-86. satırlar: HTML belgesindeki çeşitli öğelerin referansları, [id] tanımlayıcıları aracılığıyla alınır;
- satır 2-9: jS dosyasında tanımlanan tüm işlevlerde bilinen küresel değişkenler;
- 13. satır: Kullanıcı tarafından girilen URL değeri alınır;
- 14. satır: gönderilmek istenen değer alınır (GET işlemi ise boş bırakılır);
- 15. satır: Kullanıcı tarafından girilen kullanıcı adı alınır;
- 16. satır: Şifresini alırız;
- satır 17: 9. satırdaki URL'i istemek için kullanılacak [get] veya [post] yöntemini alıyoruz:
- [document], tarayıcı tarafından yüklenen belgeyi ifade eder; buna DOM (Belge Nesne Modeli) denir,
- [document.forms[0].elements['method']] ise belgedeki ilk formu ifade eder; bir belgede birden fazla form bulunabilir. Burada ise 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;
- 18. satır: "identifiant:password" dizesinin Base74 kodlamasını oluşturuyoruz. Bu kodlanmış dize, isteği doğrulamak için sunucuya göndereceğimiz HTTP [Authorization] başlığında kullanılacak;
- 22-26. satırlar: Kullanılacak HTTP yöntemine göre, [doGet] veya [doPost] yöntemini çalıştırıyoruz;
- jQuery veya [$.ajax] yöntemi, HTTP çağrısını gerçekleştirir;
- 32-34. satırlar: HTTP veya [Authorization: Basic code] başlığı gerektiren bir sunucuya bağlantı kurulur;
- satır 35: kullanıcı, [/cors-getAllCategories,/cors-addProduits, ...] türünde URL değerlerini girecektir. Dolayısıyla bu URL değerlerini, 6. satırdaki sunucunun URL değeriyle tamamlamak gerekir;
- 36. satır: HTTP yöntemi kullanılmalıdır;
- satır 37: sunucu jSON'i geri gönderir. Alındığı haliyle görüntülemek için sonuç türü olarak [text] türü belirtilir;
- 42. satır: sunucudan gelen metin yanıtının görüntülenmesi;
- 48-49. satırlar: Olası hata mesajının görüntülenmesi;
- satır 53: [doPost] yöntemi, gönderilecek değer olan ikinci bir parametre alır;
- satır 61: gönderilecek değerin jSON biçiminde bir dize olarak gönderileceğini belirtmek için;
18.7. İstemcinin çalıştırılması
İstemci uygulaması, aşağıdaki [Boot] yürütülebilir sınıfı tarafından başlatılan bir Spring Boot uygulamasıdır:
![]() |
package spring.cors.client.boot;
import org.springframework.boot.SpringApplication;
import spring.cors.client.config.WebConfig;
public class Boot {
public static void main(String[] args) {
SpringApplication.run(WebConfig.class, args);
}
}
- 10. satır: [SpringApplication.run] yöntemi, [WebConfig] yapılandırma dosyasını kullanır. [client.html] sayfası, projenin Classpath'inde bulunan Tomcat sunucusuna dağıtılacaktır;
18.8. URL [/getAllCategories]
Şunu başlatıyoruz:
- 8080 numaralı bağlantı noktasında web / json sunucusunu;
- bu sunucunun istemcisini 8081 numaralı bağlantı noktasında çalıştırıyoruz;
ardından URL, [http://localhost:8081/client.html] ve [1]'i çağırıyoruz:
![]() |
- [2]'te, URL ve [http://localhost:8080/getAllCategories] üzerinde bir GET işlemi gerçekleştiriyoruz;
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ğini göndererek belirli koşulların karşılanıp karşılanmadığını kontrol eder. Bu durumda, istekler [5-6] işaretleriyle belirtilenlerdir;
- [5]'te ise tarayıcı, URL hedefine bir GET ile ulaşılamayacağını sorar. [Access-Control-Request-Method] isteğinin başlığı, 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] başlığında ise tarayıcı, HTTP, [accept] ve [authorization] başlıklarının kabul edilip edilmediğini sorar. [Access-Control-Request-Headers] istek başlığı, 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 ve [Access-Control-Allow-Origin] başlıkları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.
18.9. Yeni web hizmeti / json
Yeni bir Maven projesi [intro-spring-cors-server-jpa] oluşturuyoruz:
![]() | ![]() |
18.9.1. Maven yapılandırması
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>istia.st.cors</groupId>
<artifactId>spring-cors-server-jpa</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-cors-server-jpa</name>
<description>démo spring cors</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.7.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>istia.st.spring.security</groupId>
<artifactId>intro-spring-security-server-01</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
<!-- eklentiler -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.18.1</version>
</plugin>
</plugins>
</build>
</project>
- 23-27. satırlar: Güvenli web sunucusu / json arşivinden yararlanarak şimdiye kadar yapılan tüm çalışmaları devralıyoruz;
18.9.2. Spring Yapılandırması
[AppConfig] yapılandırma sınıfı şu şekildedir:
![]() |
package spring.cors.server.config;
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 spring.security.config.SecurityConfig;
@Configuration
@ComponentScan(basePackages = { "spring.cors.server.service" })
@Import({ SecurityConfig.class })
public class AppConfig {
// etki alanları arası istekler
@Bean
public boolean isCorsEnabled() {
return true;
}
}
- 10. satır: Sınıf, bir Spring yapılandırma sınıfıdır;
- 11. satır: Diğer Spring bileşenleri [spring.cors.server.service] paketinde aranmalıdır;
- 16-19. satırlar: Sunucu etki alanı dışındaki istemcileri kabul edip etmediğimizi belirten [isCorsEnabled] adlı bir Spring bileşeni oluşturuyoruz;
18.9.3. [AbstractCorsController] sınıfı
Bu uygulamanın tüm denetleyicilerinin üst sınıfı olacak [AbstractCorsController] sınıfı:
![]() |
Kodu şöyledir:
package spring.cors.server.service;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
public abstract class AbstractCorsController {
@Autowired
private boolean isCorsEnabled;
// seçeneklerin istemciye gönderilmesi
public void setHeaders(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");
}
}
- 7. satır: [CorsController] sınıfı, genişletilmek üzere tasarlanmış olup örneklenmeyeceği için soyut bir sınıftır;
- 13-24. satırlar: [setHeaders] yöntemi, müşteriye gönderilen [HttpServletResponse response] yanıtına (13. satır), etki alanları arası istekler tarafından talep edilen HTTP başlıklarını ekler;
- satır 33: [/setHeaders] yöntemi şu parametreleri kabul eder:
- etki alanları arası isteklerin HTTP ve [Origin] başlıklarında bulunan [origin] dizesi:
Burada, 13. satırdaki [origin] parametresinin değeri [http://localhost:8081] olur. Talep, HTTP [Origin] başlığını içermiyorsa, [origin==null] değerini elde etmek için gerekli düzenlemeler yapılacaktır;
- (devam)
- isteği yapan müşteriye geri gönderilecek olan [HttpServletResponse response] nesnesi;
Bu iki parametre Spring tarafından eklenir;
- 15-175. satırlar: 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;
- 19. 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;
- 21. satır: 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. 20-23. satırlar, isteğin kabul edildiğini belirtmek için yalnızca istemcinin isteğini tekrarlar;
18.9.4. [MyControllerWithHttpOptions] denetleyicisi
13.5.3. paragrafta incelenen güvenli olmayan web sunucusu / jSON [intro-server-webjson-01]'i değiştirmek zorunda kalmamak için, güvenli olmayan sunucunun URL ve [/url] isteklerini işlediği yerde, yeni denetleyici ise URL ve [/cors-url]'i işleyecek ve bu URL, etki alanları arası istekleri kabul edecektir.
[MyControllerWithHttpOptions] sınıfı, [OPTIONS] türündeki HTTP isteklerini işleyecek denetleyicidir:
![]() |
package spring.cors.server.service;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.fasterxml.jackson.core.JsonProcessingException;
@Controller
public class MyControllerWithHttpOptions extends AbstractCorsController {
@RequestMapping(value = "/cors-getAllCategories", method = RequestMethod.OPTIONS)
public void getAllCategories(@RequestHeader(value = "Origin", required = false) String origin,
HttpServletResponse httpServletResponse){
// başlıklar CORS
setHeaders(origin, httpServletResponse);
}
...
- 14. satır: Sınıf, bir Spring denetleyicisi olan MVC'tir;
- 15. satır: [MyControllerWithHttpOptions] sınıfı, az önce tanımladığımız [AbstractCorsController] sınıfını genişletir;
- satır 17-18: [getAllCategories] yöntemi (satır 18), URL ["/cors-getAllCategories"] yöntemi, HTTP [OPTIONS] yöntemi ile çağrıldığında bu sınıfı işler;
- 18. satır: [getAllCategories] yöntemi iki parametre kabul eder:
- [@RequestHeader(value = "Origin", required = false) String origin], başlık değerini almak için kullanılır; HTTP [Origin:http://localhost:8081] ise mevcut olduğunda kullanılır. Bu örnekte, [String origin] parametresine [http://localhost:8081] değeri atanacaktır. Bu başlık zorunlu değildir ([required = false]). Bu başlık mevcut değilse, [String origin] parametresinin değeri null olacaktır;
- [HttpServletResponse httpServletResponse]: İstemciye gönderilecek yanıt;
- 21. satır: Etki alanları arası istekleri mümkün kılan HTTP başlıkları gönderilir. [setHeaders] yöntemi, üst sınıf olan [AbstractCorsController]'te tanımlanmıştır;
Bu işlem, web sunucusu tarafından sunulan tüm URL'ler için yapılır / 13.5.3. paragrafında incelenen güvenli olmayan jSON ve [intro-server-webjson-01]. Bu hizmet URL ve [/url]'i sunduğunda, yukarıdaki [MyControllerWithHttpOptions] sınıfı URL ve [/cors-url]'i sunar.
18.9.5. [MyControllerWithCors] denetleyicisi
![]() |
[MyControllerWithCors] sınıfı, HTTP türündeki [GET] ve [POST] isteklerini işleyecek denetleyicidir:
package spring.cors.server.service;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
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.ResponseBody;
import com.fasterxml.jackson.core.JsonProcessingException;
import spring.webjson.service.MyController;
@Controller
public class MyControllerWithCors extends AbstractCorsController {
// Spring bağımlılıkları
@Autowired
private MyController myController;
...
@RequestMapping(value = "/cors-getAllCategories", method = RequestMethod.GET, produces = "application/json; charset=UTF-8")
@ResponseBody
public String getAllCategories(@RequestHeader(value = "Origin", required = false) String origin,
HttpServletResponse httpServletResponse) throws JsonProcessingException {
// yanıt
return myController.getAllCategories();
}
...
- 17. satır: [MyControllerWithCors] sınıfı bir Spring denetleyicisidir MVC
- 18. satır: [AbstractCorsController] sınıfını genişletir;
- 21-22. satırlar: 13.5.3. paragrafında incelenen güvenli olmayan web sunucusu / jSON'ten [MyController] denetleyicisinin enjeksiyonu;
- satır 25-27: [getAllCategories] yöntemi, URL ve [/cors-getAllCategories] (satır 28) yöntemini işler;
- 26. satır: [getAllCategories] yönteminin sonucu müşteriye gönderilecektir. Bu sonuç bir jSON akışıdır (27. satırdaki [produces] özniteliği ve 25. satırdaki sonucun [String] türü);
- 27. satır: Yöntem, az önce incelediğimiz [MyControllerWithHttpOptions] denetleyicisindeki [getAllCategories] yöntemiyle aynı parametreleri alır;
- satır 30: [myController.getAllCategories()] yönteminden yanıtı göndermesi istenir;
Sonuç olarak, yanıtı gönderen, güvenli olmayan sunucudaki [myController.getAllCategories()] yöntemidir. Bu yöntemin yanıtına, etki alanları arası istekler için gerekli başlıklar eklenmiştir.
Bu işlem, 13.5.3. paragrafında incelediğimiz güvenli olmayan web sunucusu / jSON tarafından sunulan tüm URL için de aynı şekilde yapılır. Bu hizmet URL ve [/url]'i sunduğunda, yukarıdaki [MyControllerWithCors] sınıfı URL ve [/cors-url]'i sunar.
Etki alanları arası bir istek şu şekilde gerçekleşir:
- Müşterinin JS kodu,URL ve [/cors-url] kodlarını, bir HTTP, GET veya POST isteği ile talep eder;
- bu kodu çalıştıran tarayıcı bu isteği yakalar ve önceURL [/cors-url] adresini, HTTP OPTIONS isteği ile birlikte gönderir ve hedef web hizmetinin etki alanları arası istekleri kabul edip etmediğini doğrular;
- [MyControllerWithHttpOptions] denetleyicisinin yöntemlerinden biri, tarayıcının beklediği etki alanları arası başlıkları gönderir;
- tarayıcı daha sonra URL'i, [/cors-url]'i, HTTP, GET veya POST istekleriyle birlikte ister;
- ardından denetleyicinin yöntemlerinden biri [MyControllerWithCors] yanıt verir;
18.9.6. Testler
[intro-spring-cors-server-jpa] projesinin önyükleme sınıfı şöyledir:
![]() |
package spring.cors.server.boot;
import org.springframework.boot.SpringApplication;
import spring.cors.server.config.AppConfig;
public class Boot {
public static void main(String[] args) {
SpringApplication.run(AppConfig.class, args);
}
}
- 10. satır: [SpringApplication.run] statik yöntemi, [AppConfig] Spring yapılandırmasıyla yürütülür. Bu yapılandırma sayesinde, proje arşivlerinde bulunan gömülü Tomcat sunucusu çalıştırılır ve [intro-spring-cors-server-jpa] web uygulaması bu sunucuya dağıtılır. Proje arşivlerinin bir parçası olan güvenli olmayan sunucunun web uygulaması [intro-server-webjson-01] de bu sunucuya dağıtılır. [intro-spring-security-server-01] projesi de arşivlerin bir parçası olduğundan, sonuç olarak iki tür URL erişime açılır:
- güvenli web hizmetine ait olanlar: /url;
- etki alanları arası istekleri kabul eden web hizmetine ait olanlar: /cors-url;
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ş. Aşağıdaki 7. satıra bir konsol çıktısı eklesek bile, bu çıktı hiçbir zaman görüntülenmiyor; bu da [MyControllerWithHttpOptions] sınıfındaki [getAllCategories] yönteminin hiçbir zaman çağrılmadığını gösteriyor;
@Controller
public class MyControllerWithHttpOptions extends AbstractCorsController {
@RequestMapping(value = "/cors-getAllCategories", method = RequestMethod.OPTIONS)
public void getAllCategories(@RequestHeader(value = "Origin", required = false) String origin,
HttpServletResponse httpServletResponse){
System.out.println(un_texte) ;
// başlıklar CORS
setHeaders(origin, httpServletResponse);
}
Biraz araştırma yaptıktan sonra, Spring’in varsayılan olarak MVC komutlarını HTTP ve [OPTIONS] komutlarını kendisi işlediğini görüyoruz. Dolayısıyla yanıtı her zaman Spring verir; yukarıdaki 5. satırdaki [getAllCategories] yöntemi ise hiçbir zaman yanıt vermez. 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;
import spring.security.config.SecurityConfig;
@Configuration
@ComponentScan(basePackages = { "spring.cors.server.service" })
@Import({ SecurityConfig.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şler HTTP [OPTIONS]
dispatcherServlet.setDispatchOptionsRequest(true);
}
}
- 25-26. satırlar: istemcilerin isteklerini yöneten [dispatcherServlet] bean'inin enjeksiyonu. Bu bean, 13.5.3. paragrafında incelenen güvenli olmayan web sunucusu / jSON yapılandırmasında tanımlanmıştır;
- 28-29. satırlar: [init] yöntemi (29. satır), [AppConfig] sınıfı örneklendirildiğinde ve Spring enjeksiyonları gerçekleştirildiğinde çalıştırılacaktır. Dolayısıyla bu yöntem çalıştırıldığında, 26. satırdaki alan önceden başlatılmış olacaktır;
- satır 31: [dispatcherServlet] bean'i, HTTP ve [OPTIONS] komutlarının işlenmesini web uygulamasının kendisi üstlenebilmesi için yapılandırılır;
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/cors-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, bu da 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.
Bu durumda, [MyControllerWithCors] denetleyicisini, etki alanları arası istekler için gerekli başlıkları da gönderecek şekilde değiştiriyoruz:
@RequestMapping(value = "/cors-getAllCategories", method = RequestMethod.GET, produces = "application/json; charset=UTF-8")
@ResponseBody
public String getAllCategories(@RequestHeader(value = "Origin", required = false) String origin,
HttpServletResponse httpServletResponse) throws JsonProcessingException {
// başlıklar CORS
setHeaders(origin, httpServletResponse);
// yanıt
return myController.getAllCategories();
}
- 6. satır: etki alanları arası istekler için gerekli başlıklar yanıtta yer almaktadır;
Bu değişiklikten sonra sonuçlar şu şekildedir:
![]() |
Kategori listesini başarıyla aldık.
18.10. Diğerleri: URL [GET]
[MyControllerWithCors, MyControllerWithHttpOptions] denetleyicilerinde, URL ile talep edilen [GET]'i işleyen eylem kodları, daha önce URL ve [/cors-getAllCategories]'i işleyen eylemlerin modelini takip etmektedir. Okuyucu, bu belgeyle birlikte sunulan örneklerde kodu inceleyebilir. Aşağıda, URL ve [/cors-getAllProduits] için bir örnek verilmiştir:
[MyControllerWithHttpOptions] içinde
@RequestMapping(value = "/cors-getAllProduits", method = RequestMethod.OPTIONS)
public void getAllProduits(@RequestHeader(value = "Origin", required = false) String origin,
HttpServletResponse httpServletResponse) {
// başlıklar CORS
setHeaders(origin, httpServletResponse);
}
[MyControllerWithCors] içinde
@RequestMapping(value = "/cors-getAllProduits", method = RequestMethod.GET, produces = "application/json; charset=UTF-8")
@ResponseBody
public String getAllProduits(@RequestHeader(value = "Origin", required = false) String origin,
HttpServletResponse httpServletResponse) throws JsonProcessingException {
// başlıklar CORS
setHeaders(origin, httpServletResponse);
// yanıt
return myController.getAllProduits();
}
Elde edilen sonuç şöyledir:
![]() |
18.11. URL [POST]
Şu örneği inceleyelim:
![]() |
- POST [1]'ten URL [2]'e;
- [3]'te, gönderilen değer. Bu, jSON dizesidir;
- toplamda, [categorie2] adlı bir kategori oluşturmaya çalışıyoruz;
Şu an için herhangi bir kodu değiştirmiyoruz. 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 istenir. Daha önce sadece ilk iki başlık vardı;
- [4]'te, web hizmeti istenen tüm izinleri vermediğinden [5] hatası ortaya çıkıyor;
[AbstractController.sendHeaders] yöntemini şu şekilde değiştiriyoruz:
package spring.cors.server.service;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
public abstract class AbstractCorsController {
@Autowired
private boolean isCorsEnabled;
// seçeneklerin müşteriye gönderilmesi
public void setHeaders(String origin, HttpServletResponse response) {
// Cors izinli mi?
if (!isCorsEnabled || origin == null || !origin.startsWith("http://localhost")) {
return;
}
// CORS başlığı ayarlanıyor
response.addHeader("Access-Control-Allow-Origin", origin);
// belirli başlıklara izin veriliyor
response.addHeader("Access-Control-Allow-Headers", "accept, authorization, content-type");
// GET ve POST'e izin veriliyor
response.addHeader("Access-Control-Allow-Methods", "GET, POST");
}
}
- 21. satır: HTTP [Content-Type] başlığı eklendi (büyük/küçük harf fark etmez);
- 23. satır: HTTP [POST] yöntemi eklendi;
Böylece [POST] yöntemleri, [GET] istekleriyle aynı şekilde işlenir. İşte URL ve [/cors-addArticles] örnekleri:
[MyControllerWithCors] içinde
@RequestMapping(value = "/cors-addCategories", method = RequestMethod.POST, consumes = "application/json; charset=UTF-8", produces = "application/json; charset=UTF-8")
@ResponseBody
public String addCategories(HttpServletRequest request,
@RequestHeader(value = "Origin", required = false) String origin, HttpServletResponse httpServletResponse)
throws JsonProcessingException {
// CORS başlıkları
setHeaders(origin, httpServletResponse);
// yanıt
return myController.addCategories(request);
}
[MyControllerWithHttpOptions]'te
@RequestMapping(value = "/cors-addCategories", method = RequestMethod.OPTIONS)
public void addCategories(HttpServletRequest request,
@RequestHeader(value = "Origin", required = false) String origin, HttpServletResponse httpServletResponse)
throws JsonProcessingException {
// CORS başlıkları
setHeaders(origin, httpServletResponse);
}
Elde edilen sonuç şöyledir:
![]() |
[categorie2] kategorisi veritabanına başarıyla eklendi. SGBD, bu kategoriye 1729 birincil anahtarı atadı.
18.12. [AuthenticateCorsController] denetleyicisi
![]() |
[AuthenticateCorsController] denetleyicisi,URL [/cors-authenticate], halihazırda mevcut olan URL [/authenticate]'i etki alanları arası bir sorgu ile çağırmaya olanak tanır. Kodu şöyledir:
package spring.cors.server.service;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
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.ResponseBody;
import com.fasterxml.jackson.core.JsonProcessingException;
import spring.security.service.AuthenticateController;
@Controller
public class AuthenticateCorsController extends AbstractCorsController {
@Autowired
private AuthenticateController authenticateController;
@RequestMapping(value = "/cors-authenticate", method = RequestMethod.GET)
@ResponseBody
public String authenticate(@RequestHeader(value = "Origin", required = false) String origin,
HttpServletResponse response) throws JsonProcessingException {
// başlıklar CORS
setHeaders(origin, response);
// kaynak yöntemi
return authenticateController.authenticate();
}
@RequestMapping(value = "/cors-authenticate", method = RequestMethod.OPTIONS)
public void corsAuthenticate(@RequestHeader(value = "Origin", required = false) String origin,
HttpServletResponse response) {
// CORS başlıkları
setHeaders(origin, response);
}
}
İşte iki örnek:
![]() |
- Görüntülenen yanıtlar, aşağıdaki jS kodu ile görüntülenir:
function doGet(url) {
// Ajax çağrısı manuel olarak yapılır
$.ajax({
headers : {
'Authorization':'Basic '+authorizationCode
},
url : baseUrl + url,
type : 'GET',
dataType : 'text',
beforeSend : function() {
},
success : function(data) {
// metin sonucu
response.text(data);
},
complete : function() {
},
error : function(jqXHR) {
// sistem hatası
response.text(JSON.stringify(jqXHR.statusCode()));
}
})
}
- [1] yanıtı, [success] işlevinin 14. satırı tarafından görüntülenir;
- [2] yanıtı, [error] işlevinin 20. satırı tarafından görüntülenir. [JSON.stringify] işlevi, meydana gelen hatayı kapsayan [jqXHR.statusCode()] nesnesinin jSON dizesini oluşturur. Bu nesne çok az bilgi verir. Örneğin, sunucu tarafından döndürülen HTTP başlıklarını elde etmek için [jqXHR] nesnesinin diğer yöntemlerini kullanmak mümkündür;
18.13. Conclusion
Uygulamamız artık etki alanları arası istekleri desteklemektedir. Bu istekler, [AppConfig] sınıfındaki yapılandırma ile izin verilebilir veya reddedilebilir:
@ComponentScan(basePackages = { "spring.cors.server.service" })
@Import({ SecurityConfig.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);
}
}







































