再一次折腾跨域问题

2022-03-30 20:07:42 浏览数 (1)

跨域问题在前后端分离的开发场景中经常遇到,回想起来自己也已经折腾了数次,本篇文章主要对跨域问题做个记录和总结。

跨域在前端中的报错一般为:

代码语言:javascript复制
Access to XMLHttpRequest at *** from origin *** has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the request resource.

一、为什么会出现跨域问题

任何一个系统都有着或多或少的规则和限制。跨域问题的来源便是浏览器的同源策略:

同源策略是指:协议相同、域名相同、端口相同。

为什么要有同源策略呢?

试想你登录一家电商网站,该网站肯定会在本地存储一些你的隐私信息,这个时候你又登录另一个网站,如果没有同源策略,该网站就可以直接读取到你在电商网站的隐私信息,这对用户来说是完全不可接受的。

也就说,只要违反了同源策略的任何一种,都是跨域。

现代浏览器给出了限制非同源的三种行为:

  • Cookie、LocalStorage 和 IndexedDB,非同源,不可读写。
  • 网页资源,比如DOM,非同源,不可接触。
  • 发送AJAX请求,非同源,浏览器拒绝响应

二、跨域解决方案

跨域的解决方案有很多种:

一 . 使用Ajax的jsonp

代码语言:javascript复制
$.ajax({
  url: "http://localhost:8090/crossorigin_test/getList",
  dataType: 'jsonp',
  success: function (data) {
}
})

JSONP 只支持get请求、不支持post请求

二. CORS解决跨域

跨源资源共享 (CORS)是一种基于 HTTP 头的机制,该机制通过允许服务器标示除了它自己以外的其它origin(域,协议和端口),这样浏览器可以访问加载这些资源。

CORS整个通信过程都由浏览器自动完成,CORS通信与同源的AJAX请求代码逻辑完全一样,只要服务器实现了CORS接口,浏览器就会自动携带一些附加的请求头信息,从而实现跨源通信,对用户而言是无感知的。

CORS需要浏览器和服务器的支持,CORS已经被现代浏览器广泛采用,因此服务器端的支持是关键。

我们重点看看后端使用Spring如何配置CORS跨域

1. Servlets方式手工设置响应头

创建跨域拦截器实现HandlerInterceptor接口,并实现其方法,在请求处理前设置头信息,并放行

代码语言:javascript复制
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
   // 设置:Access-Control-Allow-Origin头,处理Session问题
        response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("P3P", "CP=CAO PSA OUR");
        if (request.getHeader("Access-Control-Request-Method") != null && "OPTIONS".equals(request.getMethod())) {
            response.addHeader("Access-Control-Allow-Methods", "POST,GET,TRACE,OPTIONS");
            response.addHeader("Access-Control-Allow-Headers", "Content-Type,Origin,Accept");
            response.addHeader("Access-Control-Max-Age", "120");
        }
        // 放行
        return ture;
}

再在配置文件中配置拦截器

代码语言:javascript复制
<mvc:interceptos>
  <mvc:interceptor>
    <!--拦截所有-->
    <mvc:mapping path="/*/**"/>
    <bean class="com.datahear.CrossOriginInterceptor"></bean>
  </mvc:interceptor>
</mvc:interceptors>
2. 使用注解 @CrossOrigin

在类上加注解,表示类下所有方法都支持跨域请求

代码语言:javascript复制
@CrossOrigin
@RestController
@RequestMapping("users")
public class AaaController {

}

在方法上加注解,表示该方法支持跨域请求

代码语言:javascript复制
@RestController
@RequestMapping("users")
public class AaaController {

    @CrossOrigin
    @RequestMapping("/getUser")
    public Result getUser(HttpServletRequest request, HttpServletResponse response) throws Exception {
        ……
    }
}
3. 实现WebMvcConfigurer接口,重写addCorsMappings方法
代码语言:javascript复制
@Configuration
public class MvcConfig implements WebMvcConfigurer {

    /**
     * 解决跨域请求
     * @return
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
        .allowCredentials(true)
        .allowedOrigins("*")
        .allowedHeaders("*")
        .allowedMethods("*")
        .maxAge(3600);

        WebMvcConfigurer.super.addCorsMappings(registry);
    }
}
4. 使用CorsFilter过滤器
代码语言:javascript复制
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 {
    // 当前跨域请求最大有效时长。这里默认30天
    private long maxAge = 30 * 24 * 60 * 60;

    private CorsConfiguration buildConfig() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addAllowedOrigin("*"); // 1 设置访问源地址
        corsConfiguration.addAllowedHeader("*"); // 2 设置访问源请求头
        corsConfiguration.addAllowedMethod("*"); // 3 设置访问源请求方法
        corsConfiguration.setMaxAge(maxAge);
        corsConfiguration.setAllowCredentials(true);
        return corsConfiguration;
    }

    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", buildConfig()); // 4 对接口配置跨域设置
        return new CorsFilter(source);
    }
 }

三. Nginx反向代理

利用nginx反向代理把跨域问题转为不跨域,支持各种请求方式

直接看nginx配置:

代码语言:javascript复制
location ^~/wx
        {
            proxy_pass http://localhost:8082;
        }
代码语言:javascript复制
location /admin/ {
    proxy_set_header Host $http_host;
    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;
    proxy_pass http://localhost:8080/;
  }

0 人点赞