1.什么是跨域?
当一个页面请求url的协议、域名、端口三者之间任何一者与当前页面url不同即为跨域。举个例子:
| 当前页面url |
被请求页面url |
是否跨域 |
原因 |
| http://www.yzfree.com/ |
http://www.yzfree.com/index.html |
否 |
同源(协议、域名、端口号相同) |
| http://www.yzfree.com/ |
https://www.yzfree.com/index.html |
跨域 |
协议不同(http/https) |
| http://www.yzfree.com/ |
http://www.baidu.com/ |
跨域 |
主域名不同(yzfree/baidu) |
| http://www.yzfree.com/ |
http://blog.yzfree.com/ |
跨域 |
子域名不同(www/blog) |
| http://www.yzfree.com:8080/ |
http://www.yzfree.com:8089/ |
跨域 |
端口号不同(8080/8089) |
2.为什么会出现跨域问题
出现跨域问题是源于浏览器的同源策略限制的。同源策略(Same origin policy)是一种约定,它是浏览器针对安全功能的一种实现,如果缺少了同源策略,浏览器很容易受到XSS,CSFR等网络攻击。
3.跨域会导致什么问题
不能读取非同源网页中的 Cookie、LocalStorage 等数据
不能接触非同源网页的 DOM 结构
不能向非同源地址发送 AJAX 请求
跨域的现象:

注意:html有一些特殊标签例如:<img> <script> <link> <frame> 等具有跨域的特性,可以直接访问非同源地址,可放心使用。
4.跨域问题的解决方案
4.1 JSONP
JSONP 的方式就是通过添加一个 script 元素,远程获取 js 代码并执行。
整个逻辑就是:前端向服务器发送请求并指定回调函数为:test ,后端返回 js代码 “test(110)“ ,前端收到并执行
注意:这种方式只只支持get请求。
①原生实现:
1 2 3 4 5 6
| <script src="http://yzfree.com/v1?callback=test"></script> <script type="text/javascript"> function test(res){ console.log(res.data); } </script>
|
② jQuery ajax:
1 2 3 4 5 6 7 8 9
| $.ajax({ url: 'http://yzfree.com/v1', type: 'get', dataType: 'jsonp', jsonpCallback: "test", data: { } });
|
③ Vue.js
1 2 3 4 5 6 7
| this.$http.jsonp('http://yzfree.com/v1', { params: { }, jsonp: 'test' }).then((res) => { console.log(res); })
|
4.2 CORS
CORS 跨域资源分享(Cross-Origin Resource Sharing)的办法是让每一个页面需要返回一个名为Access-Control-Allow-Origin的http头来允许非同源的站点访问
一般请求只需在服务器端设置Access-Control-Allow-Origin,如果是带cookie的跨域请求那么前后端都要进行设置
【前端】
①原生ajax
1 2 3 4 5 6 7 8 9 10 11 12 13
| var xhr = new XMLHttpRequest();
xhr.withCredentials = true; xhr.open('post', 'http://yzfree.com/v1', true); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); xhr.send('user=gwx'); xhr.onreadystatechange = function() { if (xhr.readyState == 4 && xhr.status == 200) { alert(xhr.responseText); } };
|
② jQuery ajax
1 2 3 4 5 6 7 8 9 10 11
| $.ajax({ url: 'http://yzfree.com/v1', type: 'get' data: { } xhrFields: withCredentials: true } })
|
③vue-resource
1 2
| Vue.http.options.credentials = true
|
④ axios
1 2
| axios.defaults.withCredentials = true
|
【后端SpringBoot】
在服务器响应客户端的时候,带上Access-Control-Allow-Origin
① 使用 @CrossOrigin 注解
1 2 3 4 5 6 7 8 9 10
| @RequestMapping("/v1") @RestController
@CrossOrigin("https://yzfree.com") public class CorsTestController { @GetMapping("/test") public String sayHello() { return "success"; } }
|
② CORS全局配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @Configuration public class CorsConfig { private CorsConfiguration buildConfig() { CorsConfiguration corsConfiguration = new CorsConfiguration(); corsConfiguration.addAllowedOrigin("*"); corsConfiguration.addAllowedHeader("*"); corsConfiguration.addAllowedMethod("*"); return corsConfiguration; } @Bean public CorsFilter corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", buildConfig()); return new CorsFilter(source); } }
|
③ 拦截器实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| @Component public class CorsFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletResponse res = (HttpServletResponse) response; res.addHeader("Access-Control-Allow-Credentials", "true"); res.addHeader("Access-Control-Allow-Origin", "*"); res.addHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT"); res.addHeader("Access-Control-Allow-Headers", "Content-Type,X-CAF-Authorization-Token,sessionToken,X-TOKEN"); if (((HttpServletRequest) request).getMethod().equals("OPTIONS")) { response.getWriter().println("ok"); return; } chain.doFilter(request, response); } @Override public void destroy() { } @Override public void init(FilterConfig filterConfig) throws ServletException { } }
|
4.3 代理proxy
通过中间件来实现,浏览器有跨域限制,但是服务器没有。如果 changeOrigin 设置为true,那么本地会虚拟一个 node 服务端接收你的请求并转发
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| proxyTable: { '/api': { target: '目标地址', changeOrigin: true, pathRewrite: { '^/api': '' } } }
this.$axios.post("/api/gwx",{ 发送的数据 }).then(data=>{ console.log(data); })
|
4.4 nginx反向代理
通过修改 nginx.conf 配置文件做代理转发
1 2 3 4 5 6 7 8 9 10 11 12
| server { listen 80; server_name www.yzfree.com; location ^~/gwx/ { proxy_pass http: proxy_set_header Host $host:$server_port; proxy_set_header Remote_Addr $remote_addr; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; client_max_body_size 2048m; }
|
以上就是常见的几种跨域解决方案。