作者簡介:大家好,我是擼代碼的羊駝,前阿里巴巴架構師,現某互聯網公司CTO
聯系v:sulny_ann(17362204968),加我進群,大家一起學習,一起進步,一起對抗互聯網寒冬
# 序言
使用Nginx反向代理,可以解決跨域無權和Session丟失的問題,十分方便。下面我們以前后端分離為案例,展開Nginx的使用教程。
# 配置和啟動Nginx
下載地址
Nginx下載傳送門:http://nginx.org/en/download.html
注意事項:下載之后,記得解壓到全英文路徑,避免中文路徑導致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
# 部署前端頁面
前后端分離后,可以直接將靜態資源(例如前端頁面)部署到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);
}
}
# 測試跨域訪問
打開瀏覽器,訪問?http://localhost:7000/api/user/login/verifyCode?,可以看到后臺獲取的Session和第一次生成的驗證碼。如圖:
打開瀏覽器,訪問?http://localhost:7000/api/user/login/checkVerifyCode?,可以看到后臺獲取的Session和第二次取出的驗證碼。
由上圖可以看到,跨域訪問成功,并且Session沒有丟失。
# Nginx跨域總結
Nginx VS ?CORS
簡單來說,Nginx是間接跨域,而CORS則實現了直接跨域。Nginx的反向代理“欺詐了”瀏覽器,所以瀏覽器和服務器都認為是同源訪問,所以Session不會丟失。
(PS:如果發生跨域訪問,服務器會每次都創建新的Session,所以才造成了前后端分離的Session丟失問題。) 至于CORS這種跨域機制的安全性和靈活性更高,但需要自己解決跨域訪問Session丟失的問題,通常情況可以采用Session+Redis來實現Session共享。)
Nginx跨域實現過程:
第1步:http://localhost:7000/index.html
第2步:http://localhost:7000/api/user/login/verifyCode
第1步是打開頁面,第2步是在這個頁面發起AJAX請求,并且請求的域名端口均與當前訪問頁面相同,屬于同源操作,所以瀏覽器不會報出跨域禁止的錯誤。
第3步:http://localhost:8080/user/login/verifyCode
第3步是本案例最為關鍵的一步,真正的跨域操作由Nginx的proxy_pass進行完成,并成功將驗證碼信息以代理的身份返回給瀏覽器,讓瀏覽器處于同源訪問后臺的錯覺。打開F12可以看到代理服務器:
關于proxy_pass 斜杠"/' 的坑
通常情況下,建議大家在代理地址末尾加上"/" ,表示轉發后就是proxy_pass直接拼接映射路徑。
Nginx代理效果:
轉發前URL:http://localhost:7000/api/user/login/verifyCode
轉發后URL:http://localhost:8080/hehe/user/login/verifyCode
???????
server {
listen 7000;
server_name localhost;
#正確示范: 末尾加斜杠"/"
location /api/ {
proxy_pass http://localhost:8080/hehe/;
}
}
如果代理地址末尾沒有加斜杠的話,表示轉發后也是proxy_pass直接拼接映射路徑,但是,拼接的時候會少了個"/",這樣會引發訪問路徑錯誤。
Nginx代理效果:
轉發前URL:http://localhost:7000/api/user/login/verifyCode
轉發后URL:http://localhost:8080/heheuser/login/verifyCode
???????
server {
listen 7000;
server_name localhost;
#錯誤示范: 末尾無斜杠
location /api/ {
proxy_pass http://localhost:8080/hehe;
}
}
為了更方便大家,看到在Nginx調整了proxy_pass有無斜杠的區別,樓主在控制臺打印了每次請求訪問的URL地址,這樣更加清晰:
具體做法:關閉Nginx 將代理地址修改為:proxy_pass
http://localhost:8080/hehe?,然后啟動Nginx,在瀏覽器訪問http://localhost:7000/api/user/login/verifyCode,然后查看控制臺打印的URL信息。清楚的看到了因為少了斜杠而導致拼接成錯誤路徑,如下: