Spring Boot 跨域大揭秘:4种高效解决方案让你轻松应对

springboot web的4种允许跨域的方式

跨域产生的条件

  • 浏览器异步请求

    • 协议不同,如一个为http,一个为https
    • IP或域名不同
    • 端口不同 比如你在a.com:80的域名页面访问a.com:8080
  • 推荐方式2方式4,方式1和方式3都不好控制,要想更好的控制需要自己实现

允许跨域的方式很多,比如vue的server proxy,nginx做代理,Java Web拦截器/过滤器等

坑点

  1. 测试过程中发现配置了允许跨域,chrome浏览器仍然报不允许跨域的提示,换edge却可以正常访问
    2.配置origin时,origin配置的是你的来源方的域名,比如我在http://a.com 页面调用http://b.com/api ,那么origin应配置为http://a.com

方式1

直接在接口上添加@CrossOrigin注解

// @CrossOrigin
// @CrossOrigin(origins = "*")
// @CrossOrigin(origins = "https://www.doubao.com")
// @CrossOrigin(originPatterns = "https://www.doubao.com")
// @CrossOrigin(originPatterns = "https://www.doubao.com:[*]")
@CrossOrigin(originPatterns = "https://*.doubao.com:[*]")
@PostMapping("/test")
public Demo test(@RequestBody Map<String, Object> param) {
    return null;
}

方式2(推荐)

package org.example.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {

        // 和allowedOrigins("*")不能同时配置allowCredentials(true),配置了allowedOrigins("*")就不能配置allowCredentials(true)
        // allowCredentials(true)要求配置明确的跨域源
        // 不同浏览器存在缓存还是什么机制,如下配置在chrome浏览器会跨域,edge却正常
        registry.addMapping("/**")
                // .allowedOrigins("*")

                // 精准指定
                .allowedOrigins("https://www.doubao.com")

                // 通配符指定
                // .allowedOriginPatterns("https://*.doubao.com")
                // .allowedOriginPatterns("https://www.doubao.com:[*]")

                // .allowedMethods("*")
                .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
                .allowedHeaders("Content-Type", "Authorization", "Access-Control-Allow-Origin")
                .allowCredentials(true)
                .maxAge(3600);

    }
}

方式3

自定义过滤器

package org.example.filter;

import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.Set;

/**
 * @author admin
 */
@Slf4j
@Component
@WebFilter(urlPatterns = "/*")
public class CustomCorsFilter implements Filter {

    private final Set<String> METHODS = Set.of("GET", "POST", "PUT", "DELETE", "OPTIONS");
    private final String METHOD = String.join(",", METHODS);

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        log.info("doFilter ...");
        HttpServletRequest req = (HttpServletRequest) servletRequest;
        HttpServletResponse resp = (HttpServletResponse) servletResponse;
        String method = req.getMethod();
        if (method == null) {
            return;
        }

        if (!METHODS.contains(method)) {
            return;
        }

        log.info("doFilter ... Request Method: {}", method);
        log.info("Request Headers: {}", req.getHeaderNames());
        log.info("header: {}:{}", HttpHeaders.ORIGIN, req.getHeader(HttpHeaders.ORIGIN));

        // 设置允许跨域的域名,* 表示允许所有域名
        resp.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "https://www.doubao.com");
        // resp.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, CorsConfiguration.ALL);
        // 设置允许的请求方法
        resp.addHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, METHOD);
        // 设置允许的请求头
        resp.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, HttpHeaders.CONTENT_TYPE);

        resp.setHeader(HttpHeaders.ACCESS_CONTROL_MAX_AGE, "3600");
        // 允许携带凭证(如 cookie)
        resp.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, Boolean.TRUE.toString());

        if (HttpMethod.OPTIONS.matches(req.getMethod())) {
            log.info("doFilter OPTIONS");
            resp.setStatus(HttpServletResponse.SC_OK);
            return;
        }

        filterChain.doFilter(servletRequest, servletResponse);
    }
}

方式4(推荐)

添加配置类注入web自带的跨域过滤器CorsFilter

package org.example.filter;

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;

import java.util.List;

/**
 * @author admin
 */
@Configuration
public class MyCorsFilter {

    @Bean
    public CorsFilter corsFilter() {
        System.out.println("filter......");
        // 配置跨域规则
        CorsConfiguration config = new CorsConfiguration();
        // 允许所有来源
        config.addAllowedOriginPattern("*");
        // 允许所有请求方法
        // config.addAllowedMethod("*");
        config.setAllowedMethods(List.of("GET", "PUT", "DELETE", "OPTIONS"));
        // 允许所有请求头
        config.addAllowedHeader("*");
        // 允许携带凭证(如 cookie)
        config.setAllowCredentials(true);

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        // 对所有接口都有效
        source.registerCorsConfiguration("/**", config);

        return new CorsFilter(source);
    }
}
1 Like