Skip to content

跨域的处理

产生原因

跨域产生的原因是由于前端地址与后台接口不是同源,从而导致 ajax 不能发送

非同源产生的问题

  1. Cookie、LocalStorage 和 IndexDB 无法获取

  2. DOM 无法获得

  3. AJAX 请求不能发送

同源条件

协议端口主机 三者相同即为同源。反之,其中只要 某一个 不一样则为不同源

解决方式

本地开发跨域

本地开发一般使用下面 3 种方式进行处理

  1. vite 的 proxy 进行代理
  2. 后台开启 cors
  3. 使用 nginx 转发请求

生产环境跨域

生产环境一般使用下面 2 种方式进行处理

  1. 后台开启 cors
  2. 使用 nginx 转发请求

后台开启 cors 不需要前端做任何改动

vite 的 proxy 进行代理

如果你在 src/api/ 下面的接口为下方代码,且 .env.development 文件配置如下注释,则在控制台看到的地址为 http://localhost:3100/api/oauth/anyTenant/login

由于 /api 匹配到了设置的 VITE_PROXY,所以上方实际是请求 **http://localhost:18760/api/oauth/anyTenant/login**,这样同时也解决了跨域问题。(**3100**为前端项目acuity-web-pro的端口号,**[http://localhost:18760](http://localhost:18760/)**为PROXY代理的目标后端acuity-gateway-server的地址)

// .env.development
// VITE_PROXY=[["/api","http://localhost:18760"]]
// VITE_GLOB_API_URL=/api

enum Api {
  Login = '/oauth/anyTenant/login',
}

/**
 * @description: 用户登陆
 */
export function loginApi(params: LoginParams) {
  return http.request<LoginResultModel>({
    url: Api.Login,
    method: 'POST',
    params,
  });
}
// .env.development
// VITE_PROXY=[["/api","http://localhost:18760"]]
// VITE_GLOB_API_URL=/api

enum Api {
  Login = '/oauth/anyTenant/login',
}

/**
 * @description: 用户登陆
 */
export function loginApi(params: LoginParams) {
  return http.request<LoginResultModel>({
    url: Api.Login,
    method: 'POST',
    params,
  });
}

#没有跨域时的配置

如果没有跨域问题,可以直接忽略 VITE_PROXY 配置,直接将接口地址设置在 VITE_GLOB_API_URL

# 例如接口地址为 http://localhost:18760 则
VITE_GLOB_API_URL=http://localhost:18760
# 例如接口地址为 http://localhost:18760 则
VITE_GLOB_API_URL=http://localhost:18760

如果有跨域问题,将 VITE_GLOB_API_URL 设置为跟 VITE_PROXY 内其中一个数组的第一个项一致的值即可。

下方的接口地址设置为 /api,当请求发出的时候会经过 Vite 的 proxy 代理,匹配到了我们设置的 VITE_PROXY 规则,将 /api 转化为 http://localhost:18760 进行请求

# 例如接口地址为 http://localhost:18760 则
VITE_PROXY=[["/api","http://localhost:18760"]]
# 接口地址
VITE_GLOB_API_URL=/api
# 例如接口地址为 http://localhost:18760 则
VITE_PROXY=[["/api","http://localhost:18760"]]
# 接口地址
VITE_GLOB_API_URL=/api

#跨域原理解析

vite.config.ts 配置文件中,提供了 server 的 proxy 功能,用于代理 API 请求。

server: {
  proxy: {
    "/api":{
      target: 'http://localhost:18760',
      changeOrigin: true,
      ws: true,
      rewrite: (path) => path.replace(new RegExp(`^/api`), ''),
    }
  },
},
server: {
  proxy: {
    "/api":{
      target: 'http://localhost:18760',
      changeOrigin: true,
      ws: true,
      rewrite: (path) => path.replace(new RegExp(`^/api`), ''),
    }
  },
},

注意:

从浏览器控制台的 Network 看,请求是 http://localhost:3100/api/xxx,这是因为 proxy 配置不会改变本地请求的 url。

后台开启 cors之acuity-cloud

仅需在网关服务进行跨域配置即可:

