日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

Spring Boot2.x-13前后端分离的跨域问题解决方法之Nginx

發布時間:2025/3/21 javascript 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Spring Boot2.x-13前后端分离的跨域问题解决方法之Nginx 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • 概述
  • 瀏覽器同源策略
  • 后臺搭建
    • pom.xml
    • interceptor 配置
    • Controller
    • 啟動測試
  • 瀏覽器和session
  • 后端工程發布到服務器上
  • 問題復現
  • 通過Nginx反向代理解決跨域問題
    • 安裝Nginx
    • 修改配置文件
    • 修改前臺頁面訪問地址
    • 原因分析
    • 啟動Nginx 測試
  • 小結

概述

隨著前后端分離這種開發模式的普及,前臺和后臺分開部署,可能部署在一臺主機上不同的端口下,也有可能部署在多個主機上,前后臺通過ajax或者axios等方式調用restful接口進行交互。由于瀏覽器的“同源策略”,協議、域名、端口號但凡有一個不同,勢必會產生跨域問題。

如果發生跨域的話,瀏覽器中每次請求的session都是一個新的,即sessionId肯定不相同。

我們知道 ,服務器可以為每個用戶瀏覽器創建一個session對象。默認情況下一個瀏覽器中獨占一個session.

http請求是無狀態的,那服務器是如何知道多次瀏覽器的請求是同一個會話呢?

事實上服務器創建session出來后,會將session的id,以cookie的形式回寫給客戶機,這樣,只要瀏覽器不關,再去訪問服務器時,都會帶著session的id號去,服務器發現客戶端瀏覽器攜帶session id過來了,就會使用內存中與之對應的session為之服務。 下文配合代碼和瀏覽器一起來看下。


瀏覽器同源策略

參考阮一峰老師的文章:瀏覽器同源政策及其規避方法


后臺搭建

為了簡單,我們使用Spring Boot 快速搭建個后臺服務,提供restful接口。 我這里加上了interceptor,其實驗證這個問題,沒必要加。 加上一方面是熟悉下攔截器的使用,二來也可以看下request中請求的URI


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><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.3.RELEASE</version><relativePath /> <!-- lookup parent from repository --></parent><groupId>com.artisan</groupId><artifactId>CrossDomain</artifactId><version>0.0.1-SNAPSHOT</version><name>CrossDomainByNginxBackground</name><description>Artisan </description><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><optional>true</optional><scope>true</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

interceptor 配置

不多說了,MyInterceptor.java 參考 Spring Boot2.x-12 Spring Boot2.1.2中Filter和Interceptor 的使用

按照工程中restful的設計,注意下 WebConfig中的攔截路徑即可。


Controller

