后端处理跨域问题


后端处理跨域问题

后端配置了WebMvcConfig进行WebMvcConfigurer跨域配置,前端请求接口还是提示跨域问题?
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        // 设置允许跨域的路径
        registry.addMapping("/**")
                // 设置允许跨域请求的域名
                //
                .allowedOrigins("")
                // 是否允许证书 不再默认开启
                .allowCredentials(true)
                // 设置允许的请求方法 [这里也可以传入多个可变参数:"GET","POST","DELETE" ]
                .allowedMethods("*")
                // 跨域允许时间
                .maxAge(3600);
    }
}

CORS 是一种机制,它使用额外的 HTTP 头来告诉浏览器允许一个域名访问另一个域名的资源。

配置一个全局的 CORS 支持,允许来自任何源的请求,并且允许任何 HTTP 方法和请求头,同时允许携带凭据,并设置了预检请求的有效期为 1 小时。这样的配置通常用于开发环境,因为它的安全性较低。

在生产环境中,应该明确指定 允许的源 和 方法,以提高安全性。

代码解析:

  1. addCorsMappings 方法
    • 这是 WebMvcConfigurer 接口中定义的一个方法,用于配置 CORS 支持。
    • 参数 CorsRegistry registry 是一个接口,用于注册 CORS 映射。
  2. addMapping 方法
    • addMapping("/**") 表示为所有的 URL 路径配置 CORS 支持。这里的 "/**" 是一个通配符,表示所有路径。
  3. allowedOrigins 方法
    • allowedOrigins("*") 表示允许来自任何源的请求。"*" 是一个通配符,表示任何来源都被允许。这在开发环境中很常见,但在生产环境中通常会限制允许的来源。
  4. allowedMethods 方法
    • allowedMethods("*") 表示允许任何 HTTP 方法(GET、POST、PUT、DELETE 等)。
  5. allowedHeaders 方法
    • allowedHeaders("*") 表示允许任何 HTTP 请求头。
  6. allowCredentials 方法
    • allowCredentials(true) 表示允许携带 Cookie 和 HTTP 认证信息等凭据。默认情况下,CORS 请求不允许发送凭据。
  7. maxAge 方法
    • maxAge(3600) 表示预检请求的有效期为 3600 秒(1小时)。预检请求是由浏览器发起的 OPTIONS 请求,用于确认服务器是否允许特定的跨域请求。设置 maxAge 可以减少后续预检请求的次数。

问题所在:在使用拦截器时,不能使用WebMvcConfigurer跨域配置,拦截器会让跨域配置失效(除非这个请求不会被拦截)。

拦截器会让WebMvcConfigurer全局跨域配置失效,这种情况下可以使用过滤器进行跨域处理。

在 Spring Mvc 中,请求发起 —> 过滤器 —> 拦截器 —> Controller层

有一张图,展示过滤器和拦截器执行的先后顺序:

https://pic1.zhimg.com/70/v2-26c60403f5b816f1ac0353921d6f0303_1440w.avis?source=172ae18b&biz_tag=Post

所以,我们应该把跨域 配置在过滤器上,代码如下:

@Component
public class CorsFilter implements Filter {
 
    @Override
    public void destroy() {
 
    }
 
    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
 
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
 
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "*");
 
        chain.doFilter(req, res);
 
    }
 
    @Override
    public void init(FilterConfig arg0) throws ServletException {
 
    }
 
}

最后再来总结下通用后端解决跨域的方法:

1.@CrossOrigin 注解实现方法级别的细粒度(类/方法)的跨域控制

2.springboot2.0 实现WebMvcConfigurer 实现跨域

3.过滤器实现跨域

4.定制化参数实现跨域

@WebFilter(filterName = "corsFilter", urlPatterns = "/*",
        initParams = {@WebInitParam(name = "allowOrigin", value = "*"),
                @WebInitParam(name = "allowMethods", value = "GET,POST,PUT,DELETE,OPTIONS"),
                @WebInitParam(name = "allowCredentials", value = "true"),
                @WebInitParam(name = "allowHeaders", value = "Content-Type,X-Token")})
