Springboot+Vue在开发和部署相关问题解决

一、可能遇到的问题:

  1. 跨域
  2. 路由使用hash或者history
  3. 打包时的配置(history)的情况
  4. 部署问题
  5. 部署之后出现跨域
  6. 刷新之后404
  7. Nginx如何进行端口代理

接下来通过我采坑的经历,进行一一详述!

二、跨域问题

针对跨域我们需要对SpringbootVue两部分进行处理。

2.1. Sprigboot对于跨域的处理

Springboot这个方面可能有很多中方法:

  • 我们可以添加一个拦截器设置请求的header头进行处理
  • 使用@CrossOrigin对某一个接口进行跨域设置
  • 使用手工设置HttpServletResponse对象添加响应头

下面的是我针对Springboot的跨域的处理

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

/**
 * @author 墨龙吟
 * @version 1.0.0
 * @ClassName CorsConfig.java
 * @Description 跨域配置
 * @createTime 2019年10月25日 - 15:58
 */
@Configuration
public class CorsConfig {

    @Bean
    public FilterRegistrationBean corsFilter() {
        CorsConfiguration config = new CorsConfiguration();
        // 放行哪些原始域
        config.addAllowedOrigin("*");
        // 是否发送cookie信息
        config.setAllowCredentials(true);
        // 放行的请求方式
        config.addAllowedMethod("*");
        // 放行的头部信息
        config.addAllowedHeader("*");
        // 添加映射路径
        UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();
        configSource.registerCorsConfiguration("/**", config);
        // 将新的CorsFilter注册到FilterRegistrationBean中
        FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(configSource));
        // 设置优先级
        bean.setOrder(0);
        return bean;
    }

}

这样我们的关于后端的跨域基本上就完成了。

2.2. Vue端的跨域设置

在我们开发的时候上面针对Springboot的跨域配置。我们已经可以开发了,但是在我们要部署到服务器上的时候还是出现跨域问题,所以我这里直接就弄好就可以了。

2.2.1. 首先是对vue.config.js文件配置
// vue.config.js
const path = require('path');

function resolve(dir) {
    return path.join(__dirname, dir)
}

// 项目的主要配置文件
module.exports = {
    // 开发测试的是时候使用 publicPath: '/' ; 打包部署的时候  使用 '/blog/'
    // publicPath: '/blog/',
    publicPath: '/',
    outputDir: 'dist',
    assetsDir: 'static'
    devServer: {
        port: 8080,
        // 是上线所需的额外配置,本地测试的需要需要注释掉
        // publicPath: '/',
        proxy: {// 配置跨域
            '/api': {
                // 这里后台的地址模拟的;应该填写你们真实的后台接口  
                target: 'http://localhost:9999/api',
                ws: true,
                // 允许跨域
                changOrigin: true,
                pathRewrite: {
                    // 请求的时候使用这个api就可以
                    '^/api': ''
                }
            }
        }
    }
}
2.2.2. 修改路由文件
import Vue from 'vue'
import Router from 'vue-router'
import Cookies from 'js-cookie'

Vue.use(Router)

let router = new Router({
    // 切换路由为history模型
    mode: 'history',
    // 基础路径,/blog/ 这里和vue.config.js里面publicPath相对应。
    base: '/blog/',
    routes: [
        {
            // ...
        }
    ]
});


export default router
2.2.3. 配置axios中的baseURL

一般都是要写一个关于axiosgetpost分装的工具类,我们修改axios的默认配置信息

axios.defaults.baseURL = 'http://localhost:9999/api';  
axios.defaults.withCredentials = true;

三、路由模型的选择和刷新404问题

3.1. 两种路由的区别

路由使用hash或者history,这两中区别如下:

  • hash —— 即地址栏 URL 中的 # 符号(此 hash 不是密码学里的散列运算)。
    比如这个 URL:http://www.abc.com/#/hello,hash 的值为 #/hello。它的特点在于:hash 虽然出现在 URL 中,但不会被包括在 HTTP 请求中,对后端完全没有影响,因此改变 hash 不会重新加载页面。
  • history —— 利用了 HTML5 History Interface中新增的 pushState()replaceState() 方法。(需要特定浏览器支持)这两个方法应用于浏览器的历史记录栈,在当前已有的 backforwardgo 的基础之上,它们提供了对历史记录进行修改的功能。只是当它们执行修改时,虽然改变了当前的 URL,但浏览器不会立即向后端发送请求。
