跳至主要内容

統一部署

本文檔說明如何使用 app-web-host 將前端 SPA 與 Spring Boot 統一打包部署。

架構概述

app-web (React SPA)

│ npm run build

app-web/dist/

│ copyWebBuild (Gradle task)

app-web-host/src/main/resources/static/

│ gradle build

app-web-host.war(可部署的應用程式)

為什麼需要統一部署?

優點

  1. 簡化部署:單一 WAR 檔案包含前後端
  2. 安全性:透過 Spring Security 統一管理
  3. CSP Nonce:動態注入 CSP nonce 提升安全性
  4. SPA 路由:自動處理前端路由轉發

app-web-host 提供的功能

  • SPA 路由轉發(所有非 API 路徑導向 index.html)
  • CSP nonce 動態注入
  • 安全 Headers 配置(HSTS、CSP、Referrer-Policy)
  • 動態 base href 注入

建置流程

1. 建置前端

cd app-web
npm run build

2. 複製到 Host

cd ../app-web-host
./gradlew copyWebBuild

3. 建置 WAR

./gradlew clean build

4. 部署

cp build/libs/app-web-host.war /opt/tomcat/webapps/ROOT.war

一鍵建置

cd app-web-host
./gradlew clean deployBuild

此命令會依序執行:

  1. 清理舊建置
  2. 建置前端(npm run build
  3. 複製前端到 static 目錄
  4. 建置 WAR

配置說明

application.yml

app:
web:
# SPA 路由配置
spa-routes:
- /products/**
- /orders/**
- /customers/**
- /settings/**

# 排除的路徑(不做 SPA 轉發)
exclude-paths:
- /api/**
- /actuator/**
- /static/**

security:
# CSP 配置
csp:
enabled: true
report-only: false

安全 Headers

app-web-host 自動配置以下安全 Headers:

Header
Strict-Transport-Securitymax-age=31536000; includeSubDomains
Content-Security-Policy動態生成,包含 nonce
X-Content-Type-Optionsnosniff
X-Frame-OptionsDENY
Referrer-Policystrict-origin-when-cross-origin

CSP Nonce 注入

運作方式

  1. 每個請求生成唯一的 nonce
  2. 注入到 index.html 的 <script><style> 標籤
  3. CSP Header 包含對應的 nonce

index.html 範本

<!DOCTYPE html>
<html>
<head>
<script nonce="{{CSP_NONCE}}">
// inline script
</script>
</head>
<body>
<div id="root"></div>
<script nonce="{{CSP_NONCE}}" src="/assets/main.js"></script>
</body>
</html>

環境變數

變數說明預設值
APP_BASE_HREF應用程式基礎路徑/
APP_API_URLAPI 基礎 URL/api

故障排除

SPA 路由 404

確認路由已在 spa-routes 配置中:

app:
web:
spa-routes:
- /your-new-route/**

CSP 錯誤

檢查瀏覽器開發者工具的 Console,確認是否有 CSP 違規報告。

靜態資源 404

確認前端建置輸出已複製到 src/main/resources/static/

下一步