從前端到服務器全棧搭建我的博客

全棧開發—博客前端展現(Nuxt.js)

這個開發的想法是這樣來的,大概兩個月前,騰訊雲的工做人員打電話給我,說個人域名沒有解析到騰訊雲的服務器上,並且頁腳也沒有備案號。我當時就震驚了,竟然會打電話給我,然而個人大學時代買的服務器已通過期了...因而爲了拯救個人域名,拯救我申請了好久的備案號,決意要全棧打造一個屬於本身的博客系統。javascript

主要技術

  • Nuxt.js
  • Axios
  • marked + highlight.js
  • scss

項目特色

  • 適配多個分辨率,移動端到桌面端無縫切換
  • 支持白晝黑夜主題切換(試試點擊shirmy的太陽或月亮)
  • 文章圖片懶加載
  • 評論、留言、搜索、點贊、多個做者
  • SSR服務端渲染(seo)

技術總結

什麼是服務端渲染(server side render)?

服務端渲染則把Ajax請求放到服務端,頁面加載到瀏覽器或客戶端前就已經把數據填充到頁面模板行程完整的頁面。css

優點

  • 減小首次http請求,在服務端請求首屏數據,直接渲染html
  • 利於SEO,網絡爬蟲能夠抓取到完整的頁面信息

劣勢

  • 服務端壓力大
  • 須要掌握從前端到服務端的開發(多學點很差嗎?)

什麼是客戶端渲染(client side render)?

客戶端渲染就是就是在客戶端經過Ajax請求獲取數據,而後在客戶端生成DOM插入到htmlhtml

CSR 優點

  • 先後端分離,各司其職
  • 局部刷新,無需從新刷新頁面
  • 服務器壓力小
  • 更好的實現各類前端效果

CSR 劣勢

  • 首屏渲染慢,須要下載JSCSS文件
  • 不利於SEO,爬蟲抓取不到完整的頁面數據

SSR vs CSR

門戶網站、博客網站等須要SEO優化的網站使用服務端渲染,管理後臺等內部系統或不須要SEO優化的網站使用客戶端渲染前端

對比圖

服務端渲染

前端渲染

如何實現白晝黑夜主題切換?

在這裏,要使用CSS3變量配合scss進行控制,經過控制<body>標籤的id來約束白晝或黑夜的顏色值,再給相應的屬性加上transition屬性實現顏色切換時的過渡,請看下面的示例:vue

@mixin theme(
  $theme-primary
) {
  --theme-primary: #{$theme-primary}
}

body {
  &#light {
    @include theme(
      #theme-primary: #fff,
    )
  }

  &#dark {
    @include theme(
      #theme-primary: #000,
    )
  }
}
複製代碼

全局引入上面的scss文件,這樣就能夠直接經過設置<body>標籤的id的值爲darklight--theme-primary賦予不一樣的顏色值,此時就能直接在須要應用該顏色的元素上進行以下設置:java

.example-class {
  color: var(--theme-primary);
}
複製代碼

在個人博客shirmy中點擊月亮/太陽便可看到效果,亦可用一樣的方式定義多套主題色。node

切換頁面改變頁面title

Nuxt內置了head屬性配置,head配置直接修改根目錄下的nuxt.config.js文件就能夠了,而且還內置vue-meta插件,所以想要在不一樣頁面改變相應的title,請看如下作法:ios

// nuxt.config.js
module.exports = {
  head: {
    // 組件中的 head() 方法返回的字符串會替換 %s
    titleTemplate: '%s | shirmy',
  }
}

// 文章詳情頁 pages/article/_id.vue
export default {
  head() {
    return {
      title: this.article.title
    }
  }
}
複製代碼

如此一來,當查看某篇文章詳情的時候就會觸發head()方法,title顯示爲article title | shirmynginx

切換頁面讓頁面滾動保持在頂部

只要修改nuxt.config.js配置便可,而且能夠根據傳入的參數作一些自定義的配置。git

// nuxt.config.js
module.exports = {
  router: {
    scrollBehavior: function (to, from, savedPosition) {
      return { x: 0, y: 0 }
    }
  },
}
複製代碼

Nuxt中的fetch()

fetch()Nuxt中獨有的方法,它會在組件初始化前被調用,所以沒法經過this獲取組件對象。好比進入首頁或從首頁切換到歸檔頁,在進入頁面前會先執行fetch()方法,爲了異步獲取數據,fetch()方法必須返回Promise,所以能夠直接返回一個Promise或者使用async await(async await其本質就是返回Promise)。

