背景
現在很多項目早就采用前后端分離的方式開發和部署了。前端代碼部署在nginx服務器上,由nginx直接對外提供靜態文件的服務,后端接口則由nginx做反向代理。
這本來是極為合理的部署方式,但對於一些需要登錄才能進行訪問的系統,負責安全的同事就會提出如下的疑慮:
index.html允許匿名訪問,別有用心之人豈不是可以根據index里的<script>標簽,拿到你所有的前端代碼了?
看來要解決這個問題。
思路
為了保護前端首頁代碼,一次請求的流程應該是下面這樣:
用戶發起首頁的請求,服務端發現用戶沒有登錄,跳轉到登錄頁;
用戶發起首頁的請求,服務端發現用戶已經登錄了,正常輸出首頁的內容。
注意,這里是服務端判斷,而不是客戶端判斷。
判斷有沒有登錄,毫無疑問是是我們的java后端才能做到的事情,但是首頁是html文件,在nginx下面,用戶請求它的時候還沒到后端這里呢,怎么判斷?
當然,你可以把前端文件移到后端tomcat下,由tomcat提供服務,但這樣又走回老路了,這不是一個好方法,不推薦。
其實,在不改變部署架構的前提下,我們簡單的通過nginx的配置和后端接口的配合,就可以達到目的。
簡單來說,利用nginx的rewrite + error_page指令來實現。
- 首先,利用nginx的rewrite指令,把對index的請求,rewrite到后端某個接口上
- 后端這個接口里判斷當前用戶是否已經登錄,如果沒有登錄,返回302跳轉,跳轉到授權頁去登錄
- 如果后端接口判斷當前用戶已經登錄,則返回一個錯誤碼給nginx(例如404),nginx利用error_page,指定404時,輸出index.html的文件內容。
nginx示例配置如下:
server {
listen 80;
server_name www.abc.com;
recursive_error_pages on; #這個選項要開啟
location / {
root /path/to/front-end;
}
location /api #交由tomcat處理
{
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Cookie $http_cookie;
proxy_pass http://localhost:9000;
}
location ~* ^(/|(/index\.html))$ {
#未登錄的情況下,不允許訪問首頁,注意這里rewrite到一個location,而不是直接proxy_pass到后端接口。因為我要在@fallback里利用queryString
rewrite ^/(.*) /abcdefg?res=$request_uri;
}
#
location /abcdefg {
proxy_pass http://localhost:9000/api/home/check-user?res=$request_uri;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_intercept_errors on;
error_page 404 = @fallback;
}
location @fallback {
if ($query_string ~* ^res=([^&]*)) {
set $path $1;
rewrite ^ /local/scripts$path;
}
}
location /local/scripts/ {
internal; #nginx內部才有效的location,外部無法通過/local/scripts/這個路徑訪問
alias /path/to/front-end/; #注意,最后有個/符號
error_page 404 =200 /local/scripts/index.html;
}
}
后端check-user接口示例如下:
@GetMapping("check-user")
public void checkUser(String res, HttpSession session, HttpServletRequest request, HttpServletResponse response) throws IOException {
if(session.getAttribute("User") == null){
response.sendRedirect("login?returnUrl=" + URLEncoder.encode(res, "UTF-8"));
return;
}
response.setStatus(HttpStatus.SC_NOT_FOUND);
}