前后端分离的跨域问题

跨域问题原因

  在现在流行的前后端分离开发中,跨域问题突显了出来,跨域问题的根本原因:浏览器有同源策略限制,当前域名的js只能读取同域下的窗口属性,这是一个基础安全功能。那么什么是同源策略呢?即两资源的URL中 协议域名端口,都相同则称为同源,若两资源为不同源,则不允许共享资源。
例如:以http://www.baidu.com/dir1/a.html为例子

请求地址结果原因
http://www.baidu.com/dir1/b.html成功同一域名端口,相同文件夹
http://www.baidu.com/dir2/a.html成功同一域名端口,不同文件夹
https://www.baidu.com/dir1/b.html失败不同协议,http与https
http://www.baidu.com:8000/dir1/b.html失败不同端口,默认为80
http://www.sina.com/dir1/b.html失败不同域名
注: 核心判断方式为,端口及端口左边的地址要完全一致
CORS

  CORSCross-Origin Resource Sharing的缩写。也就是支持跨域资源共享,即不同域之间可以进行资源的相互访问。那么为什么要有CORS呢,其实很简单,浏览器的同源政策保证了安全的同时,也让我们不能访问其它域的资源,即使开发者认为的安全操作也不行,这时,我们就可以用一套协议来告诉浏览器,这某几个域在我们的服务范围之内,不要一棍打死。即有如下改变:
CORS出现之前:
  浏览器接收到服务器回复 --> 检查是否同源 --> 不同源就拒绝响应!/同源就响应!
CORS出现之后:
  浏览器接收到服务器回复 --> 检查一下response header --> 发现有特定字段Access-Control-Allow-Origin --> 检查这个header后面的值包不包含自己所在的域(打比方我们现在在www.baidu.com),浏览器检查发现后response header里面写的有Access-Control-Allow-Origin: https://www.baidu.com, https://www.google.com, 正好我们在允许的域里面! --> 浏览器响应服务器返回的数据(response)。

CORS 实现逻辑

  CORS需要浏览器和服务器也就是B/S两端分别实现!

浏览器要实现的内容:

  接收到服务器的回复之后检测回复头里面是否含有特定字段!有的话查看自己的域名是否在内,一定要这两个条件都符合!浏览器才会接受(或者说响应)服务器返回的回复!

服务器需要实现的内容:

  服务器需要在接受到浏览器发来的请求(request)后,在返回response之前!设置header,在header中里面包含特定字段,以便浏览器收到回复之后可以去检查,有字段证明服务器同意服务这个网站,浏览器就可以接受回复,没有字段或者字段的值不包含发送请求的网站,浏览器就不会响应回复内容!

注意:

  如果是复杂的跨域请求,浏览器则会先发送一个OPSTIONS探路,收到响应允许跨域后会再发送真实请求。
在这里插入图片描述
注:此时服务端写代码时,还需响应(OPTIONS)允许跨域。

跨域问题的解决方案

  • 修改浏览器的设置 (不推荐,不安全)
  • 修改请求的方式:jsonp (不推荐,诸多限制)
  • 用 Nignx 统一访问域
  • CORS 前端后端代码实现

CORS后端代码实现

  Nignx和前端的解决方案自行查找下吧,这里介绍下后端的常见解决方案

第一种方法:

在controller上加上@CrossOrigin 注解或者@CrossOrigin(origins = “http://localhost:8081”),优点是粒度更细,某URL是否跨域可以分开配置,缺点是,如果整个项目都要跨域,则每个controller都需要添加注解

@RestController
@RequestMapping(path = "/user")
@CrossOrigin  // 解决跨域问题
public class UserController {
		//......
}
第二种方法:

创建配置类CorsConfig,此方法统一配置,用的较多

@Configuration
public class CorsConfig implements WebMvcConfigurer {

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

第三种方法:

创建一个过滤器CorsFilter

@WebFilter(filterName = "CorsFilter ")
@Configuration
public class CorsFilter implements Filter {
    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        
        HttpServletResponse response = (HttpServletResponse) res;
        response.setHeader("Access-Control-Allow-Origin","*");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, PATCH, DELETE, PUT");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
        
        HttpServletRequest httpRequest = (HttpServletRequest) req;
		// OPTIONS method response
		if (httpRequest.getMethod().equals("OPTIONS"))
			((HttpServletResponse) res).sendError(HttpServletResponse.SC_OK);
        
        chain.doFilter(req, res);
    }
}

第四种方法:

网关配置跨域(用于微服务)

@Configuration
public class CorsConfiguration {

    @Bean
    public CorsWebFilter corsWebFilter(){
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();

        CorsConfiguration corsConfiguration = new CorsConfiguration();

        //1、配置跨域
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.addAllowedMethod("*");
        corsConfiguration.addAllowedOrigin("*");
        // 是否允许携带cookie跨域
        corsConfiguration.setAllowCredentials(true);

        source.registerCorsConfiguration("/**",corsConfiguration);
        return new CorsWebFilter(source);
    }
}

扩展阅读

https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS

  • 24
    点赞
  • 92
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小夏陌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值