該方法不會設置組件的數據,若是想要設置組件的數據,或者使用context上下文,可使用asyncData

export default {
  // 雖然沒法經過 this.$nuxt.$route 獲取路由參數,可是能夠經過 params 來獲取
  async fetch({ store, params }) {
    await store.dispatch('about/getAuthor', params.id)
    await store.dispatch('about/getArticles', {
      authorId: params.id,
      page: 0
    })
  }
}
複製代碼

這樣就能確保內容已經渲染好再下載到瀏覽器,若是使用mounted等生命週期鉤子,則是在頁面下載到瀏覽器後再獲取數據,起不到SSR服務端渲染的效果。

style-resource

爲了方便統一管理scss變量,一般會在目錄中建立variables.scssmixin.scss,可是咱們寫的vue文件這麼多,若是都要一個個導入豈不是吃力不討好,這時能夠藉助style-resource

npm install -S @nuxtjs/style-resources
複製代碼

修改nuxt.config.js文件:

// nuxt.config.js
module.exports = {
  styleResources: {
    scss: ['./assets/scss/variables.scss', './assets/scss/mixin.scss']
  }
},
複製代碼

圖片懶加載

圖片懶加載的關鍵是使用IntersectionObserver,IE瀏覽器不兼容,須要使用polyfill。該WebAPI用於監聽元素是否出如今頂級文檔視窗中。

經過這個WebAPI,咱們能夠把<img>標籤的src屬性地址先掛在data-src屬性上,當該元素出如今視窗時就會觸發IntersectionObserver的的回調方法,此時再給<img>標籤的src屬性賦予先前掛在data-src上的地址

複製時攜帶轉載聲明

監聽copy事件,而後經過getSelection()方法獲取複製的內容,在經過clipboardDatasetData()方法在複製內容上加上轉載信息:

if (process.env.NODE_ENV === 'production') {
  const copyText = ` --------------------- 做者:shirmy 連接:${location.href} 來源:https://www.shirmy.me 商業轉載請聯繫做者得到受權,非商業轉載請註明出處。`

  document.addEventListener('copy', e => {
    if (!window.getSelection) {
      return
    }
    const content = window.getSelection().toString()

    e.clipboardData.setData('text/plain', content + copyText)
    e.clipboardData.setData('text/html', content + copyText)
    e.preventDefault()
  })
}
複製代碼

markdown防止XSS攻擊

若是讀者在評論中輸入<script>alert('xss')</script>,那麼進入文章詳情頁時就會觸發這段代碼,這時候,咱們就須要把script過濾掉,固然XSS還有許多相似的方式,可是萬變不離其宗

在這裏,我藉助了DOMPurify

npm install dompurify -S
複製代碼
DOMPurify.sanitize(html)
複製代碼

使用谷歌分析服務

首先進入官網Google 統計分析服務,並註冊你的帳號,獲得一個格式如GA_MEASUREMENT_ID的媒體資源ID

而後直接修改nuxt.config.js,固然也能夠自定義(gtag.js開發指南):

// nuxt.config.js
module.exports = {
  head: {
    // 其它配置...
    script: [
      // 其它配置...
      {
        async: 'async',
        type: 'text/javascript',
        // GA_MEASUREMENT_ID 替換爲你剛剛註冊獲得的媒體資源ID
        src: 'https://www.googletagmanager.com/gtag/js?id=GA_MEASUREMENT_ID'
      },
      {
        // Global site tag (gtag.js) - Google Analytics
        type: 'text/javascript',
        // GA_MEASUREMENT_ID 替換爲你剛剛註冊獲得的媒體資源ID
        innerHTML: ` window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'GA_MEASUREMENT_ID'); `
      }
    ]
  }
}
複製代碼

參考文檔

全棧開發—博客後臺管理系統

主要技術

  • Vue.js + Vue Router + Vuex
  • Axios
  • Element UI

項目特色

  • 權限控制
  • 用戶無感知token刷新
  • 支持用戶頭像上傳
  • 簡潔易用的菜單配置
  • 豐富的文章條件篩選

screenshot

文章篩選

新增做者

技術總結

如何實現菜單配置?

