SpringBoot 實現前後端分離的跨域訪問(Nginx)
序言:使用Nginx反向代理,可以解決跨域無權和Session丟失的問題,十分方便。下面我們以前後端分離為案例,展開Nginx的使用教程。
一. 配置和啟動Nginx
下載地址
注意事項:下載之後,記得解壓到全英文路徑,避免中文路徑導致Nginx啟動失敗。
修改配置
開啟nginx.conf ,清空配置項,然後將下面的配置資訊原封不動拷貝進去:
worker_processes 1; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; #前端頁面伺服器 server { #監聽埠和域名 listen 7000; server_name localhost; #新增頭部資訊 proxy_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 /api/ { proxy_pass http://localhost:8080/; #注意:使用代理地址時末尾記得加上斜槓"/"。 } #新增攔截路徑和根目錄 location / { root html/hehe; #注意:使用"/"攔截全路徑的時候記得放在最後。 index index.html index.htm; #index表示首頁 } } }
快速啟動
在Windows 環境中:
-
快速啟動Nginx:右鍵管理員模式,執行nginx.exe。
-
快速關閉Nginx:在nginx主目錄,新增關閉Nginx的命令。
其中結束Nginx.bat的具體內容如下:
taskkill /f /im nginx.exe
二. 部署前端頁面
前後端分離後,可以直接將靜態資源(例如前端頁面)部署到Nginx的html目錄。這裡我們在$nginx_home/html目錄下建立一個名為hehe的資料夾,並新增一個頁面(index.html)用於跨域訪問測試,index頁面內容如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"/> <title>Page Index</title> </head> <body> <h2>前臺系統7000</h2> <p id="info1"></p> <p id="info2"></p> </body> <script src="js/jquery.js"></script> <script> $.ajax({ url: 'http://localhost:7000/api/user/login/verifyCode', type: "POST", success: function (data) { //1.獲取驗證碼 $("#info1").html("跨域訪問成功:verifyCode:" + data); //2.核對驗證碼 $.ajax({ url: 'http://localhost:7000/api/user/login/checkVerifyCode', type: "POST", success: function (data) { $("#info2").html("跨域訪問成功:checkVerifyCode:" + data); } }); }, error: function (data) { $("#info1").html("跨域失敗!!"); } }); </script> </html>
三. 啟動後端系統
首先在POM檔案新增Web依賴,然後編寫控制層,提供對外的訪問介面。預設啟動埠是8080.
package com.hehe; @SpringBootApplication @RestController @RequestMapping("/user/login/*") public class SpringBootNginxApplication { //在攔截器列印訪問URL @Bean public WebMvcConfigurer webMvcConfigurer() { return new WebMvcConfigurer() { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new HandlerInterceptor() { @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception { if(response.getStatus()/100>=4){ System.err.println("訪問URL:"+request.getAttribute(WebUtils.ERROR_REQUEST_URI_ATTRIBUTE)); }else { System.out.println("訪問URL:"+request.getRequestURI()); } } }); } }; } //提供驗證碼 @RequestMapping("verifyCode") public String verifyCode(HttpServletRequest request) { request.getSession().setAttribute("verifyCode", "N7GX"); return request.getSession().getId() + ":" + request.getSession().getAttribute("verifyCode"); } //核對驗證碼 @RequestMapping("checkVerifyCode") public String checkVerifyCode(HttpServletRequest request) { return request.getSession().getId() + ":" + request.getSession().getAttribute("verifyCode"); } public static void main(String[] args) { SpringApplication.run(SpringBootNginxApplication.class, args); } }
四. 測試跨域訪問
由上圖可以看到,跨域訪問成功,並且Session沒有丟失。
五. Nginx跨域總結
Nginx VS CORS
簡單來說,Nginx是間接跨域,而CORS則實現了直接跨域。Nginx的反向代理“欺詐了”瀏覽器,所以瀏覽器和伺服器都認為是同源訪問,所以Session不會丟失。(PS:如果發生跨域訪問,伺服器會每次都建立新的Session,所以才造成了前後端分離的Session丟失問題。) 至於CORS這種跨域機制的安全性和靈活性更高,但需要自己解決跨域訪問Session丟失的問題,通常情況可以採用Session+Redis來實現Session共享。)
Nginx跨域實現過程:
第1步是開啟頁面,第2步是在這個頁面發起AJAX請求,並且請求的域名埠均與當前訪問頁面相同,屬於同源操作,所以瀏覽器不會報出跨域禁止的錯誤。
第3步是本案例最為關鍵的一步,真正的跨域操作由Nginx的proxy_pass進行完成,併成功將驗證碼資訊以代理的身份返回給瀏覽器,讓瀏覽器處於同源訪問後臺的錯覺。開啟F12可以看到代理伺服器:
image.png
關於proxy_pass 斜槓"/' 的坑
通常情況下,建議大家在代理地址末尾加上"/" ,表示轉發後就是proxy_pass直接拼接對映路徑。
server {
listen 7000;
server_name localhost;
#正確示範: 末尾加斜槓"/"
location /api/ {
proxy_pass http://localhost:8080/hehe/;
}
}
如果代理地址末尾沒有加斜槓的話,表示轉發後也是proxy_pass直接拼接對映路徑,但是,拼接的時候會少了個"/",這樣會引發訪問路徑錯誤。
server {
listen 7000;
server_name localhost;
#錯誤示範: 末尾無斜槓
location /api/ {
proxy_pass http://localhost:8080/hehe;
}
}
為了更方便大家,看到在Nginx調整了proxy_pass有無斜槓的區別,樓主在控制檯列印了每次請求訪問的URL地址,這樣更加清晰:
六. 文件資料
原始碼地址:SpringBoot+Nginx (PS:內附本案例使用的完整Nginx ) 原文連結:https://www.jianshu.com/p/520021853827