一、跨域问题出现的原因

        跨域是指a页面想获取b页面资源,如果a、b页面的协议、域名、端口、子域名不同,或是a页面为ip地址,b页面为域名地址,所进行的访问行动都是跨域的,而浏览器为了安全问题一般都限制了跨域访问,也就是不允许跨域请求资源。

  跨域问题的根本原因:因为浏览器收到同源策略的限制,当前域名的js只能读取同域下的窗口属性。什么叫做同源策略?同源策略是浏览器的一个安全限制,要求域名、协议、端口相同,如果不同则没办法进行数据交互。而跨域配置,则是为了解除这方面的限制。

配置选项简介

• Access-Control-Allow-Origin: 允许访问的域名
• Access-Control-Allow-Methods: 允许访问的请求方式
• Access-Control-Allow-Headers: 允许使用的 Header
• Access-Control-Allow-Credentials: 是否允许用户发送、处理 Cookie
• Access-Control-Max-Age: 预检有效期,单位为秒。有效期内,不需要重复发送预检请求
• Access-Control-Expose-Headers: Header 白名单,不设置的话,客户端读不到 Header 的内容

二、解决办法

第一方法:

import org.springframework.context.annotation.Configuration;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@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");
        chain.doFilter(req, res);
    }
}

        这种办法,是基于过滤器的方式,方式简单明了,这种办法,是基于过滤器的方式,方式简单明了,就是把这些响应头写入到response响应中去

第二种方法:

public class GoodsController {

    @CrossOrigin(origins = "http://localhost:4000")
    @GetMapping("goods-url")
    public Response queryGoodsWithGoodsUrl(@RequestParam String goodsUrl) throws Exception      {
    
    }
}

        这种方法就是采用@CrossOrigin注解,查看@CrossOrigin源码,就能知道这注解可以放到method、class等上面,类似于@RequestMapping,也就是说这个这个注解可以控制class类中的所有方法跨域,也可以让单个方法进行跨域。这种方式虽然实现比较简单,但此方法只能实现局部跨域,当一个项目中存在多个类或者方法的话,使用这种方法比较麻烦,需要给所有类或者方法都添加上此注解。

第三种方法:

@Configuration
public class CrossDomainStarter {

    @Bean
    public CorsFilter corsFilter() {
        CorsConfiguration config = new CorsConfiguration();
        //放行的域名
        config.addAllowedOrigin("*");
        //放行的请求类型,有 GET, POST, PUT, DELETE, OPTIONS
        config.addAllowedMethod("*");
        //放行的请求头
        config.addAllowedHeader("*");
        //暴露头部信息
        config.addExposedHeader("*");
        //是否允许发送 Cookie
        config.setAllowCredentials(true);

        UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
        corsConfigurationSource.registerCorsConfiguration("/**",config);

        return new CorsFilter(corsConfigurationSource);
    }
}

        而 Spring-Framework 5.3 版本之后,关于 CORS 跨域配置类 CorsConfiguration 中将 addAllowedOrigin 方法名修改为 addAllowedOriginPattern,因此配置了变成了以下这样:

@Configuration
public class CrossDomainStarter {

    @Bean
    public CorsFilter corsFilter() {
        CorsConfiguration config = new CorsConfiguration();
        //放行的域名
        config.addAllowedOriginPattern("*");
        //放行的请求类型,有 GET, POST, PUT, DELETE, OPTIONS
        config.addAllowedMethod("*");
        //放行的请求头
        config.addAllowedHeader("*");
        //暴露头部信息
        config.addExposedHeader("*");
        //是否允许发送 Cookie
        config.setAllowCredentials(true);

        UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
        corsConfigurationSource.registerCorsConfiguration("/**",config);

        return new CorsFilter(corsConfigurationSource);
    }
}

区别:

  1. addAllowedOriginPattern:这个方法允许通过使用通配符模式来匹配多个来源。您可以使用正则表达式或简单的通配符模式来指定允许的来源。例如,您可以使用addAllowedOriginPattern("*")来允许所有来源的访问,或使用addAllowedOriginPattern("https://*.example.com")来允许以"https://"开头并以".example.com"结尾的任何来源访问。这种方式更加灵活,可以满足多种来源的需求。
  2. addAllowedOrigin:这个方法用于指定允许访问资源的具体来源。您需要明确指定每个允许的来源。例如,您可以使用addAllowedOrigin("https://example.com")来允许只有"http://example.com"这个来源访问资源。这种方式更加具体和明确

Spring Security CORS 处理
        Spring Boot 环境下如果引入了 Spring Security,Spring 将自动配置 CorsFilter,此时从CorsConfigurationSource 类型的 bean 中读取 CORS 配置,因此将 CorsConfigurationSource 配置为 bean 即可。示例代码如下。

@Configuration
public class WebMvcConfig {

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addAllowedOrigin("*");
        corsConfiguration.addAllowedMethod("*");
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.setExposedHeaders(Arrays.asList("header1", "header2"));
        corsConfiguration.setMaxAge(3600L);
        corsConfiguration.setAllowCredentials(true);
        UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
        corsConfigurationSource.registerCorsConfiguration("/*", corsConfiguration);
        return corsConfigurationSource;
    }
}

Nginx 跨域处理

在Nginx服务器的配置文件中添加以下代码:

server {
    listen       80;
    server_name  your_domain.com;
    location /api {
        # 允许跨域请求的域名,* 表示允许所有域名访问
        add_header 'Access-Control-Allow-Origin' '*';

        # 允许跨域请求的方法
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';

        # 允许跨域请求的自定义 Header
        add_header 'Access-Control-Allow-Headers' 'Origin, X-Requested-With, Content-Type, Accept';

        # 允许跨域请求的 Credential
        add_header 'Access-Control-Allow-Credentials' 'true';

        # 预检请求的存活时间,即 Options 请求的响应缓存时间
        add_header 'Access-Control-Max-Age' 3600;

        # 处理预检请求
        if ($request_method = 'OPTIONS') {
            return 204;
        }
    }
    # 其他配置...
}

上述示例中,location /api 代表配置针对 /api 路径的请求进行跨域设置

小结

跨域问题可以在网关层、反向代理层或应用层来解决,而它们的使用优先级是:网关层 > 代理层 > 应用层。因为越靠前覆盖范围就越大,解决跨域问题就越容易