咱們知道,菜單欄是分爲多級的,其實它是和router對應上的。所以咱們能夠把一級每一個一級菜單抽出來,做爲單獨的一個對象,而後導入這些配置項,經過對數據結構的整理和重組,組合成咱們所須要的路由文件。

// article.js
export const articleRouter = {
  // ...路由配置(名稱,路徑,是否顯示,圖標,子路由等屬性)
}
// author.js
export const authorRouter = {
  // ...路由配置
}
// index.js
let homeRouter = [
  {
    // ...路由配置
  },
  articleRouter,
  authorRouter
]
// step1: 根據本身定義的配置進行處理,並供菜單欄遍歷使用
// step2: 深度遍歷構建路由
// step3: 插入到 Vue Router 中
// routes.js
const routes = [
  {
    path: '',
    name: 'Home',
    redirect: '/about',
    component: Home,
    children: [
      // 這裏就是與菜單相對應的路由數組
      ...homeRouter
    ]
  },
  // 這裏能夠額外配置一些非菜單路由
]
複製代碼

經過這種方式,就能夠更加靈活的給每一個路由添加特定的配置,實現個性化的定製,實現不一樣頁面的解耦。

限制上傳圖片大小

經過<input type="file">獲取到圖片後,經過使用URL.createObjectURL()靜態方法建立DOMString。而後賦值給Image對象,再根據該Image對象進行判斷

fileChange(e) {
  const imgFile = e.target.files[0]
  // 圖標大小大於 1M
  if (imgFile.size > 1024 * 1024 * 1) {
    // ...
  }

  const imgSrc = window.URL.createObjectURL(imgFile)
  const image = new Image()
  image.src = imgSrc
  image.onload = () => {
    // 圖片加載後獲取圖片寬度
    const w = image.width
    // 圖片加載後獲取圖片高度
    const h = image.height
    // ... 後續處理
  }
}
複製代碼

用戶無感知刷新

  1. 服務端生成兩個Token,一個accessToken,一個refreshTokenaccessToken是普通的訪問token,一般設置爲一兩個小時,refreshToken用於驗證用戶是否能夠再獲取一個新的accessToken,能夠設置爲一個月。
  2. 當用戶停留在管理系統頁面沒有退出,但accessToken過時了,在進行操做時,先把這個失敗的請求保存起來,而後再向服務器驗證refreshToken,若是經過,則頒發一個新的accessToken,再經過這個新的accessToken去請求剛剛緩存起來的請求。
  3. 若是refreshToken驗證不經過,則請求失敗,直接調用相關的loginOut()方法

按需加載

好比咱們要使用lodash中的throttle方法,咱們能夠這樣作:

// 只導入 throttle 相關模塊
import throttle from 'lodash/throttle'
複製代碼

這樣能極大減小打包後的體積,這種方式很是有用,經常使用的,包括ECharts也是如此。

編碼相關

  1. 在文章列表的級聯篩選中,咱們一般經過targetId === id來判斷是否選中某個對象,當有多個篩選條件時,咱們能夠經過如下方式把多個相同的邏輯進行以下封裝:
selectFilter(id, target) {
  // 若是和當前選擇同樣則沒必要再選中了
  if (id === this[target]) {
    return
  }
  // 只須要額外傳入和組件data相同的字符串名就不用再寫多個函數了
  this[target] = id
  this.getArticles()
}
複製代碼
  1. 全局過濾器註冊
// filter/index.js
export default {
  format(value, format) {},
  filter(value) {}
}

// filters 中包含多個過濾器
import filters from '@/services/filter'
import Vue from 'vue'

// main.js
// 全局過濾器,不要一個個註冊,全局組件同理
Object.keys(filters).forEach(k => Vue.filter(k, filters[k]))
複製代碼

參考文檔

全棧開發—博客服務端(Koa2)

主要技術

  • Koa2
  • MySQL
  • Sequelize
  • JWT
  • Axios
  • Validator.js

項目特色

  • 封裝權限控制中間件
  • 清晰的項目結構
  • 簡潔易用的參數校驗、異常處理
  • 支持用戶無感知刷新

技術總結

項目結構

