SpringBoot+Vue前后端跨域问题

随着分布式微服务的兴起,越来越多的公司在开发web项目的时候选择前后端分离的模式开发,前后端分开部署,使得分工更加明确,彻底解放了前端。

我们知道,http请求都是无状态,现在比较流行的都是jwt的形式处理无状态的请求,在请求头上带上认证参数(token等),前后端分离有好处,也有坏处,第一次开发前后端分离项目的人,肯定会遇到前端请求跨域的问题,这个怎么处理呢?在说处理方案前,有必要说明一下为什么会跨域和什么是跨域?

一、为什么会跨域?

出于浏览器的同源策略限制。同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。同源策略会阻止一个域的 javascript 脚本和另外一个域的内容进行交互。所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port)

二、什么是跨域?

当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域

当前页面url(即浏览器地址) 被请求页面url(即后端接口地址) 是否跨域 原因
https://www.site.com/ https://www.site.com/index.html 同源(协议、域名、端口号相同)
https://www.site.com/ http://www.site.com/index.html 协议不同(https/http)
https://www.site.com/ https://www.baidu.com/ 主域名不同(site/baidu)
https://www.site.com/ https://layui.site.com/ 子域名不同(www/layui)
https://www.site.com:8080/ https://www.site.com:8081/ 端口不同(8080/8081)

三、实现跨域访问

两种方面来解决

  • 前端解决(不是根本解决方案,打包后还是得由后端解决)
  • 后端解决
  • 代理服务器

3.1 前端代理访问

在vue的开发环境中跨域,通过代理的方式来实现,类似于nginx。

比如以下范例,访问 csdn 的测试接口api。

// config/index.js

module.exports = {
  dev: {
    // Paths
    assetsSubDirectory: 'static',
    assetsPublicPath: '/',
    proxyTable: {
      '/api': {
        // 真实接口域名
        target: 'https://www.csdn.net',
        // 开启代理
        changeOrigin: true,
        pathRewrite: {
          // 重写请求地址,即将字符串^/api替换成你想要的字符串,拼接到target
          '^/api': '/api'
        }
      },
      '/v1': {
        target: 'https://suggest-follow-api-ms.juejin.im',
        changeOrigin: true,
        pathRewrite: {
          '^/v1': '/v1'
        }
      }
    },
  },

}

代码中的请求方式

method: {
  ajaxFun() {
    var url = '/api/articles?type=more&category=home&shown_offset=1524276761019196&first_view=false';
    this.$axios.get(url)
      .then(res => {
        this.articles = res.data.articles;
        console.log(res);
      })
  }
}

通过 npm run dev 之后,访问完这个接口,在控制台中显示的地址是 http://localhost:8080/api/articles?type=more&category=home&shown_offset=1524276761019196&first_view=false
相当于 https://www.csdn.net/api/articles?type=more&category=home&shown_offset=1524276761019196&first_view=false

这就实现了开发环境下的跨域请求。但我们打包上线:用 npm run build 打包成dist文件。发到线上后,还是得再次解决跨域。

3.2 后端CORS实现跨域访问

授权方式

  • 方式1:注册新的CorsFilter
  • 方式2:重写WebMvcConfigurer
  • 方式3:使用注解(@CrossOrigin)
  • 方式4:手工设置响应头(HttpServletResponse )

注:CorsFilter / WebMvcConfigurer / @CrossOrigin 需要SpringMVC 4.2 以上的版本才支持,对应SpringBoot 1.3 版本以上都支持这些CORS特性。不过,使用SpringMVC4.2 以下版本的小伙伴也不用慌,直接使用方式4通过手工添加响应头来授权CORS跨域访问也是可以的。附:在SpringBoot 1.2.8 + SpringMVC 4.1.9 亲测成功。

注:方式1和方式2属于全局CORS配置,方式3和方式4属于局部CORS配置。如果使用了局部跨域是会覆盖全局跨域的规则,所以可以通过@CrossOrigin注解来进行细粒度更高的跨域资源控制。

3.2.1 注册CorsFilter(全局跨域)

Spring框架还提供了一个 CorsFilter 。在这种情况下,您可以按如下方式在 SpringBoot 应用程序中声明过滤器。

在任意配置类,返回一个新的CorsFilter Bean,并添加映射路径和具体的CORS配置信息。

