21. مدیریت دسترسی بیندامنهای
21.1. Architecture
اکنون مسئله درخواستهای بیندامنهای را بررسی خواهیم کرد. در سند [Tutoriel AngularJS / Spring 4]، یک برنامهٔ کلاینت/سرور توسعه یافته است که در آن کلاینت، برنامهای به نام AngularJS است:
![]() |
- صفحات HTML / CSS / JS از برنامه Angular از سرور [1] ارائه میشوند؛
- در [2]، سرویس [dao] درخواستی به سرور دیگری، یعنی سرور [2]، ارسال میکند. با این حال، این کار توسط مرورگری که برنامه Angular را اجرا میکند، ممنوع است زیرا یک آسیبپذیری امنیتی محسوب میشود. این برنامه فقط میتواند از سروری که از آن منشأ گرفته است، یعنی سرور [1]، درخواست کند؛
در واقع، این گفته که مرورگر مانع از ارسال درخواست توسط اپلیکیشن Angular به سرور [2] میشود، نادرست است. مرورگر در واقع از سرور [2] استعلام میگیرد تا بفهمد آیا به کلاینتی که از دامنهٔ خود آن سرور نمیآید اجازه میدهد برای آن درخواست ارسال کند یا خیر. این تکنیک اشتراکگذاری با نام CORS (اشتراکگذاری منابع فرامنشأ) شناخته میشود. سرور [2] با ارسال هدرهای خاص HTTP اجازه را صادر میکند.
برای نشان دادن مشکلاتی که ممکن است پیش آید، قصد داریم یک برنامهٔ کلاینت/سرور بسازیم که در آن:
- سرور، وبسرور امن ما / jSON خواهد بود؛
- کلاینت یک صفحه ساده HTML خواهد بود که حاوی کد جاوااسکریپت است و درخواستهایی را به وب سرور / jSON ارسال میکند؛
ما معماری زیر را پیادهسازی خواهیم کرد:
![]() |
- در [1]، یک برنامه وب صفحات HTML / jS را ارائه میدهد؛
- در [2]، مرورگر جاوااسکریپت جاسازیشده در صفحات HTML را اجرا میکند تا از سرویس وب امن [3] استعلام بگیرد؛
21.2. پروژه [spring-cors-server-jdbc-generic]
21.2.1. راهاندازی محیط کار
![]() |
- پروژههای فهرستشده در بالا را بارگذاری کنید. پروژههای [spring-cors-*] را میتوانید در پوشه [<exemples>\spring-database-generic\spring-cors] پیدا کنید؛
- کلیدهای Alt-F5 را فشار دهید و تمام پروژههای Maven را مجدداً تولید کنید؛
سپس پیکربندی اجرای به نام [spring-cors-server-jdbc-generic] را اجرا کنید (SGBD و MySQL باید در حال اجرا باشند)، که یک سرویس وب را روی پورت 8081 راهاندازی میکند:
![]() |
پایگاه داده [dbproduitscategories] را با پیکربندی زمان اجرا به نام [spring-jdbc-generic-04-fillDataBase] پر کنید:
![]() |
مخاطبهی زمان اجرا به نام [spring-cors-client-generic] را اجرا کنید که یک برنامهی وب دوم (روی یک نمونهی متفاوت از Tomcat) را روی پورت 8082 راهاندازی میکند:
![]() |
با استفاده از یک مرورگر وب، URL [http://localhost:8082/client.html] را درخواست کنید:
![]() |
- برای [1]، نسخه کوتاه همه دستهبندیها را درخواست میکنیم؛
- در [2]، پاسخ سرور jSON؛
21.2.2. درخواست کلاینت [spring-cors-client-generic]
![]() |
![]() |
فایل [application.properties] به ما امکان میدهد پورت برنامه وب کلاینت را تنظیم کنیم. محتویات آن به شرح زیر است:
server.port=8082
بدین ترتیب:
- کلاینت یک برنامه وب است که در URL [http://localhost:8082] در دسترس است؛
- سرور یک برنامه وب است که در URL [http://localhost:8081] در دسترس است؛
از آنجا که کلاینت از طریق پورت یکسان با سرور دسترسی نمییابد، مسئله درخواستهای بیندامنهای مطرح میشود. در واقع، [http://localhost:8081] و [http://localhost:8082] دو دامنهٔ متفاوت هستند.
21.2.3. پیکربندی Maven
این پروژه یک پروژه Maven با فایل [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 /> <!-- جستجوی والد از مخزن -->
</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>
<!-- افزونهها -->
<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>
- خطوط ۱۴–۱۹: این یک پروژه Spring Boot است؛
- خطوط 27–30: وابستگی [spring-boot-starter-web] استفاده میشود که شامل سرور Tomcat و Spring MVC است؛
21.2.4. مبانی jQuery و جاوااسکریپت
![]() |
برنامهٔ وب صفحهٔ واحدی را که در ادامه آمده است بازمیگرداند:
![]() |
این شامل کد جاوااسکریپت (jS) است که در مرورگر اجرا میشود. اکنون برخی از مبانی جاوااسکریپت را برای کمک به درک کد تشریح میکنیم. کلاینت با استفاده از کتابخانههای jQuery و [https://jquery.com/] که توابع متعددی برای تسهیل توسعه جاوااسکریپت فراهم میکنند، به HTTP فراخوانی خواهد کرد. ما یک فایل استاتیک HTML [jQuery.html] ایجاد میکنیم که آن را در پوشه [static] قرار میدهیم:
![]() |
این فایل شامل موارد زیر خواهد بود:
<!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>
- خط ۶: وارد کردن jQuery;
- خطوط ۱۰–۱۲: یک عنصر از صفحه با شناسه [element1]. ما قصد داریم با این عنصر آزمایش کنیم.
ما باید فایل [jquery-2.1.3.min.js] را دانلود کنیم. میتوانیم آخرین نسخه jQuery را در URL و [http://jquery.com/download/] پیدا کنیم:

فایل دانلود شده را در پوشه [static / js] قرار دهید:
![]() |
پس از انجام این کار، با استفاده از کروم [1-2] نمای ایستا [jQuery.html] را درخواست کنید:
![]() |
در گوگل کروم، عبارت [Ctrl-Maj-I] را وارد کنید تا ابزارهای توسعهدهنده (developer tools) [3] باز شود. زبانه [Console] [4] به شما امکان میدهد کد جاوااسکریپت را اجرا کنید. در زیر، چند دستور جاوااسکریپت برای تایپ کردن به همراه توضیح هر یک ارائه شده است.
|
: مجموعهٔ تمام عناصری را که دارای شناسه هستند بازمیگرداند [element1]، بنابراین معمولاً یک مجموعه از ۰ یا ۱ عنصر، زیرا امکان وجود ندارد دو شناسه یکسان در یک صفحه واحد HTML. | ![]() |
|
: متن [blabla] را به همه عناصر در مجموعه. این تغییر میدهد محتوای نمایشدادهشده توسط صفحه را تغییر میدهد. | ![]() |
|
موارد موجود در مجموعه را پنهان میکند. متن متن [blabla] دیگر نمایش داده نمیشود. | ![]() |
|
: مجموعه را دوباره نمایش میدهد. این به ما امکان میدهد ببینیم که عنصری با id [element1] دارد ویژگی CSS style='display: none;', که مخفی شدن عنصر. | |
|
: عناصر را در مجموعه نمایش میدهد. متن متن [blabla] دوباره ظاهر میشود. این ویژگی است CSS style='display: block;' که این را تضمین میکند display. | ![]() |
|
: یک ویژگی را برای همه عناصر در ... تنظیم میکند مجموعه. ویژگی در اینجا [style] است و مقدار آن [color: red]. متن [blabla] به رنگ قرمز درمیآید. | ![]() |
![]() | |
![]() |
شایان ذکر است که URL مرورگر در طول این عملیاتها بدون تغییر باقی ماند. هیچ ارتباطی با سرور وب برقرار نشد. همه چیز در داخل مرورگر انجام میشود. اکنون بیایید کد منبع صفحه را مشاهده کنیم:
<!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>
این متن اصلی است. این متن تغییرات اعمالشده بر عنصر در خطوط ۱۰ تا ۱۲ را منعکس نمیکند. هنگام اشکالزدایی جاوااسکریپت، مهم است که این موضوع را در نظر داشته باشید. بنابراین، مشاهده کد منبع صفحه نمایشدادهشده اغلب بیفایده است.
21.2.5. کد jS از برنامه
بیایید به کد HTML در صفحهٔ برنامهٔ کلاینت بازگردیم، که از سرویس وب /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">
<!-- متد 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 />
<!-- ارزش ارسالشده -->
<br /> Chaîne jSON à poster : <input type="text" id="posted" size="50" />
<!-- دکمه ارسال -->
<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>
- خط ۶: ما کتابخانه jQuery را وارد میکنیم؛
- خط ۷: ما کدی را که قصد داریم بنویسیم، وارد میکنیم؛
- خطوط ۱۱، ۱۵، ۱۷ و ۲۱: به شناسههای [id] برای اجزای صفحه توجه کنید. جاوااسکریپت از طریق این شناسهها به این اجزا ارجاع میدهد؛
کد [client.js] به شرح زیر است:
//دادههای کلی
var url;
var posted;
var response;
var method;
function requestServer() {
// بازیابی دادههای فرم
var urlValue = url.val();
var postedValue = posted.val();
method = document.forms[0].elements['method'].value;
// به صورت دستی یک فراخوانی Ajax انجام دهید
if (method === "get") {
doGet(urlValue);
} else {
doPost(urlValue, postedValue);
}
}
function doGet(url) {
// ایجاد دستی درخواست Ajax
$.ajax({
headers : {
'احراز هویت: 'Basic YWRtaW46YWRtaW4='
},
url : 'http://'localhost:8081' + url,
type : 'GET',
dataType : 'tex/plain',
beforeSend : function() {
},
success : function(data) {
// نتیجهٔ متنی
response.text(data);
},
complete : function() {
},
error : function(jqXHR) {
// خطای سیستم
response.text(jqXHR.responseText);
}
})
}
function doPost(url, posted) {
//یک فراخوانی Ajax بهصورت دستی انجام میشود
$.ajax({
headers : {
'احراز هویت: 'Basic YWRtaW46YWRtaW4='
},
url : 'http://localhost:8081 ' + url,
type : 'POST',
contentType : 'application/json',
data : posted,
dataType : 'tex/plain',
beforeSend : function() {
},
success : function(data) {
// نتیجهٔ متنی
response.text(data);
},
complete : function() {
},
error : function(jqXHR) {
// خطای سیستم
response.text(jqXHR.responseText);
}
})
}
//هنگام بارگذاری سند
$(document).ready(function() {
// بازیابی ارجاعات به اجزای صفحه
url = $("#url");
posted = $("#posted");
response = $("#response");
});
- خطوط ۷۱–۷۵: کد jS پس از اتمام بارگذاری سند در مرورگر، اجرا میشود؛
- خطوط ۷۳–۷۵: ارجاعات سه مورد از فیلدهای سند HTML بازیابی میشوند؛
- خطوط ۲–۵: متغیرهای سراسری که برای همه توابع تعریفشده در فایل jS قابل دسترس هستند؛
- خط ۹: مقدار واردشده توسط کاربر از URL بازیابی میشود؛
- خط ۱۰: مقداری که کاربر میخواهد ارسال کند، بازیابی میشود؛
- خط ۱۱: متدی ([get] یا [post]) که برای درخواست URL از خط ۹ استفاده میشود، بازیابی میشود:
- «اسناد» به سندی اشاره دارد که توسط مرورگر بارگذاری شده و به عنوان DOM (مدل شیء اسناد) شناخته میشود،
- document.forms[0] به اولین فرم در سند اشاره دارد؛ یک سند ممکن است حاوی چندین فرم باشد. در این مورد، تنها یک فرم وجود دارد،
- document.forms[0].elements['method'] به عنصر فرم اشاره دارد که دارای صفت [name='method'] است. دو مورد از آنها وجود دارد:
<input type="radio" id="get" name="method" value="get" checked="checked" />GET
<input type="radio" id="post" name="method" value="post" />POST
- (ادامه)
- document.forms[0].elements['method'].value مقداری است که برای مؤلفه با ویژگی [name='method'] ارسال خواهد شد. میدانیم که مقدار ارسالشده، مقدار ویژگی [value] از دکمه رادیویی انتخابشده است. بنابراین، در اینجا این مقدار یکی از رشتههای ['get', 'post'] خواهد بود؛
- خطوط ۱۳–۱۸: بسته به اینکه کدام متد (HTTP) باید استفاده شود، متد [doGet] یا [doPost] اجرا میشود؛
- متد jQuery [$.ajax] فراخوانیای به HTTP انجام میدهد؛
- خطوط ۲۳–۲۵: ما با سروری ارتباط برقرار میکنیم که به یک هدر از نوع HTTP یا [Authorization: Basic code] نیاز دارد. ما این هدر را برای کاربر [admin / admin] ایجاد میکنیم، که تنها فرد مجاز برای استعلام از سرور است؛
- خط ۲۶: کاربر مقدار URL از نوع [/getAllLongCategories, /saveCategories, ...] را وارد خواهد کرد. بنابراین این URL باید تکمیل شوند؛
- خط ۲۷: متد HTTP که باید استفاده شود؛
- خط ۲۸: سرور مقدار jSON را برمیگرداند. نوع [text/plain] بهعنوان نوع نتیجه مشخص شده است تا دقیقاً همانطور که دریافت شده نمایش داده شود؛
- خط ۳۳: نمایش پاسخ متنی سرور؛
- خط ۳۹: نمایش هرگونه پیام خطا در قالب متنی؛
- خط ۴۴: متد [doPost] یک پارامتر دوم میگیرد که مقداری است که باید ارسال شود؛
- خط ۵۲: برای نشان دادن اینکه مقدار ارسالشده به صورت یک رشته jSON خواهد بود؛
21.2.6. اجرای کلاینت
برنامهٔ کلاینت یک برنامهٔ کنسول است که توسط کلاس اجرایی زیر، [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);
}
}
- خط ۶: تگ [@EnableAutoConfiguration] یک تگ برای پروژه [Spring Boot] (خط ۴) است. Spring Boot آرشیوهای موجود در کلاسپث پروژه را بررسی خواهد کرد. در این مورد، اینها تمام وابستگیهای Maven هستند که توسط وابستگی واحد فایل [pom.xml] معرفی شدهاند:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
این وابستگی تعداد زیادی آرشیو را معرفی میکند، بهویژه Spring MVC و یک سرور Tomcat. به دلیل وجود این وابستگیها، Spring Boot یک پروژه Spring MVC را با استفاده از مقادیر پیشفرض روی Tomcat پیکربندی خواهد کرد. سپس سرور Tomcat برای اجرا روی پورت 8080 پیکربندی میشود. اگر میخواهید مقادیر پیشفرض انتخابشده توسط Spring Boot را تغییر دهید، میتوانید از فایل [application.properties] در ریشهٔ classpath استفاده کنید (همه چیز در [src / main / resources] در ریشهٔ classpath قرار دارد):
![]() |
ما مشخص میکنیم که سرور Tomcat باید روی پورت 8082 به شرح زیر اجرا شود:
server.port=8082
فهرستی از پارامترهایی که میتوان در [application.properties] استفاده کرد را میتوان در URL (ژوئن ۲۰۱۵) و [http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html] یافت؛
بازگردیم به کد [Client.java]:
- خط ۱۰: متد [SpringApplication.run] صفحه [client.html] را روی سرور Tomcat موجود در کلاسپث پروژه مستقر خواهد کرد؛
21.2.7. URL [/getAllShortCategories]
![]() |
ما در حال راهاندازی هستیم:
- سرور وب/JSON امن را روی پورت 8081 (پیکربندی [spring-security-server-jdbc-generic]) راهاندازی میکنیم؛
- کلاینت این سرور روی پورت 8082 (پیکربندی [spring-cors-client-generic]);
سپس URL، [http://localhost:8082/client.html] و [1] را درخواست میکنیم:
![]() |
- در [2]، ما یک GET را روی URL و [http://localhost:8081/getAllShortCategories] انجام میدهیم؛
ما پاسخی از سرور دریافت نمیکنیم. وقتی به کنسول توسعهدهنده کروم (Ctrl+Shift+I) نگاه میکنیم، با خطایی مواجه میشویم:
![]() |
- در [1]، ما روی زبانه [Network] هستیم؛
- در [2]، میتوانیم ببینیم که درخواست HTTP که ارسال شده، [GET] نیست، بلکه [OPTIONS] است. در مورد درخواست بیندامنهای، مرورگر با ارسال درخواستی به سرور HTTP [OPTIONS] بررسی میکند که تعدادی شرط برآورده شده است. در این مورد، درخواستها همانهایی هستند که با نقاط [5-6] نشان داده شدهاند؛
- در [5]، مرورگر میپرسد که آیا هدف URL از طریق GET قابل دسترسی است. درخواست [Access-Control-Request-Method] نیازمند پاسخی با هدر HTTP و [Access-Control-Allow-Methods] است که نشان دهد روش درخواستی پذیرفته شده است؛
- در [6]، مرورگر هدر HTTP [Origin: http://localhost:8081] را ارسال میکند. این هدر درخواست پاسخی به شکل یک هدر HTTP [Access-Control-Allow-Origin] را میکند که نشان میدهد منشأ مشخصشده پذیرفته شده است؛
- در [7]، مرورگر میپرسد که آیا سربرگهای HTTP، [accept] و [authorization] پذیرفته میشوند یا خیر. درخواست [Access-Control-Request-Headers] منتظر پاسخی است که شامل هدر HTTP [Access-Control-Allow-Headers] باشد و نشان دهد که هدرهای درخواستی پذیرفته شدهاند؛
- در [3] خطایی رخ میدهد. کلیک بر روی آیکون منجر به خطای [4] میشود؛
- در [4]، پیام نشان میدهد که سرور هدر HTTP [Access-Control-Allow-Origin] را ارسال نکرده است، که نشان میدهد آیا منشأ درخواست پذیرفته شده است یا خیر؛
- در [8]، میتوانیم ببینیم که سرور واقعاً این هدر را ارسال نکرده است. در نتیجه، مرورگر از انجام درخواست HTTP GET که در ابتدا درخواست شده بود، خودداری کرده است؛
ما باید وبسرور / jSON را اصلاح کنیم.
21.2.8. یک سرویس وب جدید / json
ما یک پروژه جدید Maven به نام [spring-cors-server-jdbc-generic] ایجاد میکنیم:
![]() |
پیکربندی Maven برای سرویس وب جدید به شرح زیر است:
<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>
<!-- افزونهها -->
<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>
- خطوط ۳۰–۳۲: ما تمام کارهای انجامشده تا به اینجا را با استفاده از آرشیو سرور وب/JSON امن، در خود ادغام میکنیم؛
در نهایت، وابستگیها به شرح زیر است:
![]() |
کلاس پیکربندی [AppConfig] به شرح زیر است:
![]() |
package spring.cors.server.config;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.web.servlet.DispatcherServlet;
@Configuration
@ComponentScan(basePackages = { "spring.cors.server.service" })
@Import({ spring.security.config.AppConfig.class })
public class AppConfig {
// درخواستهای بیندامنهای
@Bean
public boolean isCorsEnabled() {
return true;
}
...
}
- خط ۱۲: این کلاس یک کلاس پیکربندی Spring است؛
- خط ۹: سایر اجزای Spring در بسته [spring.cors.server.service] یافت میشوند؛
- خط ۱۴: بینز (beans) پروژه [spring-security-server-jdbc-generic] وارد میشوند؛
- خطوط ۱۸–۲۱: ما یک کامپوننت اسپرینگ به نام [isCorsEnabled] ایجاد میکنیم که مشخص میکند آیا باید کلاینتها را از خارج دامنه سرور بپذیرد یا خیر؛
21.2.9. کنترلکنندهها
سرویس وب جدید دارای چهار کنترلر است:
![]() |
- [CorsCategorieController] کنترلکنندههای URL را برای پردازش دستهبندیها مدیریت میکند. این کنترلکننده فقط سربرگهای CORS را از کلاینتهای وب مدیریت میکند. در غیر این صورت، کار را به کنترلکننده [CategorieController] در وابستگی [spring-webjson-server-jdbc-generic] واگذار میکند؛
- [CorsProduitController] و [CorsAuthenticateController] نیز به همین ترتیب با واگذاری کار به کنترلکنندههای [ProduitController] در وابستگیهای [spring-webjson-server-jdbc-generic] و [AuthenticateController] این کار را انجام میدهند[spring-security-server-jdbc-generic];
- [CorsController] برای استخراج بخش مشترک در سه کنترلکننده قبلی استفاده میشود؛
21.2.9.1. کنترلکننده [CorsController]
کلاس [CorsController] به شرح زیر است:
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;
//ارسال گزینهها به کلاینت
public void sendOptions(String origin, HttpServletResponse response) {
// آیا CORS مجاز است؟
if (!isCorsEnabled || origin == null || !origin.startsWith("http://localhost")) {
return;
}
// سربرگ CORS را تنظیم کنید
response.addHeader("Access-Control-Allow-Origin", origin);
//برخی هدرها مجاز هستند
response.addHeader("Access-Control-Allow-Headers", "accept, authorization");
// GET مجاز است
response.addHeader("Access-Control-Allow-Methods", "GET");
}
}
- خط ۸: کلاس [CorsController] یک کنترلکننده Spring است؛
- خطوط ۱۱–۱۲: تزریق bean با شناسه [isCorsEnabled] که مشخص میکند آیا باید سربرگهای CORS را مدیریت کرد یا خیر؛
- خطوط ۱۵–۲۶: متد [sendOptions] پاسخها را برای کلاینتهایی که هدرهای CORS را ارسال میکنند، مدیریت میکند؛
- خطوط 17–19: اگر برنامه برای پذیرش درخواستهای بیندامنهای پیکربندی شده باشد، و اگر فرستنده هدر HTTP [Origin] را ارسال کرده باشد، و اگر این مبدا با [http://localhost] شروع شود، در این صورت درخواست بیندامنهای پذیرفته میشود؛ در غیر این صورت، رد خواهد شد؛
- خط ۲۱: اگر کلاینت در دامنه [http://localhost:port] باشد، هدر HTTP ارسال میشود:
که به این معنی است که سرور منشأ کلاینت را میپذیرد؛
- خطوط ۲۲–۲۵: ما دو هدر خاص HTTP را در درخواستهای HTTP و [OPTIONS] شناسایی کردهایم:
در پاسخ به سربرگهای HTTP و [Access-Control-Request-X]، سرور با سربرگهای HTTP و [Access-Control-Allow-X] پاسخ میدهد که در آنها مشخص میکند چه چیزی مجاز است. خطوط ۲۲–۲۵ صرفاً درخواست کلاینت را تکرار میکنند تا نشان دهند که پذیرفته شده است؛
21.2.9.2. کنترلکننده [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) {
// روش منشأ
return categorieController.getAllShortCategories();
}
...
}
- خط ۱۹: آناوتیشن [@RestController] این کلاس را هم یک کامپوننت Spring و هم یک کنترلر MVC میکند، که خود پاسخهایش را به کلاینت ارسال میکند؛
- خط ۲۰: کلاس [CorsCategorieController] از کلاس [CorsController] که همین حالا دیدیم، ارث میبرد؛
- خطوط 22–23: تزریق کنترلکننده [CategorieController categorieController] از وابستگی [spring-webjson-server-jdbc-generic];
- خطوط ۲۵–۲۹: رسیدگی به URL و [/cors-getAllShortCategories] هنگام درخواست از طریق دستورات HTTP و [OPTIONS]. طبق قرار، ما تصمیم گرفتهایم که کلاینتهای وب که میخواهند URL [/U] را از سرویس وب امن فراخوانی کنند، در واقع باید URL [/cors-U] را فراخوانی کنند. بنابراین سرویس وب مستقرشده دو نوع URL خواهد داشت:
- [/U]: برای کلاینتهای غیروب؛
- [/cors-U]: برای کلاینتهای وب؛
- خط ۲۵: متد [/cors-getAllShortCategories] پارامترهای زیر را میپذیرد:
- شیء [@RequestHeader(value = "Origin", required = false)] که سربرگ HTTP و [Origin] را از درخواست بازیابی میکند. این سربرگ توسط مبدأ درخواست ارسال شده است:
اعلام شده است که هدر HTTP [Origin] اختیاری [required = false] است. در این حالت، اگر سربرگ وجود نداشته باشد، پارامتر [String origin] مقدار null را خواهد داشت. با [required = true] که مقدار پیشفرض است، در صورت عدم وجود سربرگ یک استثنا پرتاب میشود. ما میخواستیم از این سناریو اجتناب کنیم؛
- (ادامه)
- شیء [HttpServletResponse response]، که به کلاینتی که درخواست را ارسال کرده بازگردانده میشود؛
این دو پارامتر توسط Spring تزریق میشوند؛
- خط ۲۸: پردازش درخواست به متد [sendOptions] از کلاس والد [CorsController] واگذار میشود؛
- خطوط ۳۱–۳۶: متد [getAllShortCategories] هنگام فراخوانی با GET، متدهای URL و [/cors-getAllShortCategories] را پردازش میکند؛
- خط ۳۵: وظیفه به متد [CategorieController.getAllShortCategories] از وابستگی [spring-webjson-server-jdbc-generic] واگذار میشود؛
اکنون برای آزمایشهای بیشتر آمادهایم. نسخه جدید سرویس وب را راهاندازی میکنیم و متوجه میشویم که مشکل همچنان حل نشده باقی مانده است. هیچ چیز تغییر نکرده است. اگر یک دستور خروجی کنسول به خط ۲۸ بالا اضافه کنیم، هرگز نمایش داده نمیشود، که نشان میدهد متد [corsGetAllShortCategories] در خط ۲۵ هرگز فراخوانی نمیشود.
پس از کمی بررسی، متوجه میشویم که Spring MVC خود با استفاده از پردازش پیشفرض، دستورات HTTP و [OPTIONS] را مدیریت میکند. در نتیجه، همیشه Spring پاسخ میدهد و هرگز متد [corsGetAllShortCategories] در خط ۲۵ فراخوانی نمیشود. این رفتار پیشفرض Spring MVC را میتوان تغییر داد. ما کلاس موجود [AppConfig] را اصلاح میکنیم:
![]() |
package spring.cors.server.config;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.web.servlet.DispatcherServlet;
@Configuration
@ComponentScan(basePackages = { "spring.cors.server.service" })
@Import({ spring.security.config.AppConfig.class })
public class AppConfig {
// درخواستهای بیندامنهای
@Bean
public boolean isCorsEnabled() {
return true;
}
@Autowired
private DispatcherServlet dispatcherServlet;
@PostConstruct
public void init(){
// برنامه درخواستها را خود مدیریت میکند HTTP [OPTIONS]
dispatcherServlet.setDispatchOptionsRequest(true);
}
}
- خطوط ۲۳–۲۴: ما کامپوننت [DispatcherServlet dispatcherServlet] را که در وابستگی [spring-webjson-server-jdbc-generic] تعریف شده بود، تزریق میکنیم؛
- خطوط ۲۶–۳۰: تذکر [@PostConstruct] تضمین میکند که متد [init] پس از نمونهسازی کلاس [AppConfig] و پس از انجام تزریقها توسط Spring اجرا خواهد شد؛
- خط ۲۹: به سرولت دستور داده میشود که دستورات HTTP و [OPTIONS] را به برنامه ارسال کند؛
ما آزمایشها را با این پیکربندی جدید تکرار میکنیم. نتیجه زیر را به دست میآوریم:
![]() |
- در [1]، میتوانیم ببینیم که دو درخواست، HTTP، به URL و [http://localhost:8080/getAllCategories] وجود دارد؛
- در [2]، درخواست [OPTIONS]؛
- در [3]، سه هدر HTTP که ما همیناکنون در پاسخ سرور پیکربندی کردهایم؛
حال بیایید درخواست دوم را بررسی کنیم:
![]() |
- به [1]، درخواست مورد بررسی؛
- به [2]، که همان درخواست GET است. به لطف درخواست اول، [OPTIONS]، مرورگر اطلاعاتی را که درخواست کرده بود دریافت کرد. اکنون در حال ارسال درخواست [GET] است که در ابتدا درخواست شده بود؛
- در [3]، پاسخ سرور؛
- در [4]، سرور jSON را ارسال میکند؛
- در [5]، خطایی رخ داده است؛
- در [6]، پیام خطا؛
توضیح آنچه در اینجا رخ داده دشوارتر است. پاسخ سرور [3] طبیعی است ([HTTP/1.1 200 OK]). بنابراین باید سند درخواستی را در اختیار داشته باشیم. ممکن است سرور واقعاً سند را ارسال کرده باشد اما مرورگر از استفاده از آن جلوگیری میکند زیرا برای آن لازم است که پاسخ به درخواست GET همچنین شامل سربرگهای HTTP و [Access-Control-Allow-Origin:http://localhost:8081] باشد.
ما در حال تغییر روشی هستیم که GET را از URL و [/cors-getAllShortCategories] مدیریت میکند:
@RequestMapping(value = "/cors-getAllShortCategories", method = RequestMethod.GET)
public Response<List<Categorie>> getAllShortCategories(
@RequestHeader(value = "Origin", required = false) String origin, HttpServletResponse response) {
// سربرگها CORS
sendOptions(origin, response);
// روش مبدأ
return categorieController.getAllShortCategories();
}
- خط ۵: همانند درخواست HTTP [OPTIONS]، سرور برای درخواست HTTP [GET]، سربرگهای HTTP CORS را ارسال خواهد کرد؛
پس از این تغییر، نتایج به شرح زیر است:
![]() |
ما در واقع نسخه کوتاه همه دستهبندیها را دریافت کردهایم.
21.2.9.3. URL و [GET]
در کنترلکنندههای [CorsCategorieController, CorsProduitController, CorsAuthenticateController]، کد مربوط به اقداماتی که URL درخواستی را با [GET] پردازش میکنند، از الگوی اقداماتی پیروی میکند که قبلاً URL و [/cors-getAllShortArticles] را پردازش میکردند. خواننده میتواند کد را در مثالهای ارائهشده در این سند بررسی کند. در اینجا مثالی برای URL و [/cors-getAllLongProduits] از کنترلکننده [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) {
// سربرگها CORS
sendOptions(origin, response);
// روش منبع
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]
بیایید مورد زیر را در نظر بگیریم:
![]() |
- ما یک POST [1] را به URL [2] انجام میدهیم؛
- در [3]، مقدار ارسالشده. این رشته jSON از یک دستهبندی بدون محصول است؛
- در نهایت، ما قصد داریم یک دستهبندی به نام [categorie[2]] ایجاد کنیم؛
در این مرحله هیچ کدی را تغییر نمیدهیم. نتیجهٔ بهدستآمده به شرح زیر است:
![]() |
- در [1]، همانند درخواستهای [GET]، یک درخواست [OPTIONS] توسط مرورگر انجام میشود؛
- برای [2]، درخواست مجوز دسترسی برای درخواستی برای [POST] ارسال میشود. قبلاً این [GET] بود؛
- در [3]، درخواست مجوز برای ارسال سربرگهای HTTP و [accept, authorization, content-type] میشود. قبلاً، فقط دو سربرگ اول موجود بودند؛
- در [4]، سرویس وب همه مجوزهای درخواستی را اعطا نمیکند که باعث خطای [5] میشود؛
ما روش [CorsController.sendOptions] را به شرح زیر تغییر میدهیم:
public void sendOptions(String origin, HttpServletResponse response) {
//آیا CORS مجاز است؟
if (!isCorsEnabled || origin == null || !origin.startsWith("http://localhost")) {
return;
}
// سربرگ را تنظیم کنید CORS
response.addHeader("Access-Control-Allow-Origin", origin);
// برخی هدرها مجاز هستند
response.addHeader("Access-Control-Allow-Headers", "accept, authorization, content-type");
// GET و POST مجاز هستند
response.addHeader("Access-Control-Allow-Methods", "GET, POST");
}
}
- خط ۹: سربرگهای HTTP و [Content-Type] اضافه شدهاند (حالت حروف مهم نیست)؛
- خط ۱۱: روش HTTP [POST] اضافه شده است؛
این بدان معناست که متدهای [POST] به همان شیوهای که درخواستهای [GET] پردازش میشوند، مدیریت میگردند. در اینجا مثالی از URL و [/cors-saveCategories] در کنترلر [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) {
// سربرگها CORS
sendOptions(origin, response);
// روش منشأ
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);
}
پس از اعمال این تغییرات، نتیجه به شرح زیر است:
![]() |
کategori [categorie[2]] در واقع به پایگاه داده اضافه شده است. SGBD کلید اصلی 226 را به آن اختصاص داده است. این را میتوان با استفاده از روش GET [/cors-getAllShortCategories] تأیید کرد:
![]() |
21.2.10. نتیجهگیری
اپلیکیشن ما اکنون از درخواستهای بیندامنهای پشتیبانی میکند. این قابلیتها را میتوان از طریق پیکربندی در کلاس [AppConfig] فعال یا غیرفعال کرد:
package spring.cors.server.config;
...
@Configuration
@ComponentScan(basePackages = { "spring.cors.server.service" })
@Import({ spring.security.config.AppConfig.class })
public class AppConfig {
// درخواستهای بیندامنهای
@Bean
public boolean isCorsEnabled() {
return true;
}
...
}
21.3. پروژه اکلیپس [spring-cors-server-jpa-generic]
سرویس وب CORS اکنون توسط پروژه [spring-cors-server-jpa-generic] پیادهسازی خواهد شد، که بر اساس پروژه [spring-security-server-jpa-generic] است، و دسترسی به پایگاه داده را با استفاده از Spring Data JPA مدیریت میکند:
![]() |
پروژه [spring-cors-server-jpa-generic] با کلون کردن پروژه [spring-cors-server-jdbc-generic] که قبلاً بررسی شده بود، ایجاد شده است.
![]() |
سپس، دو تغییر باید انجام شود. اولین تغییر در فایل [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>
<!-- افزونهها -->
<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>
- خطوط ۳۰–۳۲: وابستگی به سرویس وب امن [spring-security-server-jpa-generic]؛
در نهایت، وابستگیهای پروژه به شرح زیر است:
![]() |
توجه: کلیدهای Alt-F5 را فشار دهید و سپس همه پروژهها را مجدداً تولید کنید.
تغییر دوم، بهروزرسانی imports در کلاسهایی است که خطا گزارش میکنند: [Alt-Maj-O].
تمام. سرویس وب CORS را با استفاده از پیکربندی زمان اجرا [spring-cors-server-jpa-generic-hibernate-eclipselink] راهاندازی کنید:
![]() | ![]() |
سپس کلاینت عمومی را راهاندازی میکنیم:
![]() |
و با استفاده از یک مرورگر، URL و [1] را با GET درخواست میکنیم. در [2]، میتوانیم ببینیم که نسخه کوتاهشدهٔ دستهبندیهای بازگشتی شامل فیلد [entityType] است که در نسخه قبلی، JDBC، وجود نداشت.
اکنون به دو معماری CORS دیگر میپردازیم:
- معماریهای CORS / JPA و EclipseLink / DB2؛
- CORS / JPA OpenJpa / Firebird;
21.4. معماری CORS / JPA EclipseLink / DB2
ما معماری زیر را پیادهسازی خواهیم کرد:
![]() |
ما پروژههای زیر را بارگذاری میکنیم:
![]() |
توجه: کلیدهای Alt-F5 را فشار دهید و تمام پروژههای Maven را مجدداً تولید کنید.
SGBD و DB2 را اجرا کنید و بررسی کنید که پایگاه داده [dbproduitscategories] وجود دارد. اگر وجود ندارد، آن را ایجاد کنید (بخش 12.1.2).
کاربران در پایگاه داده [dbproduitscategories] با استفاده از پیکربندی اجرای [spring-security-create-users-hibernate-eclipselink] ایجاد میشوند:
![]() | ![]() |

سپس سرویس وب CORS را با پیکربندی زمان اجرا به نام [spring-cors-server-jpa-generic-hibernate-eclipselink] و کلاینت آن به نام [spring-cors-client-generic] راهاندازی کنید:
![]() | ![]() |
پایگاه داده [dbproduitscategories] را با مقادیر با استفاده از پیکربندی اجرای [spring-jdbc-generic-04-fillDataBase] پر کنید:
![]() |
در نهایت، URL زیر را در یک مرورگر درخواست کنید:
![]() |
21.5. معماری CORS / JPA OpenJPA / Firebird
اکنون معماری زیر را پیادهسازی خواهیم کرد:
![]() |
ما در حال بارگذاری پروژههای زیر هستیم:
![]() |
توجه: کلیدهای Alt-F5 را فشار دهید و تمام پروژههای Maven را مجدداً تولید کنید.
مورد SGBD Firebird را راهاندازی کنید و بررسی کنید که پایگاه داده [dbproduitscategories] وجود دارد. اگر وجود ندارد، آن را ایجاد کنید (بخش 14.1.2).
کاربران در پایگاه داده [dbproduitscategories] با استفاده از پیکربندی اجرای [spring-security-create-users-openjpa] ایجاد میشوند:
![]() | ![]() |

سپس سرویس وب CORS را با استفاده از پیکربندی زمان اجرا به نام [spring-cors-server-jpa-generic-openjpa] راهاندازی کنید:
![]() | ![]() |
سرویس کلاینت CORS را با استفاده از پیکربندی [spring-cors-client-generic] راهاندازی کنید:
![]() |
پایگاه داده [dbproduitscategories] را با استفاده از پیکربندی اجرای [spring-jdbc-generic-04-fillDataBase] با مقادیر پر کنید:
![]() |
در نهایت، URL زیر را در یک مرورگر درخواست کنید:
![]() |


























