├── app                     # 業務代碼
│   ├── api                 # api
│   │   ├── blog            # 提供給博客前端API
│   │   └── v1              # 提供給博客管理系統API
│   ├── dao                 # 數據庫操做層
│   ├── lib                 # 工具函數、工具類、常量
│   ├── models              # Sequelize model 層
│   └── validators          # 參數校驗工具類
├── config                  # 全局項目配置
├── core                    # 核心庫
│   ├── db.js               # Sequelize 全局配置
│   ├── http-exception.js   # 異常處理定義
│   ├── init.js             # 項目初始化
│   ├── lin-validator.js    # 參數校驗插件
│   ├── multipart.js        # 文件上傳處理
│   └── util.js             # 核心庫工具函數
├── middleware              # 中間件
複製代碼

導入模塊太多?

Koa2寫服務端代碼,有一個體驗就是文件導出來導出去,各類路徑,這時咱們可使用別名:

npm install -S nodule-alias
複製代碼
{
  // ...
  "_moduleAliases": {
    "@models": "app/models",
  }
}
複製代碼
// app.js
require('module-alias/register')

// article.js
const { Article } = require('@models')
複製代碼

自動註冊路由中間件

當路由模塊不少時,在app.js中一個個導入豈不是越寫越長,這時咱們能夠藉助require-directory工具

const requireDirectory = require('require-directory')
const Router = require('koa-router')

class InitManager {
  static initCore(app) {
    // 入口
    InitManager.app = app
    InitManager.initLoadRoutes()
  }

  static initLoadRoutes() {
    // process.cwd() 獲取絕對路徑
    const appDirectory = `${process.cwd()}/app/api`
    // 使用 require-directory 提供的方法導入自動導入路由文件
    requireDirectory(module, appDirectory, {
      visit: whenLoadingModule
    })

    // 註冊全部檢測到的 Koa 路由
    function whenLoadingModule(obj) {
      if (obj instanceof Router) {
        InitManager.app.use(obj.routes())
      }
    }
  }
}

module.exports = InitManager

// app.js
const Koa = require('koa')
const app = new Koa()

InitManager.initCore(app)
複製代碼

如何使用七牛雲上傳?

實際上官方文檔就已經寫得很清楚了:Node.js SDKV6,無非就是安裝插件,照着文檔搬運代碼。

在這裏要注意的是,若是上傳多個文件,咱們須要放在一個循環裏逐一上傳,而上傳又是異步的,那麼如何驗證全部文件都已經上傳成功,在這裏咱們可使用Promise.all()方法進行封裝,舉個栗子:

class UpLoader {
  async upload(files) {
    let promise = []
    
    for (const file of files) {
      // ...
      promise.push(new Promise((resolve, reject) => {
        // 執行上傳邏輯
        // resolve() or reject()
      }))
    }

    Promise.all(promises).then(res => {
      // ... 所有成功
    }).catch(e => {
      // ... 有上傳失敗的
    })
  }
}
複製代碼

全局異常捕獲中間件

// http-exception.js
class HttpException extends Error {
  constructor(msg = '服務器異常', errorCode = 10000, code = 400) {
    super()
    this.msg = msg
    this.errorCode = errorCode
    this.code = code
  }
}

// exception.js
const { HttpException } = require('@exception')

const catchError = async (ctx, next) => {
  try {
    // 利用洋蔥圈模型的特性,全部請求都會通過這裏
    await next()
  } catch (error) {
    const isHttpException = error instanceof HttpException
    const isDev = global.config.environment = 'dev'
    if (isDev && !isHttpException) {
      throw error
    }
    // 已知錯誤
    if (isHttpException) {
      ctx.body = {
        msg: error.msg,
        errorCode: error.errorCode,
        request: `${ctx.method}: ${ctx.path}`
      }
      ctx.status = error.code
    } else {
      // 未知錯誤
      ctx.body = {
        msg: '服務器內部錯誤',
        errorCode: 999,
        request: `${ctx.method}: ${ctx.path}`
      }
      ctx.status = 500
    }
  }
}

module.exports = catchError

// app.js
const catchError = require('./middleware/exception')

app.use(catchError)
複製代碼

如何進行權限校驗

權限校驗是經過JWT實現的,使用JWT能夠用用戶ID、超時時間、權限級別給用戶生成一個Token返回到客戶端,客戶端再把這個Token存儲到cookie中,步驟以下:

  1. 安裝jsonwebtoken插件
  2. 給用戶頒發一個由用戶id、用戶權限級別、超時時間生成的accessToken
  3. 客戶端把accessToken保存到cookie中,而後之後的每次發送請求都會攜帶這個token
  4. 使用koa中間件,在API處理前校驗token是否合法,而且判斷用戶是否有權限訪問該API