@Configuration
public class GlobalCorsConfig {
    @Bean
    public CorsFilter corsFilter() {
        //1.添加CORS配置信息
        CorsConfiguration config = new CorsConfiguration();
          //放行哪些原始域
          config.addAllowedOrigin("*");
          //是否发送Cookie信息
          config.setAllowCredentials(true);
          //放行哪些原始域(请求方式)
          config.addAllowedMethod("*");
          //放行哪些原始域(头部信息)
          config.addAllowedHeader("*");
          //暴露哪些头部信息(因为跨域访问默认不能获取全部头部信息)
          config.addExposedHeader("*");

        //2.添加映射路径
        UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();
        configSource.registerCorsConfiguration("/**", config);

        //3.返回新的CorsFilter.
        return new CorsFilter(configSource);
    }
}
3.2.2 重写WebMvcConfigurer(全局跨域)

类似于使用过滤器,可以使用Spring MVC声明,并与细粒度配置结合使用。缺省情况下,允许所有源和方法。

在任意配置类,返回一个新的WebMvcConfigurer Bean,并重写其提供的跨域请求处理的接口,目的是添加映射路径和具体的CORS配置信息。

为整个应用程序启用 CORS 非常简单,例如:

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

	@Override
	public void addCorsMappings(CorsRegistry registry) {
		registry.addMapping("/**");
	}
}

如果您使用的是 Spring Boot,建议只声明 Bean 如下:WebMvcConfigurer

@Configuration
public class GlobalCorsConfig {
    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            //重写父类提供的跨域请求处理的接口
            public void addCorsMappings(CorsRegistry registry) {
                //添加映射路径
                registry.addMapping("/**");
            }
        };
    }
}

您可以轻松更改任何属性,也可以仅将此 CORS 配置应用于特定的路径模式:

@Override
//重写父类提供的跨域请求处理的接口
public void addCorsMappings(CorsRegistry registry) {
    //添加映射路径
    registry.addMapping("/**")
            //放行哪些原始域
            //.allowedOrigins("*")
            .allowedOrigins("http://domain2.com")
            //是否发送Cookie信息
            .allowCredentials(true)
            //放行哪些原始域(请求方式)
            .allowedMethods("GET","POST", "PUT", "DELETE")
            //放行哪些原始域(头部信息)
            .allowedHeaders("*")
            //暴露哪些头部信息(因为跨域访问默认不能获取全部头部信息)
            .exposedHeaders("Header1", "Header2")
            .maxAge(3600);;
}
3.2.3 使用注解@CrossOrigin(局部跨域)

在方法上(@RequestMapping)使用注解 @CrossOrigin

@RestController
@RequestMapping("/account")
public class AccountController {    
	@RequestMapping("/hello")
    @CrossOrigin("http://localhost:8080") 
    public String index( ){
        return "Hello World";
    }
}

或者在控制器(@RestController)上使用注解 @CrossOrigin

@RestController
@RequestMapping("/account")
@CrossOrigin(origins = "http://xx-domain.com", maxAge = 3600)
public class AccountController {

    @RequestMapping("/hello")
    public String index( ){
        return "Hello World";
    }
}
3.2.4 手工设置响应头(局部跨域 )

使用 HttpServletResponse 对象添加响应头(Access-Control-Allow-Origin)来授权原始域,这里 Origin 的值也可以设置为"*" ,表示全部放行。

@RestController
@RequestMapping("/account")
public class AccountController {    
	@RequestMapping("/hello")
    @ResponseBody
    public String index(HttpServletResponse response){
        response.addHeader("Access-Control-Allow-Origin", "http://localhost:8080");
        return "Hello World";
    }
}

3.4 通过nginx 代理访问

设置nginx配置文件:

location / {
    root   /dist/;
    index  index.html;
}

location /api/ {
    proxy_pass http://xxx.xxx.xxx.xxx:8090/;
    client_max_body_size 100m; 
    client_body_buffer_size 512k;
    proxy_send_timeout 300;
    proxy_read_timeout 300;
    proxy_connect_timeout 300;			
    proxy_buffer_size 64k;
    proxy_buffers 16 64k; 
    proxy_busy_buffers_size 64k;
    proxy_temp_file_write_size 64k;			
    proxy_set_header Host $host:$server_port;  
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header REMOTE-HOST $remote_addr;			
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 
    add_header Access-Control-Allow-Origin *;
    add_header Access-Control-Allow-Headers "Origin, X-Requested-With, Content-Type, Accept";
    add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
}

四、源码和文档

源码地址:SpringBoot-Cross-Orgin

专题阅读:《SpringBoot 布道系列》

官方文档:W3C规范-CORS

Spring传统文档:SpringMVC-CORS 使用手册

推荐阅读:跨域资源共享 CORS 详解 - 阮一峰

扩展

参考博客:SpringBoot 实现前后端分离的跨域访问(CORS)

你可能感兴趣的:(SpringBoot,Vue,spring,boot,vue)