java
@Configuration
public class CorsConfiguration {
    private static final String ALL = "*";
    private static final String MAX_AGE = "18000L";
  @Bean
  @Order(Integer.MIN_VALUE)
  public WebFilter corsFilter() {
      return (ctx, chain) -> {
          ServerHttpRequest request = ctx.getRequest();
          if (!CorsUtils.isCorsRequest(request)) {
              return chain.filter(ctx);
          }
          HttpHeaders requestHeaders = request.getHeaders();
          ServerHttpResponse response = ctx.getResponse();
          HttpMethod requestMethod = requestHeaders.getAccessControlRequestMethod();
          HttpHeaders headers = response.getHeaders();
          headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, requestHeaders.getOrigin());
          headers.addAll(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, requestHeaders.getAccessControlRequestHeaders());
          if (requestMethod != null) {
              headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, requestMethod.name());
          }
          headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
          headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, ALL);
          headers.add(HttpHeaders.ACCESS_CONTROL_MAX_AGE, MAX_AGE);
          if (request.getMethod() == HttpMethod.OPTIONS) {
              response.setStatusCode(HttpStatus.OK);
              return Mono.empty();
          }
          return chain.filter(ctx);
      };
  }
}
@Configuration
public class CorsConfiguration {
    private static final String ALL = "*";
    private static final String MAX_AGE = "18000L";
  @Bean
  @Order(Integer.MIN_VALUE)
  public WebFilter corsFilter() {
      return (ctx, chain) -> {
          ServerHttpRequest request = ctx.getRequest();
          if (!CorsUtils.isCorsRequest(request)) {
              return chain.filter(ctx);
          }
          HttpHeaders requestHeaders = request.getHeaders();
          ServerHttpResponse response = ctx.getResponse();
          HttpMethod requestMethod = requestHeaders.getAccessControlRequestMethod();
          HttpHeaders headers = response.getHeaders();
          headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, requestHeaders.getOrigin());
          headers.addAll(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, requestHeaders.getAccessControlRequestHeaders());
          if (requestMethod != null) {
              headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, requestMethod.name());
          }
          headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
          headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, ALL);
          headers.add(HttpHeaders.ACCESS_CONTROL_MAX_AGE, MAX_AGE);
          if (request.getMethod() == HttpMethod.OPTIONS) {
              response.setStatusCode(HttpStatus.OK);
              return Mono.empty();
          }
          return chain.filter(ctx);
      };
  }
}

后台开启 cors之acuity-boot

java
@Bean
public CorsFilter corsFilter() {
    final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    final org.springframework.web.cors.CorsConfiguration config = new org.springframework.web.cors.CorsConfiguration();
    // 允许cookies跨域
    config.setAllowCredentials(true);
    // #允许向该服务器提交请求的URI,*表示全部允许,在SpringMVC中,如果设成*,会自动转成当前请求头中的Origin
    config.addAllowedOriginPattern("*");
    // #允许访问的头信息,*表示全部
    config.addAllowedHeader("*");
    // 预检请求的缓存时间(秒),即在这个时间段里,对于相同的跨域请求不会再预检了
    config.setMaxAge(18000L);
    // 允许提交请求的方法,*表示全部允许
    config.addAllowedMethod("OPTIONS");
    config.addAllowedMethod("HEAD");
    // 允许Get的请求类型
    config.addAllowedMethod("GET");
    config.addAllowedMethod("PUT");
    config.addAllowedMethod("POST");
    config.addAllowedMethod("DELETE");
    config.addAllowedMethod("PATCH");
    source.registerCorsConfiguration("/**", config);
    return new CorsFilter(source);
}
@Bean
public CorsFilter corsFilter() {
    final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    final org.springframework.web.cors.CorsConfiguration config = new org.springframework.web.cors.CorsConfiguration();
    // 允许cookies跨域
    config.setAllowCredentials(true);
    // #允许向该服务器提交请求的URI,*表示全部允许,在SpringMVC中,如果设成*,会自动转成当前请求头中的Origin
    config.addAllowedOriginPattern("*");
    // #允许访问的头信息,*表示全部
    config.addAllowedHeader("*");
    // 预检请求的缓存时间(秒),即在这个时间段里,对于相同的跨域请求不会再预检了
    config.setMaxAge(18000L);
    // 允许提交请求的方法,*表示全部允许
    config.addAllowedMethod("OPTIONS");
    config.addAllowedMethod("HEAD");
    // 允许Get的请求类型
    config.addAllowedMethod("GET");
    config.addAllowedMethod("PUT");
    config.addAllowedMethod("POST");
    config.addAllowedMethod("DELETE");
    config.addAllowedMethod("PATCH");
    source.registerCorsConfiguration("/**", config);
    return new CorsFilter(source);
}

使用 nginx 转发请求

  1. 配置前端项目接口地址
ini
# 在.env.production内,配置接口地址
VITE_GLOB_API_URL=/api
# 在.env.production内,配置接口地址
VITE_GLOB_API_URL=/api
  1. 在 nginx 配置请求转发到后台
nginx
server {
  listen       80;
  server_name  acuity.top;
  # 接口代理,用于解决跨域问题
  location /api {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    # 后台 acuity-gateway-server 接口地址
    proxy_pass http://110.110.1.1:8760/api;
    proxy_redirect default;
    add_header Access-Control-Allow-Origin *;
    add_header Access-Control-Allow-Headers X-Requested-With;
    add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
  }
}
server {
  listen       80;
  server_name  acuity.top;
  # 接口代理,用于解决跨域问题
  location /api {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    # 后台 acuity-gateway-server 接口地址
    proxy_pass http://110.110.1.1:8760/api;
    proxy_redirect default;
    add_header Access-Control-Allow-Origin *;
    add_header Access-Control-Allow-Headers X-Requested-With;
    add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
  }
}

欢迎使用天源云Saas快速开发系统