• 问题:跨域及解决方案


    跨域问题

    一、跨域问题介绍

    在前后端分离项目中,跨域问题是一定会遇到的。跨域问题的出现,会导致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;
          }
      }
      
  • 相关阅读:
    git clone 很慢提速方法
    在Windows上安装pytorch
    关于一些知名深度学习模型的转换
    【转】安装caffe2的参考
    Nasty Hacks
    寻梦
    Fibonacci Again
    统计元音
    首字母变大写
    查找最大元素
  • 原文地址:https://www.cnblogs.com/banmoon/p/13403260.html
Copyright © 2020-2023  润新知