SpringBoot 项目解决跨域问题

跨域

对于前后端分离的项目来说,如果前端项目与后端项目部署在两个不同的域下,那么势必会引起跨域问题的出现。

针对跨域问题,我们可能第一个想到的解决方案就是jsonp,并且以前处理跨域问题我基本也是这么处理。

但是jsonp方式也同样有不足,不管是对于前端还是后端来说,写法与我们平常的ajax写法不同,同样后端也需要作出相应的更改。并且,jsonp方式只能通过get请求方式来传递参数,当然也还有其它的不足之处,针对于此,我并没有急着使用jsonp的方式来解决跨域问题,去网上找寻其它方式,也就是本文主要所要讲的,在springboot中通过cors协议解决跨域问题。

Cors协议

H5中的新特性:Cross-Origin Resource Sharing(跨域资源共享)。通过它,我们的开发者(主要指后端开发者)可以决定资源是否能被跨域访问。

cors是一个w3c标准,它允许浏览器(目前ie8以下还不能被支持)像我们不同源的服务器发出xmlHttpRequest请求,我们可以继续使用ajax进行请求访问。

具体关于cors协议的文章 ,可以参考http://www.ruanyifeng.com/blog/2016/04/cors.html 这篇文章,讲的相当不错。

Cors配置

下列提供三种springboot + cors 解决跨域的方案

1. @CrossOrigin注解方式 Controller method CORS configuration

  • 在控制层方法上注解@CrossOrigin

    @CrossOrigin
    @RequestMapping(value = "users", method = RequestMethod.GET)
    public ResponseEntity<JsonResult> getUserList (){
    
    }
  • 在整个Controller类上面。即该controller所有映射都支持跨域请求。

    @CrossOrigin(origins = "http://domain2.com",
            maxAge = 3600,
            methods = {RequestMethod.GET, RequestMethod.POST})
    @RestController
    public class UserController{
    
    }

2. 全局CORS配置

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
 
@Configuration
public class CORSConfiguration {
 
    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurerAdapter() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
//                registry.addMapping("/api/**");
                        // 请求路径
                registry.addMapping("/**") 
                        // 允许跨域的地址    
                        .allowedOrigins("http://domain.com", "http://domain2.com") 
                        // 允许请求的类型
                        .allowedMethods("GET", "POST", "DELETE", "PUT", "OPTIONS")
                        .allowCredentials(false).maxAge(3600);
            }
        };
    }
}

3. 过滤器实现跨域 Filter based CORS support

@Configuration
public class MyConfiguration {
 
    @Bean
    public FilterRegistrationBean corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(false);
        config.addAllowedOrigin("http://domain.com");
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        source.registerCorsConfiguration("/**", config);
        FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
        bean.setOrder(0);
        return bean;
    }
}

MND推荐的Nginx配置

Nginx配置相当于在请求转发层配置。

location / {
     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' '*' always;
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range' always;
        add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
     }
     if ($request_method = 'GET') {
        add_header 'Access-Control-Allow-Origin' '*' always;
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range' always;
        add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
     }
}

在配置Access-Control-Allow-Headers属性的时候,因为自定义的header包含签名和token,数量较多。为了简洁方便,我把Access-Control-Allow-Headers配置成 * 。

在Chrome和firefox下没有任何异常,但在IE11下报了如下的错:

Access-Control-Allow-Headers 列表中不存在请求标头 content-type。

原来IE11要求预检请求返回的Access-Control-Allow-Headers的值必须以逗号分隔。



参考:

https://blog.csdn.net/saytime/article/details/74937204
https://mp.weixin.qq.com/s/v25dM-6HH3UtW9wz-zQNgQ

本文链接: https://jianz.xyz/index.php/archives/78/

1 + 6 =
快来做第一个评论的人吧~