跨域原因:

跨域请求 浏览器不会把cookies带到后端吗 浏览器报跨域_跨域

  1. 浏览器限制,浏览器出于安全原因,当它发现你的请求跨域了,它就会开启校验,如果校验不通过就会报跨域安全错误;
  2. 请求域名、协议、端口不一致,产生跨域;
  3. 发出的是XHR(XMLHttpRequest)请求;

满足上面三点浏览器就会产生跨域报错;

跨域解决办法:

  1. 针对浏览器限制,可以关闭浏览器的跨域校验,这个方法实际环境不能实现,不可能要求每个用户关闭浏览器跨域校验(不推荐);
  2. 针对XHR请求问题,可以使用Jsonp, Jsonp需要修改前后端代码,使用起来并不方便,现在使用的比较少(不推荐);
  3. 被调用方解决(服务器端)处理,返回校验信息,告诉浏览器服务器允许客户端进行跨域请求,那么浏览器通过校验就不会限制跨域,这个方式需要服务器端是本公司的,修改服务器端支持跨域(推荐);
  4. 调用方解决,服务器不是本公司,或者没有办法修改服务器端代码。那么需要调用方通过代理等方式隐藏跨域(推荐);

被调用方解决(服务器端)

  1. 应用服务器端
    通过Filter来处理
import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @description 跨域处理的Filter
 * @author: fanxl
 * @date: 2018/10/10 0010 12:42
 */
public class CrosFilter implements Filter {
    
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        // 如果是所有方法和所有域名 可以使用 *
        response.addHeader("Access-Control-Allow-Origin", "http://localhost:8081");
        response.addHeader("Access-Control-Allow-Methods", "GET");
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {

    }
}

注册Filter

@Bean
public FilterRegistrationBean registrationFilter() {
    FilterRegistrationBean bean = new FilterRegistrationBean();
    bean.addUrlPatterns("/*");
    bean.setFilter(new CrosFilter());
    return bean;
}

对于简单请求,浏览器是先请求再做跨域校验,而对于非简单请求,则是先判断再请求。
工作中简单请求:
请求方法为: GET HEAD POST
请求header里面:
无自定义头
Content-Type为以下几种:
text/plain
multipart/form-data
application/x-www-form-urlencoded

非简单请求:
请求方法为: PUT DELETE
请求header里面有自定义头
Content-Type 为application/json的

对于非简单请求,浏览器会先发一个预检命令,预检命令通过之后再发生请求。这个时候需要增加

response.addHeader("Access-Control-Allow-Headers", "Content-Type");

由于非简单请求每次都用发送两遍请求,所以可以设置预检命令请求缓存,这里指定缓存时间 单位秒

response.addHeader("Access-Control-Max-Age", "3600");
带Cookie的跨域

如果请求是带Cookie的,那么服务器端设置的域名不能设置为*,必须设置具体的域名,而如果设置具体的域名,那么就不能实现对其他域名的跨域支持,这个时候就需要动态是设置跨域域名,根据请求的域名进行配置,代码如下:

HttpServletRequest request = (HttpServletRequest) servletRequest;
String origin = request.getHeader("Origin");
if (!StringUtils.isEmpty(origin)){
    response.addHeader("Access-Control-Allow-Origin", origin);
}
// 支持cookie
response.addHeader("Access-Control-Allow-Credentials", "true");

支持自定义头的跨域

// 支持所有自定义头的跨域
String heads = request.getHeader("Access-Control-Allow-Headers");
if (!StringUtils.isEmpty(heads)) {
    response.addHeader("Access-Control-Allow-Headers", heads);
}
  1. Spring服务器
    直接在对应Controller上添加@CrossOrigin即可
@RestController
@RequestMapping(value = "/api/v1/tickey")
@CrossOrigin
public class TickeyRestController
  1. 服务器端(nginx配置)
# nginx配置支持跨域 
 add_header Access-Control-Allow-Methods *;
 add_header Access-Control-Max-Age 3600;
 add_header Access-Control-Allow-Credentials true;

 add_header Access-Control-Allow-Origin $http_origin;
 add_header Access-Control-Allow-Headers $http_access_control_request_headers;

 if ($request_method = OPTIONS) {
     return 200;
 }

调用方解决(隐藏跨域)

调用方解决方案,是利用调用方的Nginx服务器的反向代理来实现