element-ui+springboot文件上传时报403错误

问题

正如标题所述,在使用element-ui的文件上传组件向springboot后端上传文件时报403forbidden错误,而使用axios向后端发送请求时并未出现错误,且springboot后端并未使用spring security或设置拦截器。并且在后端直接使用html页面进行文件上传时并未出现错误,所以猜测是跨源(cross-origin)导致的问题。也就是说问题在于某个跨源请求被禁止了,所以解决问题的关键在于解决跨源问题。

image

如上图,浏览器的控制台具体的报错信息也印证了上面的猜测,确实是跨源请求导致的错误。

我的相关代码

Vue解决跨源请求的代码

在config/index.js中修改proxyTable为:

proxyTable: {
  '/api': {
    target: 'http://localhost:8080/',
      changeOrigin: true,
        pathRewrite: {
          '^/api': ''
        }
  }
}

在src/main.js中设置axios:

// 设置全局的axios
Vue.prototype.$axios = axios
// 设置基本路径
axios.defaults.baseURL = '/api'

前端上传文件代码

<template>
  <div>
    <div>
      <el-upload
        :before-remove="beforeRemove"
        :file-list="fileList"
        :limit="1"
        :on-exceed="handleExceed"
        :on-preview="handlePreview"
        :on-remove="handleRemove"
        action="http://localhost:8080/admin/upload/course"
        class="upload-demo"
        multiple
        name="file">
        <el-button size="small" type="primary">点击上传</el-button>
        <div slot="tip" class="el-upload__tip">只能上传excel文件</div>
      </el-upload>
    </div>
  </div>
</template>

<script>
export default {
  data () {
    return {
      fileList: []
    }
  },
  methods: {
    handleRemove (file, fileList) {
      console.log(file, fileList)
    },
    handlePreview (file) {
      console.log(file)
    },
    handleExceed (files, fileList) {
      this.$message.warning(`当前限制选择 1 个文件,本次选择了 ${files.length} 个文件,共选择了 ${files.length + fileList.length} 个文件`)
    },
    beforeRemove (file, fileList) {
      return this.$confirm(`确定移除 ${file.name}?`)
    }
  }
}
</script>

<style scoped>

</style>

后端接收代码

@RequestMapping("/admin/upload/course")
public boolean uploadCourse(@RequestParam("file") MultipartFile file) throws IOException {
    log.warn("{}", file);
    return true;
}

解决办法

方法一

既然是跨源问题,那么简单粗暴地直接粘贴以下代码解决跨源问题即可。

import org.springframework.boot.SpringBootConfiguration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@SpringBootConfiguration
public class WebMvcConfiguration implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOriginPatterns("*")
                .allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")
                .allowCredentials(true)
                .maxAge(3600)
                .allowedHeaders("*");
    }
}

简单说明一下上面的方法做了什么:
首先,addCorsMappings()方法用于配置“全局”跨源请求处理。然后,配置了什么呢?配置内容如下:

  1. .addMapping():该方法配置可以被跨源请求的路径。支持精确的路径映射URI(如“/admin”)以及Ant风格的路径模式(path patterns)(如“/admin/**”)。
  2. .allowedOriginPatterns():该方法是allowedOrigins()方法的替代方案,allowedOriginPatterns它支持更灵活的模式来指定允许从浏览器进行跨源请求的源(即配置允许请求的前端URL)。
  3. .allowedMethods():该方法用于设置允许用“以下方法”来请求访问跨域(cross-domain)资源。“以下方法”指的是HTTP方法,例如“GET”、“POST”等。而特殊值“*”指允许所有方法。默认情况下,允许使用“简单”方法GET、HEAD和POST。
  4. .allowCredentials():官方对于这个方法的注释是:(没看懂注释中“the annotated endpoint”具体指的是哪,求教)
Whether the browser should send credentials, such as cookies along with cross domain requests, to the annotated endpoint. The configured value is set on the Access-Control-Allow-Credentials response header of preflight requests.
NOTE: Be aware that this option establishes a high level of trust with the configured domains and also increases the surface attack of the web application by exposing sensitive user-specific information such as cookies and CSRF tokens.
By default this is not set in which case the Access-Control-Allow-Credentials header is also not set and credentials are therefore not allowed.
  1. .maxAge():该方法可以配置客户端可以缓存预检请求(pre-flight request)的响应的时间(单位是秒)。默认设置为1800秒(30分钟)。
  2. .allowedHeaders():该方法可以设置允许指定的请求header访问,可以自定义设置任意请求头信息,如:"X-YAUTH-TOKEN"。特殊值“*”可用于允许所有header。默认情况下,允许使用所有header。

方法二

既然我已经在Vue中使用proxyTable来解决了跨源问题(见上文“Vue解决跨源请求的代码”),那为什么依然会出现跨源问题呢?
分析代码发现,在element-ui的上传组件中是通过一个“action”属性发送HTTP请求的,而我的action属性是这么写的:action="http://localhost:8080/admin/upload/course"(注:前端为localhost:8081,后端为localhost:8080)。这样写显然不对,直接请求后端就不会使用webpack为我们提供的http代理服务器(即proxyTable的配置),就必然会出现跨源问题。
综上分析,只需把action属性改为:action="/api/admin/upload/course"。其中,/api是我在Vue中配置的“baseURL”;/admin/upload/course是后端的@RequestMapping。(见上文“我的相关代码”)
问题解决!

若文中有错漏,欢迎评论指出。

posted @ 2022-06-18 15:46  水煮酸菜鱼  阅读(1693)  评论(0编辑  收藏  举报