其它業務代碼及框架的基本用法就很少說了,能夠直接參考smile-blog-koa

參考文檔

全棧開發—Travis CI持續集成

前期準備

  • 我使用的 macOS 10.14.5
  • 首先咱們須要註冊一個GitHub帳號而且建立項目,把已經寫好的代碼託管到GitHub上
  • 而後登陸Travis CI官網,使用GitHub帳號登陸受權,這時Travis就能獲取到Github上的項目
  • 準備一個雲服務器,我用的是騰訊雲(CentOS 7.2 64位)
  • 選擇須要持續集成的項目,看下圖:

關聯項目

持續集成

這裏以一個前端項目爲例,首先在項目根目錄下建立一個.travis.yml文件,並寫入如下代碼保存:

language: node_js
cache:
 directories:
 - node_modules      # 緩存 node_modules
node_js: stable       # 穩定版本
branches:
 only:
 - master            # 每次 push 或者 pull request 時會觸發持續集成
install:
 - npm install       # 固然你可使用 yarn
scripts:
 - npm test          # 執行測試
 - npm build         # build
複製代碼

這時只要把項目pushmaster分支就會觸發部署,這一步的目的是驗證項目是否經過測試、編譯,模擬生產環境進行自動測試,提早發現錯誤。效果圖大概長這樣:

效果圖

持續部署

建立 rsa 對

  1. 進入你的服務器,查看有沒有rsa對,若是沒有則使用ssh-keygen建立,並賦予權限:
cd ~/.ssh

sudo chmod 700 ~/.ssh/

