跨域

Wu Jun 2019-12-25 15:59:03
Categories: > Tags:

常用的跨域解决方案有 JSONP 和 CORS, spingboot 2.0 开始不推荐使用 JSONP。

1 CORS

1.1 属性含义

属性 含义
value 指定所支持域的集合, 表示所有域都支持,默认值为 。这些值对应于 HTTP 请求头中的 Access-Control-Allow-Origin
origins @AliasFor(“value”),与 value 属性一样
allowedHeaders 允许请求头中的 headers,在预检请求 Access-Control-Allow-Headers 响应头中展示
exposedHeaders 响应头中允许访问的 headers,在实际请求的 Access-Control-Expose-Headers
methods 支持的 HTTP 请求方法列表,默认和 Controller 中的方法上标注的一致。
allowCredentials 表示浏览器在跨域请求中是否携带凭证,比如 cookies。在预检请求的 Access-Control-Allow-Credentials
maxAge 预检请求响应的最大缓存时间,单位为秒。在预检请求的 Access-Control-Max-Age 响应头中展示

1.2 实现方法

1) CrossOrigin 注解

在 Spring Boot 中为我们提供了一个注解 @CrossOrigin 来实现跨域,这个注解可以加在类或者方法上。

@Controller
public class HomeController {

    @GetMapping("/xxx")
    @ResponseBody
    @CrossOrigin
    public String xxx() {
        return "xxx";
    }
}

2)实现 WebMvcConfigurer 接口

@Configuration
public class CorsConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")
                .allowCredentials(true)
                .maxAge(168000)
                .allowedHeaders("*");
    }
}

3)过滤器

@Configuration
public class CorsConfig implements WebMvcConfigurer {
    @Bean
    public FilterRegistrationBean corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedOrigin("*");
        source.registerCorsConfiguration("/**", config);
        FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
        bean.setOrder(0);
        return bean;
    }
}

2 Jsonp

spingboot2.0 已不支持 JSONP

通过 jsonp 调用,对格式重新封装,解决前端跨域。
如:请求http://xxxx?&callback=exec, 那么返回的jsonp格式为exec({"code":0, "message":"success"});

继承AbstractJsonpResponseBodyAdvice,加入@ControllerAdvice注解,basePackages 标识要被处理的 controller。

@ControllerAdvice(basePackages = "xxx.controller")
public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice {

    private final String[] jsonpQueryParamNames;

    public JsonpAdvice() {
        super("callback", "jsonp");
        this.jsonpQueryParamNames = new String[]{"callback"};
    }

    @Override
    protected void beforeBodyWriteInternal(MappingJacksonValue bodyContainer, MediaType contentType, MethodParameter returnType, ServerHttpRequest request, ServerHttpResponse response) {

        HttpServletRequest servletRequest = ((ServletServerHttpRequest) request).getServletRequest();
     
    //如果不存在callback这个请求参数,直接返回,不需要处理为jsonp
        if (ObjectUtils.isEmpty(servletRequest.getParameter("callback"))) {
            return;
        }
    //按设定的请求参数处理返回结果为jsonp格式
        for (String name : this.jsonpQueryParamNames) {
            String value = servletRequest.getParameter(name);
            if (value != null) {
                MediaType contentTypeToUse = getContentType(contentType, request, response);
                response.getHeaders().setContentType(contentTypeToUse);
                bodyContainer.setJsonpFunction(value);
                return;
            }
        }
    }
}```