package com.artisan.controller;import javax.servlet.http.HttpServletRequest;import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;@RestController @RequestMapping("/artisan") public class ArtisanController {@GetMapping("/getValueFromSession")public String getSession(HttpServletRequest request) {// 獲取當前request的session,將屬性設置到session里request.getSession().setAttribute("artisan", "artisanTest");return "sessionId:" + request.getSession().getId() + ", artisans的屬性值:" + request.getSession().getAttribute("artisan");}@GetMapping("/checkCrossDomain")public String checkCrossDomain(HttpServletRequest request) {return "sessionId:" + request.getSession().getId() + ", artisans的屬性值:" + request.getSession().getAttribute("artisan");}}

啟動測試

沒在application.yml中指定server.port ,使用了默認的8080端口,啟動項目,確保可以訪問
http://localhost:8080/artisan/getValueFromSession


不要關閉瀏覽器,繼續訪問
http://localhost:8080/artisan/checkCrossDomain

注意下這兩個sessionId是一樣的,說明是同一個session


瀏覽器和session

剛才概述中

再細化點

  • 用戶向服務器發送請求,比如登錄操作發送用戶名和密碼

  • 服務器驗證通過后,通過HttpServletRequest#getSession()#setAttribute等方法保存相關數據

  • 服務器向用戶返回一個 session_id,瀏覽器set-cookie Cookie 即Cookie = session_id

  • 用戶隨后的每一次請求,都會通過 Cookie,將 session_id 傳回服務器。

  • 服務器收到 session_id,找到前期保存的數據,由此得知用戶的身份。

  • 當然了單節點的情況下還好,如果是集群環境,或者是跨域的服務請求,那么久需要實現session 數據共享,使集群中的每臺服務器都能夠讀取 session。

    總的來說【集群環境下】我目前所了解的有三種思路

    • session復制,比如Tomcat支持的Session復制. 優點:tomcat內置支持 缺點:如果集群過大,session 復制為all to all占用帶寬,效率不高

    • session 數據持久化,寫入redis或者數據庫等。優點架構清晰,缺點是工程量大。而且也需要考慮session數據的持久層的高可用,否則單點登錄就會失敗。

    • 服務端不保存 session ,所有數據都保存在客戶端,比如 JWT (JSON WEB TOKEN)


    我們清空瀏覽器的緩存(包括cookie)

    結合上面建好的工程來演示下上面的描述。

    重新訪問 http://localhost:8080/artisan/getValueFromSession


    上面的截圖就是: 服務器創建session出來后,會將session的id,以cookie的形式回寫給客戶機

    不要關閉瀏覽器,新開個窗口訪問
    http://localhost:8080/artisan/checkCrossDomain


    上面的截圖就是: 只要瀏覽器不關,再去訪問服務器時,都會帶著session的id號去,服務器發現客戶端瀏覽器攜帶session id過來了,就會使用內存中與之對應的session為之服務


    后端工程發布到服務器上

    把剛才的spring boot 服務端,達成了可執行的jar 【sts 工程右鍵-- Run As --Maven build , 輸入clean package (清除、打包)】 ,放到192.168.31.34服務器上 , 為了創造一個不同的ip地址。 順便我把端口號也通過啟動腳本設置成了9000

    啟動腳本如下:

    #!/bin/bash nohup java -jar CrossDomain-0.0.1-SNAPSHOT.jar --server.port=9000 > log.txt & tail -f log.txt

    問題復現

    為了模擬【協議、域名、端口號但凡有一個不同,勢必會產生跨域問題 】,那就讓ip地址+端口號不同吧。

    正好前幾天折騰axis , 搭建axis環境的時候,正好需要用tomcat去驗證下是否搭建成功(把axis拷貝到tomcat的webapps下),那順便借用下這里的index.html ,修改后的index.html如下

    <!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"/><title>Cross Domain Test</title> </head> <body> <h2>Artisan</h2> <button type="submit" id="btn">跨域請求</button> <p id="crossDomainRequest1"></p> <p id="crossDomainRequest2"></p> </body><script src="https://code.jquery.com/jquery-3.2.1.min.js"></script> <script>$("#btn").click(function(event){$.ajax({url: 'http://192.168.31.34:9000/artisan/getValueFromSession',type: "GET",success: function (data) {$("#crossDomainRequest1").html("跨域訪問成功->getValueFromSession方法返回:" + data);$.ajax({url: 'http://192.168.31.34:9000/artisan/checkCrossDomain',type: "GET",success: function (data) {$("#crossDomainRequest2").html("跨域訪問成功->checkCrossDomain方法返回:" + data);}});},error: function (data) {$("#crossDomainRequest1").html("發生跨域錯誤!!");}});}); </script></html>

    啟動tomcat ,訪問 http://localhost:8080/axis/index.html ,點擊按鈕,觀察開發者工具中的Network和Console


    點擊 getValueFromSession 查看,

    服務端其實是返回了,也從側面說明了跨域問題是瀏覽器的“同源策略”導致,和服務端不相干。


    再繼續看下報錯

    Access to XMLHttpRequest at ‘http://192.168.31.34:9000/artisan/getValueFromSession’ from origin ‘http://localhost:8080’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.

    如上 發生了跨域問題。


    通過Nginx反向代理解決跨域問題

    原理: Nginx的反向代理“欺詐”瀏覽器,使得瀏覽器和服務器是同源訪問。

    安裝Nginx

    因為要測試跨域 ,為了方便,服務端放到了服務器上,使用Nginx部署的前臺我們就放到本地吧,所以使用了windows版本的Nginx 。

    Nginx 下載地址: http://nginx.org/en/download.html


    修改配置文件

    worker_processes 1;events {worker_connections 1024; }http {include mime.types;default_type application/octet-stream;sendfile on;keepalive_timeout 65;#前端頁面服務器信息server {#啟動的端口和域名listen 8888; server_name localhost;#添加頭部信息,proxy_set_header用來重定義發往后端服務器的請求頭。#語法 proxy_set_header Field Valueproxy_set_header Cookie $http_cookie;proxy_set_header X-Forwarded-Host $host;proxy_set_header X-Forwarded-Server $host;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;#代理地址 及映射的服務端的地址 # 最重要的配置 location /frontend/ { proxy_pass http://192.168.31.34:9000/; #使用代理地址時末尾加上斜杠"/" # 如下 proxy_set_header 和 add_header 不加經過驗證也是OK的。# 使用add_header指令來設置response headerif ($request_method = 'OPTIONS') {add_header 'Access-Control-Allow-Origin' '*';add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range,Token';add_header 'Access-Control-Max-Age' 1728000;add_header 'Content-Type' 'text/plain; charset=utf-8';add_header 'Content-Length' 0;return 204;}if ($request_method = 'POST') {add_header 'Access-Control-Allow-Origin' '*';add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range,Token';add_header 'Access-Control-Expose-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range,Token';}if ($request_method = 'GET') {add_header 'Access-Control-Allow-Origin' '*';add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range,Token';add_header 'Access-Control-Expose-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range,Token';} }#添加攔截路徑和根目錄location / {root html/artisan; # 根目錄index index.html index.htm; #首頁} } }

    最重要的是 proxy_pass配置

    關于add_header ,比如 GET 增加了 add_header ,在瀏覽器中GET請求的方法可以在response header查看到相關信息

    add_header ‘Access-Control-Expose-Headers’ 必須要加上你請求時所帶的header,比如我們經常用的Token

    參考: https://enable-cors.org/server_nginx.html

    下面的瀏覽器返回截圖,是沒有增加add_header的,故特意貼一張截圖如上,增加上也是OK的,更細粒度的控制,請知悉。


    修改前臺頁面訪問地址

    <!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"/><title>Nginx Cross Domain Test</title> </head> <body> <h2>Artisan</h2><button type="submit" id="btn">跨域請求</button><p id="crossDomainRequest1"></p> <p id="crossDomainRequest2"></p> </body><script src="https://code.jquery.com/jquery-3.2.1.min.js"></script> <script>$("#btn").click(function(event){$.ajax({url: 'http://localhost:8888/frontend/artisan/getValueFromSession',type: "GET",success: function (data) {$("#crossDomainRequest1").html("跨域訪問成功->getValueFromSession方法返回:" + data);$.ajax({url: 'http://localhost:8888/frontend/artisan/checkCrossDomain',type: "GET",success: function (data) {$("#crossDomainRequest2").html("跨域訪問成功->checkCrossDomain方法返回:" + data);}});},error: function (data) {$("#crossDomainRequest1").html("發生跨域錯誤!!");}});}); </script></html>

    原因分析

    先看index.html的存放位置

    與 nginx的配置文件中如下配置保持一致

    同時配置的啟動端口和域名,對應配置文件中的

    所以通過訪問 http://localhost:8888/index.html 就找到了 html/artisan目錄下的index.html文件


    再看下 index.html中修改的請求地址,由原先的直接請求后臺,改為請求Nginx,讓Nginx去轉發請求

    localhost:8888上面說了,下面來看下這個frontend是個啥東西呢? 是自定義的,叫啥都行,只要能對應上就行。

    意思是讓Nginx代理該請求

    html中的兩個地址經過Nginx后,發生如下變化

    請求URL:http://localhost:8888/frontend/artisan/getValueFromSession
    代理后的URL:http://192.168.31.34:9000/artisan/getValueFromSession

    請求URL:http://localhost:8888/frontend/artisan/checkCrossDomain
    代理后的URL:http://192.168.31.34:9000/artisan/checkCrossDomain

    代理后的地址也是192.168.31.34:9000端口了,和服務端 192.168.31.34:9000一致,也就不存在跨域問題了。

    跨域操作實際上是由Nginx的proxy_pass進行完成.

    這個可以從控制臺中得到確認


    啟動Nginx 測試

    雙擊nginx.exe 啟動Nginx , 訪問 http://localhost:8888/index.html

    訪問正常,且是通過一個session , 跨域問題使用Nginx得到解決。


    小結

    • 通過Nginx去解決跨域問題本質上是間接跨域,因為使用反向代理欺騙瀏覽器,所以瀏覽器任務客戶端和服務端在相同的域名中,可以認為是同源訪問,所以session不會丟失。上面的實驗結論也證明了這一點

    • 如果使用CORS實現了直接跨域,主要是在服務端通過給response設置header屬性,幫助服務器資源進行跨域授權。 因為發生跨域訪問,服務器會每次都創建新的Session,會導致session丟失,安全性和靈活性更高,但需要開發人員去解決跨域session丟失的問題

    《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀

    總結

    以上是生活随笔為你收集整理的Spring Boot2.x-13前后端分离的跨域问题解决方法之Nginx的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。