从前端到服务器全栈搭建我的博客

全栈开发—博客前端展现(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

相关文章
相关标签/搜索