sudo chmod 600 ~/.ssh/*
複製代碼
  1. 將公鑰添加到受信任列表,並從新賦予權限
cat id_rsa.pub >> authorized_keys

cat authorized_keys

sudo chmod 600 ~/.ssh/*
複製代碼
  1. 若是沒有id_rsaid_rsa.pub文件,其實就跟你配置GitHub SSH Key同樣:
ssh-keygen -t rsa -C "這裏替換成你的郵箱"

# 獲取祕鑰並複製之
cat id_rsa.pub
複製代碼

瀏覽器打開GitHub:用戶頭像 -> Setting -> SSH and GPG keys -> New SSH Key,添加之

安裝 Ruby

Ruby官方安裝指南

上面的能夠找到Windows、Mac、Linux的Ruby安裝方式,我使用的是Mac,直接使用Homebrew安裝,固然Macb自己就自帶Ruby:

安裝教程

# 安裝 homebrew
ruby -e "$(curl -fsSL https://raw.github.com/Homebrew/homebrew/go/install)"

# 安裝最新版本 Ruby

# 更新 brew 支持的版本信息
brew update

# 編譯安裝最新版本
brew install ruby

# 檢驗是否安裝成功
ruby --version
複製代碼

安裝ruby是由於travis客戶端使用ruby寫的

安裝配置 Travis 客戶端

# 安裝
gem install travis
複製代碼

進入到要部署的項目根目錄下,用GitHub的帳號密碼登陸Travis客戶端:

travis login --auto
複製代碼

利用服務器私鑰加密生成id_rsa.enc文件,travis會藉助它來登陸你的服務器,這樣就能夠在你的服務器上進行自動部署的操做了:

travis encrypt-file ~/.ssh/id_rsa --add
複製代碼

執行完這句後,項目根目錄下就會生成一個id_rsa.enc文件,而且先前在目錄下建立好的.travis.yml文件會多出這樣一行:

before_install:
- openssl aes-256-cbc -K $encrypted_######_key -iv $encrypted_#######_iv -in id_rsa.enc -out ~\/.ssh/id_rsa -d
複製代碼

踩了不少坑?不要緊,這時已經很是接近成功了

執行部署

Travis CI自帶了一些生命鉤子,咱們能夠在相應的生命鉤子(Travis CI Job Lifecycle)搞事情,其中after_success鉤子是執行部署腳本的鉤子。

此時在.travis.yml上添加部署腳本,若是你不想暴露你在服務器上的部署用戶名和服務器IP,你能夠在travis中配置環境變量

項目部署面板 -> 右側的More options -> Settings -> 找到Environment Variables -> 輸入變量名,變量值,而後在yml文件中用$變量名來引用

language: node_js
cache:
 directories:
 - node_modules
node_js: stable
branches:
 only:
 - master
before_install:
  # 注意 這裏我改爲了 ~/.ssh/id_rsa 而不是自動生成的 ~\/.ssh/id_rsa
 - openssl aes-256-cbc -K $encrypted_######_key -iv $encrypted_######_iv -in id_rsa.enc -out ~/.ssh/id_rsa -d
  # 賦予權限
 - chmod 600 ~/.ssh/id_rsa
install:
 - npm install
scripts:
 - npm test          # 執行測試
 - npm build         # build
# 執行部署腳本
after_success:
# $DEPLOY_USER 環境變量:服務器用戶名
# $DEPLOY_HOST 環境變量:服務器IP
# 項目目錄 你在服務器上的項目目錄
- ssh "$DEPLOY_USER"@"$DEPLOY_HOST" -o StrictHostKeyChecking=no 'cd 項目目錄 && git pull && bash ./script/deploy.sh'
addons:
 ssh_known_hosts:
  # 你的服務器IP
 - "$DEPLOY_HOST"
複製代碼

在根目錄下建立script文件夾放部署腳本

# script/deploy.sh
#!/bin/bash 
echo 'npm install'
npm install

echo 'npm run build'
npm run build

echo 'success'
複製代碼

部署成功以後,就會看到上面有個下面這樣的徽章,點擊它,按照你須要的格式複製使用

Build Status

總結

再來回顧一下流程:

  1. Travis官網關聯GitHub項目
  2. 在服務器中生成rsa對,把rsa對添加到github上,並把項目拉到服務器中
  3. 安裝ruby,安裝travis客戶端並登錄,在項目目錄下生成id_rsa.enc
  4. 編寫項目部署腳本
  5. 把項目推送或PR到相應的分支上
  6. 獲取徽章

使用Nginx配置HTTPS和反向代理

想起大學時本身建了個簡單的網站,當時使用的http,而後打開的時候常常都被http劫持,那是真的迫不得已,因此此次必需要整個https。https是啥?想必你們都知道,不懂的直接超文本安全傳輸協議

HTTPS效果圖

配置HTTPS

證書申請

  • 電腦操做系統是macOS 10.14.5
  • 服務器:騰訊雲CentOS 7.2
  • Nginx版本:1.16.0

在這裏,我使用的是騰訊雲的免費證書:

  • 進入控制檯 -> 左上角雲產品 -> 域名和網站 -> SSL證書 -> 申請免費證書
  • 申請以後,大概過不了幾分鐘,就經過審覈了,而後下載之
  • 下載以後有幾個文件:

目錄

證書配置

首先登陸你的服務器,咱們須要建立一個文件夾來放咱們的證書

mkdir /usr/local/nginx/cert
複製代碼

而後使用scp命令把咱們的證書傳到服務器上

# 進入到放證書的文件夾
cd shirmy.me

# 把 Nginx 下的文件遠程拷貝到服務器上
scp -r ./Nginx/* 你的服務器用戶名@你的服務器IP:/usr/local/nginx/cert
複製代碼

接下來就能夠修改Nginx的配置了,其實騰訊雲提供了很完善的證書安裝指引,裏面有除了Nginx以外的其它服務器配置方式:

騰訊雲SSL證書安裝指引

若是直接使用文檔中的方式,Nginx會報警告,須要作一些小的修改:

server {
    listen 443; #SSL 訪問端口號爲 443
    server_name www.shirmy.com; #填寫綁定證書的域名
    # ssl on; #啓用 SSL 功能 這行會報警告 去掉便可
    ssl_certificate ../cert/1_www.shirmy.me_bundle.crt; #證書文件名稱
    ssl_certificate_key ../cert/2_www.shirmy.me.key; #私鑰文件名稱
    ssl_session_timeout 5m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2; #請按照這個協議配置
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE; #請按照這個套件配置,配置加密套件,寫法遵循 openssl 標準。
    ssl_prefer_server_ciphers on;

    location / {
        root /var/www/www.domain.com; #網站主頁路徑。此路徑僅供參考,具體請您按照實際目錄操做。
        index  index.html index.htm;
        # 這是爲解決 Vue Router 哈希模式刷新後404的問題 Nginx 找不到文件後會在內部發起一個子請求到根目錄下的 index.html
        try_files $uri $uri/ /index.html;
    }
}
複製代碼

HTTP跳轉到HTTPS

騰訊雲文檔提供瞭如下的配置方式,可是我用的是另一種配置方式:

# 文檔提供的配置方式
}
  server {
    listen 80;
    server_name www.domain.com; #填寫綁定證書的域名
    rewrite ^(.*)$ https://$host$1 permanent; #把http的域名請求轉成https
}
複製代碼

另外一種方式

這種方式實際上是利用了<meta>標籤中的的http-equiv屬性,與之對應的值是content,咱們須要新建一個index.html文件,複製並修改如下代碼:

<html>
    <!-- 自動刷新並指向新頁面,0 是指0秒後刷新(當即刷新) -->
    <meta http-equiv="refresh" content="0;url=https://www.shirmy.me/">
</html>
複製代碼

這樣當咱們訪問http://www.shirmy.me時就會從新刷新到https://www.shirmy.me,而後再修改nginx配置以下:

server {
    listen 80;  # 監聽默認端口
    server_name www.shirmy.me; # 域名
    location / {
        root www/http.shirmy.me/;    # 剛剛的 index.html 所在目錄
        index index.html index.htm;
    }
}
複製代碼

最後,重啓咱們的Nginx服務器:

cd /usr/local/nginx/sbin

# 平滑重啓
./nginx -s reload

# 非平滑重啓
./nginx -s stop && ./nginx
複製代碼

大功告成,配置了HTTPS的網站,要保證網站的連接都是安全的,包括API請求都必須使用HTTPS

Nginx反向代理

  • 咱們的網頁發起請求時,帶個端口豈不是很難看,好比https//api.shirmy.me:3000/v1/articles,如何去掉端口呢?
  • 又好比說咱們要訪問集羣服務器時,會先訪問一箇中間服務器,而後這個中間服務器再把你的請求分發到壓力小的服務器,這也須要經過反向代理來實現。
# 負載均衡就是靠下面這個來實現
# blogapi 替換成你喜歡的名字
upstream blogapi {
    server http://127.0.0.1:3000;
    # server 你也能夠選擇配置多個IP
}
server {
    # 同上面同樣的 HTTPS 配置
    listen 443 ssl;
    server_name api.shirmy.me;
    ssl_certificate ../cert/1_api.shirmy.me_bundle.crt;
    ssl_certificate_key ../cert/2_api.shirmy.me.key;
    ssl_session_timeout 5m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
    ssl_prefer_server_ciphers on;

    # 反向代理配置
    location / {
        # $host 表明轉發服務器
        proxy_set_header Host $host;
        proxy_redirect off;
        # 記錄真實IP
        proxy_set_header X-Real-IP $remote_addr;
        # 存儲請求鏈路上各代理IP
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        # 鏈接超時時間
        proxy_connect_timeout 60;
        # nginx接收upstream server數據超時時間
        proxy_read_timeout 600;
        # nginx發送數據至upstream server超時時間
        proxy_send_timeout 600;
        # 反向代理到上面定義好的 upstream blogapi 下的服務器上
        proxy_pass http://blogapi;
    }
}
複製代碼

如此一來,就實現了反向代理和負載均衡,此外,咱們應該讓用戶第一次訪問該服務器後,之後再訪問也是訪問該服務器,避免屢次創建http鏈接,那麼咱們能夠這樣修改:

upstream blogapi {
    # 避免每次被請求到多臺服務器上 知足用戶保持訪問同一臺服務器 又能實現負載均衡
    ip_hash;
    server http://127.0.0.1:3000;
    # server 你也能夠選擇配置多個服務器IP
}
複製代碼

最後記得重啓/usr/local/nginx/sbin/nginx -s reload

多個域名配置

除了主頁shirmy.me以外,咱們一般還要有一個管理後臺:admin.shirmy.me,由於用的是免費證書,因此咱們也只好爲子域名申請一個SSL證書,而且以一樣的方式配置。

咱們又總不能用端口shirmy.me:5000這樣子訪問吧,其實只要這樣作:

server {
    listen 80;
    # admin.shirmy.me
    server_name admin.shirmy.me;
    location / {
        # 直接看上面 HTTP 跳轉到 HTTPS 的配置
        root www/http.admin.shirmy.me/;
        index index.html index.htm;
    }
}
複製代碼

最後記得重啓/usr/local/nginx/sbin/nginx -s reload

相關文章
相關標籤/搜索