Vue-SpringBoot-分离-部署

Vue-SpringBoot-分离-部署

说一下我的初衷

我用vue+springboot写了一个博客,打算长久编码使用。国内服务器一般按限带宽不限流量,我有一台1M带宽的服务器,这个情况加载Vue压缩的几兆大小的js文件是很难受的。就思量着把前端放在腾讯云的对象云存储的桶里(主要是想用coding平台的ci,你们可以选择任意的pages服务),把后端放在服务器上,会快很多。

预备

  1. 技术栈:vue+elementui+springboot+springsecurity+springdata
  2. 后台管理的页面是放在前端的,既然想省带宽就一不做二不休,可是想想如果做主题的话会比较麻烦,以后的事以后再讲吧。
  3. SpringBoot端口8080
  4. 在服务器上装了一个宝塔面板,管理网站很方便。
  5. 我博客的域名是blog.unwi.net,给后端分配一个次级域名be.blog.unwi.net,
  6. 文章中只提供重点代码,还有疑问可移步源码或者留言

跨域访问

本身学艺不精,还没搞懂怎么携带csrf token,直接disable了。后面再补充。

前端封装一个axios实例,重点是withCredentials: true

/**
 * 基础配置
 */
const baseConfig = {
  baseURL: process.env.VUE_APP_API_HOST,
  // 表示跨域请求时是否需要使用凭证
  withCredentials: true
}

后端配置一个bean即可。

    /**
     * spring security 会自动寻找这个Bean
     * @return
     */
    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        // 表示跨域请求时是否需要使用凭证
        configuration.setAllowCredentials(true);
        configuration.setAllowedOrigins(Arrays.asList(SecurityConstants.CROSS_ORIGINS));
        configuration.setAllowedMethods(Arrays.asList(SecurityConstants.CROSS_METHODS));
        configuration.setAllowedHeaders(Arrays.asList("*"));
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }

自动部署

这里就是用coding的ci完成提交代码自动部署前后端的任务,说实话coding的ci不如华为云的好使,但是华为的代码托管不好使,又长期依赖coding。做这类工具不少,慎重选择。

coding的项目功能中打开持续集成,打开里面的构建计划,新建一个后端的pipeline,停止springboot进程采用的是接收一个http请求关闭spring的context,详见SystemController.java

pipeline {
  agent any
  stages {
    stage('检出') {
      steps {
        checkout([
          $class: 'GitSCM',
          branches: [[name: GIT_BUILD_REF]],
          userRemoteConfigs: [[
            url: GIT_REPO_URL,
            credentialsId: CREDENTIALS_ID
          ]]])
        }
      }
      stage('编译') {
        steps {
          sh 'gradle clean'
          sh 'gradle bootJar'
        }
      }
      stage('部署和收集') {
        parallel {
          stage('部署') {
            steps {
              script {
                def remote = [:]
                remote.name = 'server'
                remote.user = 'root'
                remote.allowAnyHosts = true
                remote.host = env.HOST
                // 需要先创建一对 SSH 密钥,把私钥放在 CODING 凭据管理,把公钥放在服务器的 `.ssh/authorized_keys`,实现免密码登录
                withCredentials([sshUserPrivateKey(credentialsId: env.CREDENTIALS, keyFileVariable: 'id_rsa')]) {
                  remote.identityFile = id_rsa
                  // 备份
                  sshCommand remote: remote, command: 'tar -zcvf '+env.DEPLOY_PATH+'../ulog.bak.tar.gz '+env.DEPLOY_PATH
                  // 清理
                  sshCommand remote: remote, command: 'rm -f '+env.DEPLOY_PATH+'*'
                  // SSH 上传文件到远端服务器
                  sshPut remote: remote, from: './build/libs/ulog-'+env.VERSION+'.jar', into: env.DEPLOY_PATH
                  // 停止springboot
                  try {
                    sshCommand remote: remote, command: 'curl -X POST http://localhost:8080/system/shutdown'
                  } catch (err) {
                    echo '已忽略关闭springboot错误'
                  }
                  // 启动
                  sshCommand remote: remote, command: 'nohup java -jar '+env.DEPLOY_PATH+'ulog-'+env.VERSION+'.jar>'+env.DEPLOY_PATH+'output.log 2>'+env.DEPLOY_PATH+'error.log &'
                }
              }

              echo '部署完成'
            }
          }
          stage('收集') {
            steps {
              codingArtifactsGeneric(files: 'build/libs/*.jar', credentialsId: '${env.CODING_ARTIFACTS_CREDENTIALS_ID}', withBuildProps: true, repoName: 'releases')
            }
          }
        }
      }
    }
  }

再建一个前端的,这个可以直接选vue+cos的模板创建

pipeline {
  agent any
  stages {
    stage('检出') {
      steps {
        checkout([$class: 'GitSCM',
        branches: [[name: GIT_BUILD_REF]],
        userRemoteConfigs: [[
          url: GIT_REPO_URL,
          credentialsId: CREDENTIALS_ID
        ]]])
      }
    }
    stage('安装依赖') {
      steps {
        sh 'npm install'
      }
    }
    stage('编译') {
      steps {
        sh 'npm run build'
      }
    }
    stage('上传到 COS Bucket-收集') {
      parallel {
        stage('收集') {
          steps {
            codingArtifactsGeneric(files: 'dist/', credentialsId: '${env.CODING_ARTIFACTS_CREDENTIALS_ID}', withBuildProps: true, repoName: 'releases', zip: 'dist.zip')
          }
        }
        stage('上传到 COS Bucket') {
          steps {
            sh "coscmd config -a ${COS_SECRET_ID} -s ${COS_SECRET_KEY} -b ${COS_BUCKET_NAME} -r ${COS_BUCKET_REGION}"
            sh "coscmd upload -r ${COS_UPLOAD_FROM_PATH} /"
            echo "上传成功,访问 https://${COS_BUCKET_NAME}.cos-website.${COS_BUCKET_REGION}.myqcloud.com 预览效果"
            echo "您也可以访问原域名 https://${COS_BUCKET_NAME}.cos.${COS_BUCKET_REGION}.myqcloud.com/index.html 预览效果"
          }
        }
      }
    }
  }
}

反向代理

在宝塔面板新建一个后台be.blog.unwi.net的网站,配置反向代理到 localhost:8080

对于websocket,在反向代理里加两句

proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

网站配置

参考