Spring Boot实现跨域请求

Spring Boot实现跨域请求

  • 一、 简介
  • 二、实现跨域的两种方式
    • JSONP 跨域
    • CORS 跨域
  • 三、Spring Boot配置跨域
    • 3.1、在方法上添加 @CrossOrigin 注解 ,仅对该接口有效:
    • 3.2、在类上添加@CrossOrigin 注解,仅对该类下的接口有效:
    • 3.3、全局配置,新增配置类WebMvcConfig.java,对该Application有效:
  • 4、原理简单剖析
  • 参考连接

一、 简介

    CORS是一个W3C标准,全称是“跨域资源共享”(Cross-origin resource sharing)。它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。
    由于浏览器同源策略(同源策略,它是由Netscape提出的一个著名的安全策略。现在所有支持JavaScript 的浏览器都会使用这个策略。所谓同源是指,域名,协议,端口相同。),凡是发送请求url的协议、域名、端口三者之间任意一与当前页面地址不同即为跨域。

二、实现跨域的两种方式

JSONP 跨域

    JSONP 是 JSON with padding(填充式 JSON 或参数式 JSON)的简写。JSONP实现跨域请求的原理简单的说,就是动态创建 < script >标签,然后利用< script >的src 不受同源策略约束来跨域获取数据。
    JSONP 由两部分组成:回调函数和数据。回调函数是当响应到来时应该在页面中调用的函数。回调函数的名字一般是在请求中指定的。而数据就是传入回调函数中的 JSON 数据。
    这里不详讲这个,有兴趣的话请看下面这篇文章:
                            轻松搞定JSONP跨域请求

CORS 跨域

为了解决浏览器同源问题,W3C 提出了跨源资源共享,即 CORS(Cross-Origin Resource Sharing)。

三、Spring Boot配置跨域

主要使用@CrossOrigin注解实现,源码如下:

...
@Target({ElementType.METHOD, ElementType.TYPE})//可作用于方法,类上
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CrossOrigin {
	...
    @AliasFor("origins")//允许可访问的域列表
    String[] value() default {};
	...
    long maxAge() default -1L;//准备响应前的缓存持续的最大时间(以秒为单位)。
}

3.1、在方法上添加 @CrossOrigin 注解 ,仅对该接口有效:

    @CrossOrigin(origins = "http://localhost:8080",maxAge = 3600)
    @RequestMapping("/test")
    String test() {
		...
	}

3.2、在类上添加@CrossOrigin 注解,仅对该类下的接口有效:

@CrossOrigin(origins = "http://localhost:8080",maxAge = 3600)
@RestController
public class UserController {
	...
}

3.3、全局配置,新增配置类WebMvcConfig.java,对该Application有效:

/**
 * @author Macky
 * @Title class WebMvConfig
 * @Description: TODO
 * @date 2019/8/2 13:52
 */
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE")
                .maxAge(3600)
                .allowCredentials(true);
    }
}

还可以添加Filter,指定CORS规则,并指定对哪些接口有效:

@Bean
    public FilterRegistrationBean corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);   
        config.addAllowedOrigin("http://localhost:8080");
        config.addAllowedOrigin("null");
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        source.registerCorsConfiguration("/**", config); // CORS 配置对所有接口都有效
        FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
        bean.setOrder(0);
        return bean;
    }

4、原理简单剖析

Spring cors的校验都是交给DefaultCorsProcessor类中的processRequest()方法实现的,核心代码如下:

public boolean processRequest(@Nullable CorsConfiguration config, HttpServletRequest request, HttpServletResponse response) throws IOException {
        if (!CorsUtils.isCorsRequest(request)) {//1.判断是否有“Origin”的header,,有的话
            return true;
        } else {
            ServletServerHttpResponse serverResponse = new ServletServerHttpResponse(response);
            if (this.responseHasCors(serverResponse)) {//2.判断response中header是否有“Access-Control-Allow-Origin”
                logger.trace("Skip: response already contains \"Access-Control-Allow-Origin\"");
                return true;
            } else {
                ServletServerHttpRequest serverRequest = new ServletServerHttpRequest(request);
                if (WebUtils.isSameOrigin(serverRequest)) {//3.判断是否同源
                    logger.trace("Skip: request is from same origin");
                    return true;
                } else {
                    boolean preFlightRequest = CorsUtils.isPreFlightRequest(request);
                    if (config == null) {//4.是否配置了 CORS 规则,如果没有配置,且是预检请求,则拒绝该请求,如果没有配置,且不是预检请求,则交给负责该请求的类处理。如果配置了,则对该请求进行校验。
                        if (preFlightRequest) {
                            this.rejectRequest(serverResponse);
                            return false;
                        } else {
                            return true;
                        }
                    } else {
                        return this.handleInternal(serverRequest, serverResponse, config, preFlightRequest);
                    }
                }
            }
        }
    }

参考连接

SpringBoot配置Cors解决跨域请求问题

你可能感兴趣的:(spring-boot)