—-慢慢来比较快,虚心学技术—-
跨域问题模拟
现在我们有一个本地接口:http://localhost:8090/log/getLog
在浏览器随手打开百度页面(https://www.baidu.com),并打开开发者工具
在百度页面的console窗口中,执行如下js代码,模拟一个XMLHttpRequest请求,访问本地接口
var xml = new XMLHttpRequest();
xml.onreadystatechange = function(){
if(xml.readyState == 0){console.log('请求未初始化');}
if(xml.readyState == 1){console.log('服务器连接已建立');}
if(xml.readyState == 2){console.log('请求已接收');}
if(xml.readyState == 3){console.log('请求处理中');}
if(xml.readyState == 4){console.log('请求已完成,且响应已就绪');}
};
xml.open("GET","http://localhost:8090/log/getLog",true);
xml.send();
console.log(xml);
建立连接并发起请求后,报出CROS异常
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.
一、为什么会出现跨域问题
浏览器的同源策略
同源策略指的是:为了安全,确保一个应用中的资源只能被本应用的资源访问
如果没有同源策略,当有人在你的页面嵌入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,已能跨域访问
参考文档:https://blog.csdn.net/qq_38128179/article/details/84956552
如有贻误,还请评论指正