1. 程式人生 > >SpringBoot 實現前後端分離的跨域訪問(Nginx)

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