解决跨域问题


CORS全称Cross-Origin Resource Sharing,意为跨域资源共享。当一个资源去访问另一个不同域名或者同域名不同端口的资源时,就会发出跨域请求。如果此时另一个资源不允许其进行跨域资源访问,那么访问的那个资源就会遇到跨域问题。
跨域问题演示及解决
我们使用mall项目的源代码来演示一下跨域问题。此时前端代码运行在8090端口上,后端代码运行在8080端口上,由于其域名都是localhost,但是前端和后端运行端口不一致,此时前端访问后端接口时,就会产生跨域问题。
覆盖默认的CorsFilter来解决该问题
添加GlobalCorsConfig配置文件来允许跨域访问。

点击查看代码
package com.macro.mall.config;

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;

/**
 * 全局跨域配置
 * Created by macro on 2019/7/27.
 */
@Configuration
public class GlobalCorsConfig {

    /**
     * 允许跨域调用的过滤器
     */
    @Bean
    public CorsFilter corsFilter() {
        CorsConfiguration config = new CorsConfiguration();
        //允许所有域名进行跨域调用
        config.addAllowedOrigin("*");
        //允许跨越发送cookie
        config.setAllowCredentials(true);
        //放行全部原始头信息
        config.addAllowedHeader("*");
        //允许所有请求方法跨域调用
        config.addAllowedMethod("*");
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        return new CorsFilter(source);
    }
}

重新运行代码,点击登录按钮

发现需要登录认证的/admin/info接口的OPTIONS请求无法通过认证,那是因为复杂的跨越请求需要先进行一次OPTIONS请求进行预检,我们的应用整合了SpringSecurity,对OPTIONS请求并没有放开登录认证。

设置SpringSecurity允许OPTIONS请求访问

在SecurityConfig类的configure(HttpSecurity httpSecurity)方法中添加如下代码。

.antMatchers(HttpMethod.OPTIONS)//跨域请求会先进行一次options请求

.permitAll()

解决跨域问题的三种思路

1、客户端浏览器解除跨域限制(理论上可以但是不现实)
2、发送JSONP请求替代XHR请求(并不能适用所有的请求方式,不推荐)
3、修改服务器端(包括HTTP服务器和应用服务器)(推荐)
.补充知识
在HTTP1.1 协议中的,请求方法分为GET、POST、PUT、DELETE、HEAD、TRACE、OPTIONS、CONNECT 八种。浏览器根据这些请求方法和请求类型将请求划分为简单请求和非简单请求。

简单请求:浏览器先发送(执行)请求然后再判断是否跨域。

请求方法为 GET、POST、HEAD,请求头header中无自定义的请求头信息,请求类型Content-Type 为 text/plain、multipart/form-data、application/x-www-form-urlencoded 的请求都是简单请求。

非简单请求:浏览器先发送预检命令(OPTIONS方法),检查通过后才发送真正的数据请求。
{% asset_img 非简单请求的预检命令.png 非简单请求的预检命令%}

预检命令会发送自定义头为Access-Control-Request-Headers: content-type的请求到服务器,根据响应头的中的 “Access-Control-Allow-Headers”: “Content-Type” 判断服务器是否允许跨域访问。预检命令是可以缓存,服务器端设置 “Access-Control-Max-Age”: “3600”,这样后面发送同样的跨域请求就不需要先发送预检命令了

3.修改服务器端

根据现如今网站架构设计,可以将前端应用看作调用方使用服务,将后端应用看作被调用方提供服务。

在应用服务器增加指定字段

对于不带 Cookie 的跨域请求,设置允许跨域的原始域名为任意域名,“Access-Control-Allow-Origin”: “”,设置允许跨域的方法为任意方法,“Access-Control-Allow-Methods”: “”,但是这样的星号设置不能满足带 Cookie 的跨域请求。

在 Java Web 中,可以添加一个过滤器来设置上面的参数。springboot使用注解完成

在 HTTP 服务器增加指定字段

以常用的 Nginx 服务器和 Apache 服务器为例。
Nginx 服务器允许跨域配置(注意不要手动直接点击Nginx.exe,否则停止和重新载入配置会失败的)

调用方修改
调用方的解决思路是反向代理,也即是将被调用方的域名代理到调用方域名下,这样就符合同源策略了,也就解决了跨域问题。