3.2. 两种路由的优缺点:
  • Hash路由:

    优点

    1. 实现简单,兼容性好(兼容到ie8
    2. 绝大多数前端框架均提供了给予hash的路由实现
    3. 不需要服务器端进行任何设置和开发
    4. 除了资源加载和ajax请求以外,不会发起其他请求

    缺点

    1. 对于部分需要重定向的操作,后端无法获取hash部分内容,导致后台无法取得url中的数据,典型的例子就是微信公众号的oauth验证
    2. 服务器端无法准确跟踪前端路由信息
    3. 对于需要锚点功能的需求会与目前路由机制冲突
  • History路由

    优点

    1. 对于重定向过程中不会丢失url中的参数。后端可以拿到这部分数据
    2. 绝大多数前段框架均提供了browser的路由实现
    3. 后端可以准确跟踪路由信息
    4. 可以使用history.state来获取当前url对应的状态信息

    缺点

    1. 兼容性不如hash路由(只兼容到IE10)
    2. 需要后端支持,每次返回html文档
3.3. 使用History部署的时候刷新404问题解决:

因此可以说,hash模式和history模式都属于浏览器自身的特性,Vue-Router只是利用了这两个特性(通过调用浏览器提供的接口)来实现前端路由。

我们使用history的时候是没有#虽然美观,但是会有问题 **不怕前进,不怕后退,就怕刷新,F5,(如果后端没有准备的话),因为刷新是实实在在地去请求服务器的。**这里就需要我们在服务器里面配置一下

我们使用Springboot项目所以正常都会安装Tomcat服务器,当我们把Vue项目打包之后放到webapps

这里的项目目录blog也是和我们上面配置的相对应的。

在我们的项目里面添加一个WEB-INF目录。里面添加web.xml,内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
       version="3.1" metadata-complete="true">
<display-name>Router for Tomcat</display-name>
<error-page>
  <error-code>404</error-code>
  <location>/index.html</location>
</error-page>
</web-app>

这样子,我们再次刷新之后就不会出现404的问题了。

四、Nginx代理的问题

我们先从安装开始!

4.1. Centos8安装Nginx

下载Nginx地址: http://nginx.org/en/download.html,下载最新的版本1.17.9

一般来说我们自己安装软件的话,一般都放到/opt目录里面。将安装包拷贝到/opt目录之后创建一个nginx的目录,执行解压。

# 创建nginx目录 解压安装包  然后进入解压后的目录
mkdir nginx && tar -zxvf nginx-1.17.9.tar.gz && cd nginx-1.17.9

接着安装Nginx所需要的一些依赖

yum install -y pcre pcre-devel zlib zlib-devel openssl openssl-devel

然后就进行配置、编译和安装了

# /opt/nginx 就是安装路径,这个看你是安装到哪个目录自己配置吧
./configure --prefix=/opt/nginx --with-http_stub_status_module --with-http_ssl_module && make && make install

最后还需要将nginx添加到服务里面,方便我们去通过systemctl启动和停止。

对于systemctl相关的serivce文件一般都是在/etc/systemd/system/目录里面的,但是,里面存放的大部分文件都是符号链接,指向目录/usr/lib/systemd/system/,真正的配置文件存放在/usr/lib/systemd/system/目录的。所以哪个目录都可以,看自己高兴吧!

创建一个nginx.service文件,里面的内容如下:

[Unit]
Description=Nginx - high performance web server
After=network.target remote-fs.target nss-lookup.target

[Service]
Type=forking
ExecStart=/opt/nginx/sbin/nginx -c /opt/nginx/conf/nginx.conf
ExecReload=/opt/nginx/sbin/nginx -c /opt/nginx/conf/nginx.conf -s reload
ExecStop=/opt/nginx/sbin/nginx -s stop
PrivateTmp=true

[Install]
WantedBy=multi-user.target

其他的地方都不需要配置,只需要将 /opt/nginx/sbin/nginx的执行文件的位置和/opt/nginx/conf/nginx.conf配置文件的位置替换成你自己的安装位置就可以了!

最后让服务生效,启动nginx

# 刷新systemctl
systemctl daemon-reload
# 将nginx添加到开机自启
systemctl enable nginx
# 将nginx启动
systemctl start nginx
# 将nginx停止。每次修改conf文件都是重启nginx
systemctl stop nginx
4.2. 配置Nginx

这里的配置相对来说,就是对nginx,日后将会针对对个项目配置,那样的话都写在conf文件会很乱,也不方便管理,我们可以将每个项目都创建一个conf文件,然后加载入nginx中。

首先需要在nginx中添加一行配置:

include       /opt/nginx/webapps/*.conf;

添加的位置:

这样就会将/opt/nginx/webapps/下所有conf后缀的文件都加载在nginx启动的时候!

4.3. 配置端口转发

接着回归我们之前的问题,部署之后我们访问是需要带着端口号的,所有我们可以使用Nginx做端口转发。我们在/opt/nginx/webapps/下添加一个blog.conf文件,具体配置如下:

server {
	# 监听80端口
	listen       80;
	server_name  localhost;
	
	location / {
		root /opt/tomcat9/webapps/blog/;
		index index.html;
		try_files $uri $uri/ @router;
		index index.html;
	}	
	location @router {
		rewrite ^.*$ /index.html last;
	}

	# 我们在上面vue上配置的项目名现在还有用处。通过/blog就可以转发到我们要的端口
	location /blog {
		# root             /opt/tomcat9/webapps;
		# try_files        $uri $uri/ /blog/index.html;
		proxy_pass       http://服务器IP地址:8080/blog;
	}

}

五、Tomcat安装配置

最后我在说一下TomcatCentos下的安装配置,其实安装很简单,就是下载安装包,解压就可以启动了,重点是要添加systemclt系统服务中,还是在我们4.1中所说,在/usr/lib/systemd/system/或者/etc/systemd/system/中添加一个tomcat.service的文件。

[Unit]
Deascription=Tomcat 9 servlet container
After=network.target

[Service]
Type=forking

# 这里添加你Centos中jdk的jre目录位置
Environment="JAVA_HOME=/opt/java8/jre"
Environment="JAVA_OPTS=-Djava.security.egd=file:///dev/urandom"

Environment="CATALINA_BASE=/opt/tomcat9/"
Environment="CATALINA_HOME=/opt/tomcat9"
# 配置一下运行的内存大小
Environment="CATALINA_OPTS=-Xms256M -Xmx512M -server -XX:+UseParallelGC"

# 启动和停止的命令
ExecStart=/opt/tomcat9/bin/startup.sh
ExecStop=/opt/tomcat9/bin/shutdown.sh

[Install]
WantedBy=multi-user.target

最后还是和上面一下执行:

# 刷新systemctl
systemctl daemon-reload
# 将tomcat添加到开机自启
systemctl enable tomcat
# 将tomcat启动
systemctl start tomcat
# 将tomcat停止
systemctl stop tomcat

六、产生跨域的原因

在上面我们一直在寻找跨域的解决问题,但是为什么会产生跨域呢,我们需要了解下,做到知其然而知其所以然!

6.1. 同域策略

当我们请求的时候会出现这样的问题:

这个就是浏览器的同源策略的有关系了!官方解释很官方,我用大白话解释一下吧,简单来说:当前发起请求的域与该请求指向的资源所在的域不一样。这里的域指的是这样的一个概念:我们认为若协议 + 域名 + 端口号均相同,那么就是同域,不是同域的就是跨域了。

我们上面接口的地址是:localhost:9999但是vue的地址是localhost:8080这样就不符合协议 + 域名 + 端口号均相同,这样就不在同一个域里面就是出现跨域的问题了。

我们经常出现的问题是因为同域策略中一条:CookieXMLHttprequest层面的同源策略:禁止 Ajax 直接发起跨域HTTP请求(其实可以发送请求,结果被浏览器拦截,不展示),同时Ajax请求不能携带与本网站不同源的 Cookie

6.2. 跨域会出现的安全问题

浏览器会对上面提到的跨域请求作出限制。浏览器之所以要对跨域请求作出限制,是出于安全方面的考虑,因为跨域请求有可能被不法分子利用来发动 CSRF攻击。

通俗来说CSRF攻击就是攻击者盗用了你的身份,以你的名义发送而已请求。

CSRF能够做的事情包括:以你名义发送邮件,发消息,盗取你的账号等等,如果你的个人隐私泄露那么财产安全就更危险了。

6.2. Jsonpcors

在安全上来说同源限制是必要的,但有时同源策略会对我们开发造成影响,为了避免开发的限制,有多种方式可以绕开同源策略,下面介绍的是经常使用的 Jsonp, cors 方法。

6.2.1. Jsonp方法

Jsonp是一种非官方的跨域数据交互协议,只支持get方法,限制很多,但是可以再一些很古老的浏览器中使用,现在使用的也很少了,我们就不做过多的深究了。

6.2.2. cors方法

CORS简称:跨源资源共享 Cross-Origin Resource Sharing 是一个新的 W3C标准,它新增的一组HTTP首部字段,允许服务端其声明哪些源站有权限访问哪些资源。简单来说,它允许浏览器向声明了CORS 的跨域服务器,发出 XMLHttpReuest 请求,从而克服 Ajax 只能同源使用的限制。

我们在使用的时候,浏览器必须首先使用 optioin方法发起一个预检请求,从而获知服务端是否允许该跨域请求,在服务器确定允许后,才发起实际的HTTP请求。

下面我们了解下新增的header头信息:

  • Access-Control-Allow-Origin:响应首部中可以携带这个头部表示服务器允许哪些域可以访问该资源。
  • Access-Control-Allow-Methods:该首部字段用于预检请求的响应,指明实际请求所允许使用的HTTP方法。
  • Access-Control-Allow-Headers:该首部字段用于预检请求的响应。指明了实际请求中允许携带的首部字段。
  • Access-Control-Max-Age:该首部字段用于预检请求的响应,指定了预检请求能够被缓存多久。
  • Access-Control-Allow-Credentials:该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。设为true,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。
  • Origin:该首部字段表明预检请求或实际请求的源站。不管是否为跨域请求,Origin字段总是被发送。

我们在Springboot上面的CORS过滤器配置,也是在设置这些头:

好了!就这样了,在项目部署的时候遇到的坑就这些了。

欢迎关注我的公众号!

在这里插入图片描述

Logo

前往低代码交流专区

更多推荐