—-慢慢来比较快,虚心学技术—-

image.png

跨域问题模拟

现在我们有一个本地接口:http://localhost:8090/log/getLog

在浏览器随手打开百度页面(https://www.baidu.com),并打开开发者工具
image.png

在百度页面的console窗口中,执行如下js代码,模拟一个XMLHttpRequest请求,访问本地接口

  1. var xml = new XMLHttpRequest();
  2. xml.onreadystatechange = function(){
  3. if(xml.readyState == 0){console.log('请求未初始化');}
  4. if(xml.readyState == 1){console.log('服务器连接已建立');}
  5. if(xml.readyState == 2){console.log('请求已接收');}
  6. if(xml.readyState == 3){console.log('请求处理中');}
  7. if(xml.readyState == 4){console.log('请求已完成,且响应已就绪');}
  8. };
  9. xml.open("GET","http://localhost:8090/log/getLog",true);
  10. xml.send();
  11. console.log(xml);

建立连接并发起请求后,报出CROS异常
image.png
Access to XMLHttpRequest at ‘http://localhost:8090/log/getLog’ from origin ‘https://www.baidu.com’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.

image.png

一、为什么会出现跨域问题


浏览器的同源策略

image.png
同源策略指的是:为了安全,确保一个应用中的资源只能被本应用的资源访问

如果没有同源策略,当有人在你的页面嵌入js代码就可以访问未知的服务器资源,可能导致完全不可控的影响

什么是源(origin)? 源(origin)就是协议、域名和端口号。

若两个URL的协议,端口,主机都一直,那就认为这两个URL是同源的,这也是浏览器的一种保护机制,确保当前应用中的资源只能被本应用的资源访问

如上我们打开的是https://www.baidu.com,基于该网站该协议的资源都是允许访问的,但是当我们去访问协议为http,主机为localhost,端口号为8090的接口,已经违反了同源策略,所以报出跨域异常

二、什么是跨域

当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域

二、跨域是请求报错还是响应报错

答案:响应报错,因为浏览器同源策略并不阻拦接口访问,只是限制了响应的接收。从我们的demo例子里面也是可以看到的

三、跨域问题解决方案

前端解决-JSONP

使用JSONP进行资源访问【将请求从XMLHttpRequest转为从javascript发起】
限制:仅允许Get请求发送非xhr请求

中间件解决-Nginx反向代理【常用】

保证本页面下的所有请求都是统一协议,端口和主机,自然不存在跨域问题

示例:
前端访问资源和访问接口都用统一路径:http://localhost:8080,对于浏览器而言,符合协议、主机、端口一致

server {

  # nginx监听所有localhost:8080端口收到的请求
    listen       8080;
    server_name  localhost;

    # Load configuration files for the default server block.
    include /etc/nginx/default.d/*.conf;

  #localhost:8080 会被转发到这里
    #同时, 后端程序会接收到 "192.168.25.21:8088"这样的请求url
    location / {
        proxy_pass http://192.168.25.21:8088;
    }

    # localhost:8080/api/ 会被转发到这里
  # 同时, 后端程序会接收到 "192.168.25.20:9000/api/"这样的请求url
    location /api/ {
        proxy_pass http://192.168.25.21:9000;
    }
}

优点:支持所有浏览器,支持 session,不需要修改任何代码,并且不会影响服务器性能。

后端解决-CORS协议

跨源资源共享 Cross-Origin Resource Sharing(CORS) 是一个新的 W3C 标准,它新增的一组HTTP首部字段,允许服务端其声明哪些源站有权限访问哪些资源。换言之,它允许浏览器向声明了 CORS 的跨域服务器,发出 XMLHttpReuest 请求,从而克服 Ajax 只能同源使用的限制。

CORS协议新增的HTTP首部字段大体如下:

Access-Control-Allow-Origin | * origin 参数的值指定了允许访问该资源的外域 URI。对于不需要携带身份凭证的请求,服务器可以指定该字段的值为通配符,表示允许来自所有域的请求。
Access-Control-Allow-Methods [, ]* 用于预检请求的响应,指明实际请求所允许使用的HTTP方法
Access-Control-Allow-Headers [, ]* 用于预检请求的响应。指明了实际请求中允许携带的首部字段
Access-Control-Max-Age 用于预检请求的响应,指定了预检请求能够被缓存多久
Access-Control-Allow-Credentials true | false 表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中
Origin 表明预检请求或实际请求的源站。不管是否为跨域请求,Origin字段总是被发送
Access-Control-Request-Method 用于预检请求。其作用是,将实际请求所使用的 HTTP 方法告诉服务器
Access-Control-Request-Headers [, ]* 用于预检请求。其作用是,将实际请求所携带的首部字段告诉服务器

想要实现跨域请求,后端需要进行响应头设置.

以SpringBoot项目为例,可以添加一个全局过滤器,将向HttpServletResponse的响应头设置跨域允许路径

import cn.hutool.core.util.StrUtil;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
public class CorsFilter implements Filter {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        // 向响应头设置允许跨域请求的url
        if(StrUtil.isBlank(response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN))){
            response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "https://www.baidu.com");
        }
        filterChain.doFilter(servletRequest,servletResponse);
    }
}

此时再去访问http://localhost:8090/log/getLog,已能跨域访问
image.png
image.png

参考文档:https://blog.csdn.net/qq_38128179/article/details/84956552

如有贻误,还请评论指正