zoukankan      html  css  js  c++  java
  • 问题:跨域及解决方案

    跨域问题

    一、跨域问题介绍

    在前后端分离项目中,跨域问题是一定会遇到的。跨域问题的出现,会导致css、js或者ajax对后端请求等资源无法访问的情况。

    要想解决跨域问题,首先先得了解什么是跨域问题。

    问题:什么是跨域

    跨越问题是由于浏览器的同源策略,是浏览器强制要求的安全策略,所以想要了解跨域,首先得了解同源。

    所谓同源是指,协议域名端口均一致,只要以上三者有一个不一致,就会引发跨域问题

    前端页面 后端接口 是否跨域 原因
    http://banmoon.cn/index.html http://banmoon.cn/user/get 皆一致
    http://banmoon.cn/index.html https://banmoon.cn/user/get 协议不一致
    http://banmoon.cn/index.html http://other.com/user/get 域名不一致
    http://banmoon.cn/index.html http://banmoon.cn:8080/user/get 端口不一致
    http://localhost/index.html http://127.0.0.1/user/get 域名和IP路径也会导致跨域
    http://aaa.banmoon.cn/index.html http://bbb.banmoon.cn/user/get 二级域名不一致(cookie也不能访问)

    二、解决跨域问题的方法

    1、jsonp请求

    不做详解,列出优缺点,主要使用cors通信

    • 优点

      • 兼容性很好,能在许多低版本的浏览器上运行
    • 缺点

      • 只支持get请求,而不支持post及其他请求
      • 在调用失败时不会返回各种http的状态码
      • 只支持跨域http请求,不能解决跨域访问资源修改的问题
      • 安全性,容易被注入

    2、CORS通信

    1)介绍

    CORS 是一个 W3C 标准,全称是“跨域资源共享”(Cross-origin resource sharing)。它允许浏览器向跨域的服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。

    CORS 需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能。

    整个 CORS 通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS 通信与普通的 AJAX 通信没有差别,代码完全一样。浏览器一旦发现 AJAX 请求跨域,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感知。因此,实现 CORS 通信的关键是服务器。只要服务器实现了 CORS 接口,就可以跨域通信。

    2)简单请求与非简单请求

    CORS可分为两种请求,简单请求(simple-request)和非简单请求(no-so-simple-request)

    • 简单请求

      以下条件均满足的为简单请求,否则为非简单请求

      情况一:请求方式,为以下其中之一

      • get
      • post
      • head

      情况二:请求头,不能出现以下没有的

      • Accept
      • Accept-Language
      • Content-Language
      • Content-Type (需要注意额外的限制)
      • DPR
      • Downlink
      • Save-Data
      • Viewport-Width
      • Width

      情况三:Content-Type 的值仅限于下列三者之一

      • text/plain
      • multipart/form-data
      • application/x-www-form-urlencoded

      如果是简单请求,

    1. 浏览会在请求的头信息中添加Origin,请求直接到后端符文
    2. 后端服务根据请求头Origin(协议 + 域名 + 端口),判断本次请求是否允许跨域
    3. 后端设置Access-Control-Allow-Origin(* 或者 传入的Origin)响应头,表示同意本次请求跨域
    4. 浏览器识别是否有Access-Control-Allow-Origin,如果没有则抛出错误
    • 非简单请求
      当不满足简单请求的构成条件时,该请求就为非简单请求
      如果是非简单请求
    1. 浏览器会提前发送一个请求方式为options的预检请求

      Origin:同简单请求一样(协议 + 域名 + 端口)

      预检请求还包含两个特殊的请求头

      Access-Control-Request-Method:该字段是必须的,本次请求会用到哪些方法

      Access-Control-Request-Headers:本次请求额外携带的请求头,多个时用逗号分隔

    2. 服务器需要进行处理,并成功响应返回成功的状态码,并设置以下响应头

      Access-Control-Allow-Origin:可设置为*,也可设置为具体的请求地址(协议 + 域名 + 端口)

      Access-Control-Allow-Methods:必须,表示服务器所支持的所有请求方法,多个时用逗号分隔

      Access-Control-Allow-Headers:表示服务端所支持额外的请求头字段,多个时用逗号分隔

      Access-Control-Allow-Credentials:布尔值,代表服务端是否同意发送cookie

      Access-Control-Max-Age:本次预检请求的有效期(秒),在此期间浏览器不用再次发送预检请求

    3. 浏览器判断预检请求回来的响应头信息,判断本次是否允许跨域

    4. 允许跨域后,发送真正的请求,携带真实的数据进行传输请求

    5. 如果不允许,则控制台打印报错,不会发送真正的请求

    注意:

    1. CORS默认不发送Cookie,想要发送cookie必须如下设置

      1. 服务端必须将Access-Control-Allow-Credentials响应头设置为true,表示服务端同意传输cookie

      2. 前端必须设置withCredentials,浏览器处理同意传输cookie

        // 原生 xml 的设置方式
        var xhr = new XMLHttpRequest();
        xhr.withCredentials = true;
        // axios 设置方式
        axios.defaults.withCredentials = true;
        
      3. 服务端响应头Access-Control-Allow-Origin不能再设置为*,只能是具体的(协议 + 域名 + 端口),与Origin请求头保持一致

    2. 新版谷歌浏览器看不见options请求

      1. 在浏览器地址栏输入chrome://flags/#out-of-blink-cors
      2. 将其设置为disabled,然后重启浏览器

    三、CORS服务端设置响应

    1、SpringBoot简单设置,@CrossOrigin注解设置

    对于没有发送cookie要求的,我们可以直接使用@CrossOrigin注解,加在控制类或者方法上

    SpringBoot会自动将Access-Control-Allow-Origin的值设置为*

    @CrossOrigin
    @RestController
    public class UserController {
        
        @CrossOrigin
        @PostMapper("/create")
    	public ResultInfo create(@RequestBody User user){
            return null;
        }
        
    }
    

    2、SpringBoot简单设置,配置类实现WebMvcConfigurer接口设置

    此方式可以指定某几个地址访问,比上一种灵活一点

    @Configuration
    public class WebMvcConfiguration implements WebMvcConfigurer {
        
        @Override
        public void addCorsMappings(CorsRegistry registry) {
        	registry.addMapping("/**")//设置允许跨域的路径
                .allowedOrigins("*")//设置允许跨域请求的域名
                .allowCredentials(true)//是否允许证书 不再默认开启
                .allowedMethods("GET", "POST", "PUT", "DELETE")//设置允许的方法
                .maxAge(6000);//跨域允许时间
        }
        
    }
    

    3、SpringBoot拦截器设置

    此方法灵活程度最高,可以自行设置,允许cookie传输可通过此方法设置

    import org.apache.commons.lang.StringUtils;
    import org.springframework.http.HttpMethod;
    import org.springframework.http.HttpStatus;
    import org.springframework.stereotype.Component;
    import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    /**
    * CORS的拦截器
    */
    @Component
    public class CorsInterceptor extends HandlerInterceptorAdapter {
    	
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    	String origin = request.getHeader("Origin");
    	response.setHeader("Access-Control-Allow-Headers", "Sso-Token,token");
            response.setHeader("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH");
    	if(StringUtils.isBlank(origin)){
    	    response.setHeader("Access-Control-Allow-Origin", "*");
    	}else{
    	    response.setHeader("Access-Control-Allow-Origin", origin);
    	}
    	if (HttpMethod.OPTIONS.toString().equals(request.getMethod())) {
    	    response.setStatus(HttpStatus.NO_CONTENT.value());
    	    return true;
    	}
    	return true;
        }
    	
    }
    
    /**
    * 注册使用拦截器
    */
    @Configuration
    public class WebMvcConfiguration implements WebMvcConfigurer {
        @Resource
        private CorsInterceptor corsInterceptor;
        
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(corsInterceptor).addPathPatterns("/**");
        }
        
    }
    

    4、使用Nginx设置响应头达到CORS对服务器的配置

    ngx_http_headers_module是nginx编译时默认自带的模块,里面有add_headerexpires

    使用该模块下的add_header可以完成添加头信息的操作

    • 语法:add_ header name value [always];

    • 作用:可以添加自定义的响应头信息

    • 作用域:可以在http、server、location、if in location中使用

    • 注意

      • 当前作用域没有add_header,才会继承上一层设置的响应头。也就是说,add_header可以在最上层统一设置,然后个性化独立设置
      • Nginx 1. 7. 5后增加了always语法,即便后端接口发生500错误,设置的响应头也能生效
    • 简单使用

      server {
          listen 80;
          listen 443 ssl;
          server_name banmoon.com;
      
          add_header 'Access-Control-Allow-Origin' '*';
          add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,bird-idempotency';
          add_header 'Access-Control-Allow-Methods' 'GET,PUT,POST,DELETE,OPTIONS';
          add_header 'Access-Control-Allow-Credentials' 'true';
          
          location / {
              proxy_pass localhost:8080;
          }
      }
      
    • 需要传递cookie时

      server {
          listen 80;
          listen 443 ssl;
          server_name banmoon.com;
      
          if ($request_method = 'OPTIONS'){
              add_header 'Access-Control-Allow-Origin' "$http_origin";
              add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,bird-idempotency';
              add_header 'Access-Control-Allow-Methods' 'GET,PUT,POST,DELETE,OPTIONS';
              add_header 'Access-Control-Allow-Credentials' 'true';
              return 204;
          }
          if ($request_method != 'OPTIONS'){
              add_header 'Access-Control-Allow-Origin' "$http_origin";
              add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,bird-idempotency';
              add_header 'Access-Control-Allow-Methods' 'GET,PUT,POST,DELETE,OPTIONS';
              add_header 'Access-Control-Allow-Credentials' 'true';
          }
          
          location / {
              proxy_pass localhost:8080;
          }
      }
      
  • 相关阅读:
    2017ccpc全国邀请赛(湖南湘潭) E. Partial Sum
    Codeforces Round #412 C. Success Rate (rated, Div. 2, base on VK Cup 2017 Round 3)
    2017 中国大学生程序设计竞赛 女生专场 Building Shops (hdu6024)
    51nod 1084 矩阵取数问题 V2
    Power收集
    红色的幻想乡
    Koishi Loves Segments
    Wood Processing
    整数对
    Room and Moor
  • 原文地址:https://www.cnblogs.com/banmoon/p/13403260.html
Copyright © 2011-2022 走看看