//前面要么是*,实际需求是根据业务参数定制化
public class CorsFilter implements Filter {
 
    private String allowOrigin;
    private String allowMethods;
    private String allowCredentials;
    private String allowHeaders;
    private String exposeHeaders;
 
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        allowOrigin = filterConfig.getInitParameter("allowOrigin");
        allowMethods = filterConfig.getInitParameter("allowMethods");
        allowCredentials = filterConfig.getInitParameter("allowCredentials");
        allowHeaders = filterConfig.getInitParameter("allowHeaders");
        exposeHeaders = filterConfig.getInitParameter("exposeHeaders");
    }
 
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        if (!StringUtils.isEmpty(allowOrigin)) {
            if(allowOrigin.equals("*")){
                // 设置哪个源可以访问
                response.setHeader("Access-Control-Allow-Origin", allowOrigin);
            }else{
                List<String> allowOriginList = Arrays.asList(allowOrigin.split(","));
                if (allowOriginList != null && allowOriginList.size() > 0) {
                    String currentOrigin = request.getHeader("Origin");
                    if (allowOriginList.contains(currentOrigin)) {
                        response.setHeader("Access-Control-Allow-Origin", currentOrigin);
                    }
                }
            }
        }
        if (!StringUtils.isEmpty(allowMethods)) {
            //设置哪个方法可以访问
            response.setHeader("Access-Control-Allow-Methods", allowMethods);
        }
        if (!StringUtils.isEmpty(allowCredentials)) {
            // 允许携带cookie
            response.setHeader("Access-Control-Allow-Credentials", allowCredentials);
        }
        if (!StringUtils.isEmpty(allowHeaders)) {
            // 允许携带哪个头
            response.setHeader("Access-Control-Allow-Headers", allowHeaders);
        }
        if (!StringUtils.isEmpty(exposeHeaders)) {
            // 允许携带哪个头
            response.setHeader("Access-Control-Expose-Headers", exposeHeaders);
        }
        filterChain.doFilter(servletRequest, servletResponse);
    }
 
    @Override
    public void destroy() {
 
    }
}

5.使用SpringCloud网关GateWay实现跨域

 
 
@Configuration
public class CorsConfig {
    @Bean
    public CorsWebFilter corsFilter() {
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(Boolean.TRUE);//允许Cookie跨域
        config.addAllowedMethod("*");
        config.addAllowedOrigin("*");
        config.addAllowedHeader("*");//不要设置成*,参考前面
 
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
        source.registerCorsConfiguration("/**", config);
 
        return new CorsWebFilter(source);
    }
}
 

:在下层服务不需要在做任何跨域配置,例如注解@CrossOrigin,否则会由于配置冲突导致依然出现跨域问题

6、nginx配置代理解决跨域问题

server {
        listen       8000;
        server_name  localhost;
        # / 表示匹配路径为/的url
        location / {
           proxy_pass http://需要跨域的域名:5500;
        }
 
        # /user 表示访问以/user 开头 的地址 如/username,/user/find等
        location /user {
           proxy_pass http://需要跨域的域名:3000;
        }
 
    }

7、nginx配置响应头允许跨域

#
# Wide-open CORS config for nginx
#
location / {
	
	#### 对OPTIONS请求,会设置很多的请求头,并返回204
     if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Allow-Origin' '*';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        #
        # Custom headers and headers various browsers *should* be OK with but aren't
        #
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
        #
        # Tell client that this pre-flight info is valid for 20 days
        #
        add_header 'Access-Control-Max-Age' 1728000;
        add_header 'Content-Type' 'text/plain; charset=utf-8';
        add_header 'Content-Length' 0;
        return 204;
     }
     if ($request_method = 'POST') {
        add_header 'Access-Control-Allow-Origin' '*';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
        add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
     }
     if ($request_method = 'GET') {
        add_header 'Access-Control-Allow-Origin' '*';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
        add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
     }
}

前端不建议做跨域处理,并且线上环境容易失效

SpringBoot
JAVA-技能点

评论