1、前言
本篇文章使用的是vue2+vue-cli和vue3+vite组合的形式(vue3+vue-cli 3.0也可以,不过都用vue3了,为什么还要再用vue-cli,vite不香嘛),通过配置proxy解决跨域(开发环境可以生效,但是线上这种形式就不行了,需要使用Nginx进行反向代理,文章后面也会讲解)
1.1、什么是跨域?
应该不少小伙伴经常在开发中遇到一下问题吧,看到这串报错是不是瞬间头皮发麻
Access to XMLHttpRequest at '......' from origin '......' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
这就是前后端分离项目最常见的跨域问题,违反了浏览器的同源策略。
跨域是指一个域下的文档或脚本尝试去请求另一个域下的资源时,由于浏览器同源政策的限制,而不能直接进行的行为。这里的“域”指的是协议、域名(或ip地址)和端口号三者组成的组合。
同源政策(Same-origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少这个约定,浏览器很容易受到XSS、CSRF等攻击。所以,它限定了从一个源加载的文档或脚本如何与另一个源的资源进行交互。它能帮助阻止恶意文档,减少可能窃取数据的安全威胁。
具体来说,当协议、域名(或ip地址)和端口号中的一项不同,就会被视为跨域,例如:
http://www.example.com
与https://www.example.com
:协议不同,是跨域。http://www.example.com
与http://www.example.org
:域名不同,是跨域。http://www.example.com:8080
与http://www.example.com:8081
:端口号不同,是跨域。跨域资源共享(CORS)是一种机制,它使用额外的HTTP头来告诉浏览器,让运行在一个源上的Web应用被准许访问不同源上指定的资源。在某些情况下,例如API的调用,跨域请求是必要的,此时就需要正确地设置CORS策略来允许跨域请求。
2、跨域解决
2.1、vue2+vue-cli的形式
用到vue-cli脚手架,我们需要在vue.config.js这个配置文件进行配置proxy代理,如下:
module.exports = {
outputDir: path.resolve(__dirname, `./dist`),//打包后的dist存放在当前目录下
publicPath: './', //生产环境的项目前缀
productionSourceMap: false,
devServer: {
open: true, //启动项目是否自动弹出浏览器
port: 8080, //开发服务器的端口号
host: 'localhost', //开发服务器的主机名
proxy: {
'/api': {
target: 'http://localhost:8085', // 目标服务器地址,也就是后端服务地址
changeOrigin: true, // 是否改变源地址
pathRewrite: {
'^/api': '' // 重写路径
}
}
}
}
}
在axios创建时,加上baseUrl,这里需要判断一下是否为生产环境,如果为生产环境,将baseUrl改为api-prod,为的就是和开发环境区分开
let baseUrl = "/api";
//判断是否生成/开发环境
if(process.env.NODE_ENV === 'production'){
//是生产环境
baseUrl = '/api-prod';
}
const service = axios.create({
baseURL: baseUrl,
timeout: 200000 // 请求后期时间(毫秒)
});
在生产环境中,这样配置是不生效的,需要借助Nginx反向代理来解决跨域问题,在linux服务器安装Nginx后,先把前端项目打包dist后上传至nginx目录下,找到nginx.conf配置文件,进行编译
server{
listen 80;
location /{
root /usr/share/nginx/dist;
try_files $uri $uri/ /index.html;
index index.html index.htm;
}
location /api-prod/ {
proxy_pass http:localhost:8085/;
}
}
2.2、vue3+vite的形式
如果用vite脚手架进行开发,需要在vite.config.ts配置文件进行配置,语法与vue-cli有一点不一样
export default defineConfig({
root:process.cwd(), //项目根路径
base: '/', //生产环境服务的公共基础路径
publicDir: resolve(__dirname, './dist'),
build: {
outDir: 'dist', // 构建输出目录
assetsDir:'static',//指定静态资源存放路径
minify: true, // 是否压缩代码
sourcemap: true, // 是否生成 source map
},
plugins: [
vue(),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
},
server: {
port: 8085, //开发环境端口号
open: true, //是否自动弹出浏览器
proxy: {
'/api': {
target: 'http://localhost:8085', //需要代理到的目的服务器地址
changeOrigin: true, //是否改变源地址
rewrite: (path) => path.replace(/^\/api/, ''), //注意这里的语法和vue-cli不一样
}
}
}
})
在创建axios时,设置baseUrl
const BASE_URL = process.env.NODE_ENV === 'production' ? '/api-prod' : '/api'
const httpInstance = axios.create({
baseURL: BASE_URL, // 基地址
timeout: 5000 // 超时器
})
同样的,上面这些配置只适用于开发环境,如果生产环境的话,需要借助Nginx进行反向代理,
打包前端项目为dist,在nginx进行配置
server{
listen 80;
location /{
root /usr/share/nginx/dist;
try_files $uri $uri/ /index.html;
index index.html index.htm;
}
location /api-prod/ {
proxy_pass http:localhost:8085/;
}
}
3、后端如何解决跨域?
上面介绍了这么多,都是前端进行解决的,那么后端如何解决跨域问题呢?
进行CORS配置,有三种方式
3.1、@CrossOrigin注解局部跨域(方式一)
最简单也是最方便的方法就是在类上或者方法上加上@CrossOrigin注解进行局部跨域
@RestController
@RequestMapping("/user")
@CrossOrigin
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/login")
public Result<LoginVo> login(@RequestBody LoginDto loginDto) {
LoginVo loginVo = userService.login(loginDto);
return Result.success(loginVo,"登录成功!");
}
}
3.2、实现WebMvcConfigurer接口全局跨域(方式二)
但是这样有一个问题,如果控制层太多,那岂不是一个个的加,所有有一个全局配置的方式,就是实现WebMvcConfigurer接口,重写addCorsMappings方法
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class MyWebMvcConfig implements WebMvcConfigurer {
private static final String ORIGINS[] = new String[]{"GET", "POST", "PUT", "DELETE", "OPTIONS"};
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") // 所有的当前站点的请求地址,都支持跨域访问。
.allowedOriginPatterns("*") // 所有的外部域都可跨域访问。 如果是localhost则很难配置,因为在跨域请求的时候,外部域的解析可能是localhost、127.0.0.1、主机名
.allowCredentials(true) // 是否支持跨域用户凭证
.allowedMethods(ORIGINS) // 当前站点支持的跨域请求类型是什么
.maxAge(3600); // 超时时长设置为1小时。 时间单位是秒。
}
}
3.3、配置CorsFilter全局跨域(方式三)
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;
@Configuration
public class CorsConfig {
@Bean
public CorsFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
// 设置允许跨域请求的域名
config.addAllowedOrigin("*");
// 设置允许跨域请求的请求头
config.addAllowedHeader("*");
// 设置允许跨域请求的请求方法
// config.addAllowedMethod("*"); 放行所有请求方法
config.addAllowedMethod("GET");
config.addAllowedMethod("POST");
config.addAllowedMethod("PUT");
config.addAllowedMethod("DELETE");
// 是否携带cookies
// config.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
// 对所有路径进行跨域配置
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}
好了,文章到这里就结束了,相信大家看完这篇文章后,对跨域问题有一些新的见解,点赞关注支持一下哟~
版权归原作者 热爱编程的申同学 所有, 如有侵权,请联系我们删除。