SpringMvc CORS跨域设置

简介

基于安全的考虑,W3C规范规定浏览器禁止访问不同域(origin)的资源,目前绝大部分浏览器遵循这一规范,从而衍生出了跨域资源共享 (CORS)问题,相比于IFRAMEJSONPCORS更全面并且更安全,Spring Mvc为我们提供了一套多粒度的CORS解决方案。

CORS介绍

关于CORS的介绍,主要参考文章《Cross-Origin Resource Sharing (CORS)》

CORS的工作原理是添加新的HTTP headers来让服务器描述哪些源的请求可以访问该资源,对于可能对服务器造成不好影响的请求,规范规定浏览器需要先发送“预检”请求(也就是OPTION请求),在预检请求通过后再发送实际的请求,服务器还可以通知客户端是否应该随请求发送“凭据”(例如 Cookie 和 HTTP 身份验证),更详细的介绍可以参考上面的文章,本文主要讨论Spring MvcCORS的支持。

需要注意的是:

  • 简单请求
  • 下列请求之一:
    • HEAD
    • GET
    • POST
  • 只包含下列头信息:
    • Accept
    • Accept-Language
    • Content-Language
    • Content-Type (只局限于application/x-www-form-urlencoded、multipart/form-data 、text/plain)

不需要发送”预检“请求

  • 相关HTTP Header介绍

    • Request Header
      • Origin:请求的源信息(协议 + 域名 + 端口)
      • Access-Control-Request-Method:预检请求类型
      • Access-Control-Request-Headers:额外发送的Header信息
    • Resonse Header
      • Access-Control-Allow-Origin:服务器接受的源信息,“*”表示所有
      • Access-Control-Allow-Methods:服务器支持的所有跨域请求类型,“*”表示所有
      • Access-Control-Allow-Credentials:服务器是否接受Cookies和HTTP Authentication
      • Access-Control-Expose-Headers:服务器暴露的额外的Header,“*”表示所有
      • Access-Control-Max-Age:本次预检的有效期
  • Access-Control-Allow-Credentials设置为trueAccess-Control-Allow-Origin不能设置为“*”

@CrossOrigin注解

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CrossOrigin {
    @Deprecated
    String[] DEFAULT_ORIGINS = {"*"};

    @Deprecated
    String[] DEFAULT_ALLOWED_HEADERS = {"*"};

    @Deprecated
    boolean DEFAULT_ALLOW_CREDENTIALS = false;

    @Deprecated
    long DEFAULT_MAX_AGE = 1800;

    @AliasFor("origins")
    String[] value() default {};

    @AliasFor("value")
    String[] origins() default {};

    /**
     * @since 5.3
     */
    String[] originPatterns() default {};

    String[] allowedHeaders() default {};

    String[] exposedHeaders() default {};

    RequestMethod[] methods() default {};

    String allowCredentials() default "";

    long maxAge() default -1;
}

可以看见@CrossOrigin注解可以标注在类或者方法上,其中几个常量如DEFAULT_ORIGINS已经在Spring 5.0弃用,取而代之的是CorsConfiguration#applyPermitDefaultValues方法。

@CrossOrigin属性介绍

  • originsvalue

    支持的源,originsvalue都是相同的配置,互为别名,默认配置是“*”,表示服务器支持所有源的跨域请求,安全信息较低,最好根据实际情况设置对应的信息(协议 + 域名 + 端口)。

  • originPatterns

    同样表示支持的源,Spring 5.3 引入的属性,默认为空,与origins二选一,支持通配符的形式配置origins,比如https://*.domain1.com,该字段为list,也就是可以配置多个。

  • allowedHeaders

    允许跨域的请求头信息,默认为“*”表示允许所有的请求头,CORS默认支持的请求头为:Cache-ControlContent-LanguageExpiresLast-ModifiedPragma,如果你需要携带其他的请求头需要设置该属性。

  • exposedHeaders

    服务器允许客户端访问的相应头,默认为空,表示只允许访问:Cache-ControlContent-LanguageExpiresLast-ModifiedPragma,如果需要客户端访问其他的相应头需要设置该属性。

  • methods

    服务器允许的Http Request类型,默认是允许GETPOSTHEAD,根据项目需要自行设置。

  • allowCredentials

    浏览器是否需要把凭证(如:cookies、CSRF tokens)发送到服务器,默认是关闭的,因为该选项开启后会与配置的源建立高度信任的关系,并且还会暴露一些敏感信息,所以开启该选项时origin不允许设置为“*”。

  • maxAge

    “预检”结果的缓存时间,单位是秒,默认1800s,在缓存时间内同一请求不需要“预检”请求。

@CrossOrigin用法

标注在类上,该类的所有方法均会生效

@RestController
@RequestMapping("/account")
public class AccountController {

    @CrossOrigin
    @GetMapping("/{id}")
    public Account retrieve(@PathVariable Long id) {
        // ...
    }

    @DeleteMapping("/{id}")
    public void remove(@PathVariable Long id) {
        // ...
    }
}

同时也可以类和方法结合使用

@CrossOrigin(maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {

    @CrossOrigin("https://domain2.com")
    @GetMapping("/{id}")
    public Account retrieve(@PathVariable Long id) {
        // ...
    }

    @DeleteMapping("/{id}")
    public void remove(@PathVariable Long id) {
        // ...
    }
}

@CrossOrigin注解比较适用于较细粒度的跨域控制,对于全局的跨域控制,Spring Mvc提供了Global Configuration配置。

Global Configuration

Spring Mvc对于全局的CORS比较简单,分为两个方案

实现WebMvcConfigurer接口

创建WebConfig类实现WebMvcConfigurer接口,通过CorsRegistry设置跨域信息

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {

        registry.addMapping("/api/**")
            .allowedOrigins("https://domain2.com")
            .allowedMethods("PUT", "DELETE")
            .allowedHeaders("header1", "header2", "header3")
            .exposedHeaders("header1", "header2")
            .allowCredentials(true).maxAge(3600);

        // Add more mappings...
    }
}

通过CorsFilter

通过CorsConfiguration设置跨域信息,并将CorsConfiguration通过CorsFilter构造函数传递进去

    @Bean
    CorsFilter corsFilter(){
        CorsConfiguration config = new CorsConfiguration();

        // Possibly...
        // config.applyPermitDefaultValues()

        config.setAllowCredentials(true);
        config.addAllowedOrigin("https://domain1.com");
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");

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

        return new CorsFilter(source);
    }

总结

Spring Mvc对于CORS可以说是非常方便,本文主要是想让各位开发者对跨域有个整体的了解,各个参数代表的含义,而不是在所有项目中都一概而论的设置为“*”,要在自身项目的实际需求以及安全性上多做思考,防止生产事故。

你可能感兴趣的:(SpringMvc CORS跨域设置)