解决跨域问题

  跨域问题说明,参考【JS】AJAX跨域-JSONP解决方案(一)

  实例,使用上一章(【JS】AJAX跨域-JSONP解决方案(一))的实例

解决方案三(被调用方支持跨域-服务端代码解决)

  被调用方解决,基于支持跨域的解决思路,基于Http协议关于跨域的相关规定,在响应头里增加指定的字段告诉浏览器,允许调用

  跨域请求是直接从浏览器发送到被调用方,被调用方在响应头里增加相关信息,返回到页面,页面能正常获取请求内容。

  1、服务端增加一个过滤器(CrossFilter.java),过滤所有请求,在请求响应中增加内容,如下:

 package com.test.ajax.cross.filter;

 import java.io.IOException;

 import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.springframework.util.StringUtils; /**
* 服务端解决跨域
* @author h__d
*
*/
public class CrossFilter implements Filter { @Override
public void init(FilterConfig filterConfig) throws ServletException {
// TODO Auto-generated method stub } @Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse res = (HttpServletResponse) response; HttpServletRequest req = (HttpServletRequest) request; // 允许所有域,但不能满足带 cookie 的跨域请求
res.addHeader("Access-Control-Allow-Origin","*");
// 允许所有header
res.addHeader("Access-Control-Allow-Headers","*");
// 允许所有方法
res.addHeader("Access-Control-Allow-Methods", "*");
// 允许浏览器在一个小时内,缓存跨域访问信息(即上面三个信息)
res.addHeader("Access-Control-Max-Age", "3600"); chain.doFilter(request, response); } @Override
public void destroy() {
// TODO Auto-generated method stub } }

  2、在web.xml文件中注册过滤器

<filter>
<filter-name>CrossFilter</filter-name>
<filter-class>com.test.ajax.cross.filter.CrossFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CrossFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

  3、编辑测试界面,test3.html

 <!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="jquery-1.11.3.min.js" type="text/javascript"></script>
</head>
<body>
<h2>测试服务端解决跨域问题</h2>
<a href="#" onclick="get()">发送get请求</a>
</body>
<script type="text/javascript">
function get(){
$.getJSON("http://localhost:8080/test-ajax-cross/test/get").then(function(result){
console.log(result);
$("body").append("<br>" + JSON.stringify(result));
});
}
</script>
</html>

  4、在浏览器中输入地址进行访问,http://a.com:8080/test-ajax-cross/static/test3.html

    

    其他请求

      简单请求与非简单请求

      • 简单请求:浏览器先发送真正的请求后检查
      • 请求方法:GET、HEAD、POST的一种
      • 请求header:无自定义header;Content-Type为:text/plain、multipart/form-data、application/x-www-form-urlencoded的一种
      • 非简单请求:浏览器先发预检命令,检查通过后,才发送真正的请求
      • 常见的有:PUT、DELETE
      • 其它条件:发送Json格式的请求、带自定义header的请求
      • 预检命令:浏览器检测到跨域请求, 会自动发出一个OPTIONS请求, 就是所谓的预检(preflight)请求。当预检请求通过的时候,才发送真正的请求。

    A、带cookie的ajax请求,跨域问题

      1、编辑test3.html页面,增加一个带cookie的ajax请求

 <!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="jquery-1.11.3.min.js" type="text/javascript"></script>
</head>
<body>
<h2>测试服务端解决跨域问题</h2>
<a href="#" onclick="get()">发送get请求</a>
<a href="#" onclick="getCookie()">发送getCookie请求</a>
</body>
<script type="text/javascript">
function get(){
$.getJSON("http://localhost:8080/test-ajax-cross/test/get").then(function(result){
console.log(result);
$("body").append("<br>" + JSON.stringify(result));
});
}
// 测试带上cookie的请求能否跨域
function getCookie(){
$.ajax({
url: "http://localhost:8080/test-ajax-cross/test/getCookie",
xhrFields:{
// 带上证书,发送 AJAX 请求时带上 cookie
withCredentials:true
},
// 允许跨域
crossDomain: true,
success:function(result){
console.log(result);
$("body").append("<br>" + JSON.stringify(result));
}
});
}
</script>
</html>

      2、增加接受请求的方法

 @RequestMapping(value = "/getCookie", method = RequestMethod.GET)
@ResponseBody
public Map getCookie(@CookieValue(value = "cookie1") String cookie1) {
System.out.println("TestController getCookie()");
Map<String, Object> map = new HashMap();
map.put("data", "getCookie" + cookie1);
return map;
}

      3、编辑过滤器,支持带cookie的ajax请求

 package com.test.ajax.cross.filter;

 import java.io.IOException;

 import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.springframework.http.HttpStatus;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMethod; /**
* 服务端解决跨域
*
* @author h__d
*
*/
public class CrossFilter implements Filter { @Override
public void init(FilterConfig filterConfig) throws ServletException {
// TODO Auto-generated method stub } @Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse res = (HttpServletResponse) response; HttpServletRequest req = (HttpServletRequest) request; // 支持所有域
String origin = req.getHeader("Origin");
if (!StringUtils.isEmpty(origin)) {
// 支持任何域名的跨域调用 且 支持带cookie(是被调用方域名的cookie,而不是调用方的cookie)
res.addHeader("Access-Control-Allow-Origin", origin);
}
// 指定允许的域,带cookie时,origin必须是全匹配,不能使用 *
// res.addHeader("Access-Control-Allow-Origin","http://localhost:8081");
// 允许所有域,但不能满足带 cookie 的跨域请求
// res.addHeader("Access-Control-Allow-Origin","*"); // 支持所有header
res.addHeader("Access-Control-Allow-Headers","*"); // 指定允许的方法
// res.addHeader("Access-Control-Allow-Methods","GET");
// 允许所有方法
res.addHeader("Access-Control-Allow-Methods", "*");
// 允许浏览器在一个小时内,缓存跨域访问信息(即上面三个信息)
res.addHeader("Access-Control-Max-Age", "3600"); // 允许证书,启用 cookie
res.addHeader("Access-Control-Allow-Credentials", "true"); chain.doFilter(request, response); } @Override
public void destroy() {
// TODO Auto-generated method stub } }

      4、在localhost下,增加一个cookie,方法是:浏览器打开localhost:8080,按F12打开控制台,输入:document.cookie="cookie1=test"

        

      5、浏览器访问http://a.com:8080/test-ajax-cross/static/test3.html#,点击发送getCookie请求,可以看到控制台报错

        

      6、解决,响应是"Access-Control-Allow-Origin",不能用"*",通配符代替。在请求头中我们可以看到有Origin字段,后端服务可以获取这个字段的值,然后设置未允许

        

        编辑后端过滤器

      7、浏览器访问http://a.com:8080/test-ajax-cross/static/test3.html#,点击发送getCookie请求,可以看到已经能正常返回

    B、带自定义头的ajax请求,跨域问题

      1、编辑test3.html页面,增加一个带自定义头的ajax请求

 <!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="jquery-1.11.3.min.js" type="text/javascript"></script>
</head>
<body>
<h2>测试服务端解决跨域问题</h2>
<a href="#" onclick="get()">发送get请求</a>
<a href="#" onclick="getCookie()">发送getCookie请求</a>
<a href="#" onclick="getHeader()">发送getHeader请求</a>
</body>
<script type="text/javascript">
function get(){
$.getJSON("http://localhost:8080/test-ajax-cross/test/get").then(function(result){
console.log(result);
$("body").append("<br>" + JSON.stringify(result));
});
}
// 测试带上cookie的请求能否跨域
function getCookie(){
$.ajax({
url: "http://localhost:8080/test-ajax-cross/test/getCookie",
xhrFields:{
// 带上证书,发送 AJAX 请求时带上 cookie
withCredentials:true
},
// 允许跨域
crossDomain: true,
success:function(result){
console.log(result);
$("body").append("<br>" + JSON.stringify(result));
}
});
}
// 测试带上不同header的请求能否跨域
function getHeader(){
$.ajax({
url: "http://localhost:8080/test-ajax-cross/test/getHeader",
headers:{
"x-header1":"AAA"
},
beforeSend:function(xhr){
xhr.setRequestHeader("x-header2","BBB")
},
success:function(result){
console.log(result);
$("body").append("<br>" + JSON.stringify(result));
}
});
}
</script>
</html>

      2、增加自定义头的ajax请求,接受请求的方法

 @RequestMapping(value = "/getHeader", method = RequestMethod.GET)
@ResponseBody
public Map getHeader(
@RequestHeader("x-header1") String header1,
@RequestHeader("x-header2") String header2) {
System.out.println("TestController getHeader()");
Map<String, Object> map = new HashMap();
map.put("data", "getHeader" + header1+header2);
return map;
}

      3、浏览器访问http://a.com:8080/test-ajax-cross/static/test3.html#,点击发送getHeader请求,可以看到已经能正常返回

        打开F12,查看Network中,发现浏览器做了2次请求,第一是请求method是options,就是所谓的预检(preflight)请求,才发送真正的请求。且第一次OPTIONS请求,headers是不会带到后端服务器上来

        

    附:完整版过滤器

 package com.test.ajax.cross.filter;

 import java.io.IOException;

 import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.springframework.http.HttpStatus;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMethod; /**
* 服务端解决跨域
*
* @author h__d
*
*/
public class CrossFilter implements Filter { @Override
public void init(FilterConfig filterConfig) throws ServletException {
// TODO Auto-generated method stub } @Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse res = (HttpServletResponse) response; HttpServletRequest req = (HttpServletRequest) request; // 支持所有域
String origin = req.getHeader("Origin");
if (!StringUtils.isEmpty(origin)) {
// 支持任何域名的跨域调用 且 支持带cookie(是被调用方域名的cookie,而不是调用方的cookie)
res.addHeader("Access-Control-Allow-Origin", origin);
}
// 指定允许的域,带cookie时,origin必须是全匹配,不能使用 *
// res.addHeader("Access-Control-Allow-Origin","http://localhost:8081");
// 允许所有域,但不能满足带 cookie 的跨域请求
// res.addHeader("Access-Control-Allow-Origin","*"); // 允许所有header,但是第一次OPTIONS请求,headers是不会带过来的
// String headers = req.getHeader("Access-Control-Allow-Headers");
// if (!StringUtils.isEmpty(headers)) {
// // 允许所有header,自定义头访问出现问题
// res.addHeader("Access-Control-Allow-Headers", headers);
// }
// 支持所有header
res.addHeader("Access-Control-Allow-Headers","*"); // 指定允许的方法
// res.addHeader("Access-Control-Allow-Methods","GET");
// 允许所有方法
res.addHeader("Access-Control-Allow-Methods", "*");
// 允许浏览器在一个小时内,缓存跨域访问信息(即上面三个信息)
res.addHeader("Access-Control-Max-Age", "3600");
// 允许证书,启用 cookie
res.addHeader("Access-Control-Allow-Credentials", "true"); // 第一次OPTIONS请求,headers是不会带过来的
if (req.getMethod().equals(RequestMethod.OPTIONS.name())) {
res.setStatus(HttpStatus.OK.value());
} chain.doFilter(request, response); } @Override
public void destroy() {
// TODO Auto-generated method stub } }

    

解决方案三(被调用方支持跨域-Spring框架解决)

  Spring Framework 4.2 GA为CORS提供了第一类支持。所以springMVC的版本要在4.2或以上版本才支持@CrossOrigin,@CrossOrigin可以用在类上和方法上

  @CrossOrigin的作用就相当于上例中的CrossFilter.class

 @Controller
@RequestMapping("/test")
@CrossOrigin
public class TestController {
...
}

解决方案三(被调用方支持跨域-代理方案解决)

  在很多项目的架构中,都是使用了代理的机制,浏览器访问nginx,nginx访问tomcat服务,如下图:

    

  那么在上例中可以想到,跨域请求是直接从浏览器发送到被调用方,被调用方在响应头里增加相关信息,可以在tomcat应用服务程序中加,也可以在nginx/apache等代理中加

  相当于nginx/apache做了过滤器的功能

  流程:

  1、修改页面访问地址,统一请求到http://b.com/test-ajax-cross,让请求经过代理服务器nginx/apache

  2、修改代理服务器nginx/apache配置。

  Nginx配置如下:

 server{
listen 80;
server_name b.com; location /{
proxy_pass http://localhost:8080/; add_header Access-Control-Allow-Methods *;
add_header Access-Control-Max-Age 3600;
add_header Access-Control-Allow-Credentials true; add_header Access-Control-Allow-Origin $http_origin;
add_header Access-Control-Allow-Headers *; # 预检请求,直接返回正确
if ($request_method = OPTIONS){
return 200;
}
}
}

  Apache配置

  • 修改conf/httpd.conf找到LoadModule vhost_alias_module module/mod_vhost_alias.so取消注释
  • 修改conf/httpd.conf找到LoadModule proxy_module module/mod_ proxy.so取消注释
  • 修改conf/httpd.conf找到LoadModule proxy_http_module module/mod_ proxy_http.so取消注释
  • 修改conf/httpd.conf找到LoadModule headers_module module/mod_ headers.so取消注释
  • 修改conf/httpd.conf找到LoadModule rewrite_module module/mod_ rewrite.so取消注释
  • 修改conf/httpd.conf找到Include conf/extra/httpd-vhosts.conf取消注释
  • 修改conf/extra/httpd-vhosts.conf在最后面增加下面的内容即可
<VirtualHost *:80>
ServerName b.com
ErrorLog "logs/b.com-error.log"
CustomLog "logs/b.com-access.log" common
ProxyPass / http://localhost:8080/ # 把请求头的origin值返回到Access-Control-Allow-Origin字段
Header always set Access-Control-Allow-Origin "expr=%{req:origin}" Header always Access-Control-Allow-Headers "*" Header always set Access-Control-Allow-Methods "*";
Header always set Access-Control-Max-Age "3600";
Header always set Access-Control-Allow-Credentials ""true"; # 处理预检命令OPTIONS,直接返回204
RewriteEngine On
RewriteCond %{REQUEST_METHOD}OPTIONS
RewriteRule ^(.*)$"/" [R=204,L]
</VirtualHost>

解决方案四(被调用方支持跨域)

  使用nginx进行反向代理,结构如下

  

  当页面要进行跨域请求是,可以由nginx对url进行过滤,跨域的url请求,转发到其他服务器上,进行处理,前端无感知

  nginx配置如下:

 server{
listen 80;
server_name a.com; location /{
proxy_pass http://127.0.0.1:8080/;
} location /test-ajax-cross/test{
# 也可以转发到其他域名,进行跨域请求
proxy_pass http://localhost:8080/test-ajax-cross/test;
}
}

  1、编辑一个页面test4.html

 <!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="jquery-1.11.3.min.js" type="text/javascript"></script>
</head>
<body>
<h2>测试服务端解决跨域问题</h2>
<a href="#" onclick="get()">发送get请求</a>
<a href="#" onclick="getCookie()">发送getCookie请求</a>
<a href="#" onclick="getHeader()">发送getHeader请求</a>
</body>
<script type="text/javascript">
function get(){
$.getJSON("http://a.com/test-ajax-cross/test/get").then(function(result){
console.log(result);
$("body").append("<br>" + JSON.stringify(result));
});
}
// 测试带上cookie的请求能否跨域
function getCookie(){
$.ajax({
url: "http://a.com/test-ajax-cross/test/getCookie",
xhrFields:{
// 带上证书,发送 AJAX 请求时带上 cookie
withCredentials:true
},
// 允许跨域
crossDomain: true,
success:function(result){
console.log(result);
$("body").append("<br>" + JSON.stringify(result));
}
});
}
// 测试带上不同header的请求能否跨域
function getHeader(){
$.ajax({
url: "http://a.com/test-ajax-cross/test/getHeader",
headers:{
"x-header1":"AAA"
},
beforeSend:function(xhr){
xhr.setRequestHeader("x-header2","BBB")
},
success:function(result){
console.log(result);
$("body").append("<br>" + JSON.stringify(result));
}
});
}
</script>
</html>

  2、配置nginx做反向代理

  3、使用地址 http://a.com/test-ajax-cross/static/test4.html#,进行访问,测试是能正常访问的

  Apache反向代理实现

  配置如下:

 <VirtualHost *:80>
ServerName a.com
ErrorLog "logs/a.com-error.log"
CustomLog "logs/a.com-access.log" common
ProxyPass / http://127.0.0.1:8080/
ProxyPass /ajaxserverapache http://localhost:8080/test-ajax-cross/test
</VirtualHost>

  

  

【JS】AJAX跨域-被调用方与调用方解决方案(二)的更多相关文章

  1. JS Ajax跨域访问

    js ajax跨域访问报"No 'Access-Control-Allow-Origin' header is present on the requested resource 如果请求的 ...

  2. js ajax跨域

    一般情况后台返回... 也就是说,无论数据本身是什么数据类型,数据,对象,都是以字符串形式返回的. 如何把字符串化成相应对象. 如: var s='{"left":100}' co ...

  3. js Ajax跨域调用JSON并赋值全局变量

    //跨域调用JSON <script type="text/javascript"> function _callback(obj) { alert(obj); } j ...

  4. js ajax跨域调用

    正常使用ajax调用java.com $.ajax({ type: 'get', url: 'http://www.java.com/custinfo?id=888', dataType: 'json ...

  5. Ajax跨域(jsonp) 调用JAVA后台

    1. JSONP定义    JSONP是英文JSON with Padding的缩写,是一个非官方的协议.它允许在服务器端生成script tags返回至客户端,通过javascript callba ...

  6. ajax跨域实现api 接口调用

    背景: 想实现跨域去调用接口, 然后同时支持下次调用,能够带cookie信息过来,同时支持来自多个源头的域名的跨域调用. 1.这样支持来自所有域名的跨域调用: 不支持跨域是,浏览器报错: 在api接口 ...

  7. JS AJAX 跨域

    原因: 浏览器的同源策略,不允许AJAX 访问 其他接口 协议,域名,端口 一个不同 就跨域了  http 端口(80) https(443) 可以跨域的三个标签: 1. img : 打点统计,没有浏 ...

  8. js ajax跨域被阻止 CORS 头缺少 'Access-Control-Allow-Origin'(转)

    今天ajax请求域名的时候出现 已阻止跨源请求:同源策略禁止读取位于 http://www.zuimeimami.com*****的远程资源.(原因:CORS 头缺少 'Access-Control- ...

  9. 解决 js ajax跨域访问报“No 'Access-Control-Allow-Origin' header is present on the requested resource.”错误

    参考页面:https://blog.csdn.net/idomyway/article/details/79572973 如果请求的是PHP页面: header("Access-Contro ...

  10. js Ajax 跨域请求

    一.使用jsonp的方式(只支持get请求) 二.使用cors的方式(支持HTTP的大部分请求方式) 三.apache的转发(修改服务器配置) 没有试验,暂时不详细写!

随机推荐

  1. ElasticSearch(十一):Spring Data ElasticSearch 的使用(一)

    1.环境准备 我本地使用的环境为: 虚拟机版本:Centos 7.3 两台   IP 分别为:192.168.56.12, 192.168.56.13 Elasticsearch版本:6.4.0  ( ...

  2. java.sql.SQLException: The server time zone value '�й���׼ʱ��' is unrecognized or represents more than one time zone.

    java.sql.SQLException: The server time zone value '�й���׼ʱ��' is unrecognized or represents more tha ...

  3. 《Coderxiaoban团队》团队作业5:项目需求分析改进与系统设计

    实验八 <Coderxiaoban团队>团队作业5:项目需求分析改进与系统设计 项目 内容 这个作业属于哪个课程 任课教师博客主页链接 这个作业的要求在哪里 团队作业5:项目需求分析改进与 ...

  4. js判断是否第一次访问跳转

    今天分享一套关于Js劫持代码,进行判断第一次访问进行跳转,仅供大家参考学习! 未加密: if (c.indexOf('isfirstvisited=false') != -1) { } else { ...

  5. 最小圆覆盖(洛谷 P1742 增量法)

    题意:给定N个点,求最小圆覆盖的圆心喝半径.保留10位小数点. N<1e5: 思路:因为精度要求较高,而且N比较大,所以三分套三分的复杂度耶比较高,而且容易出错. 然是写下增量法吧. 伪代码加深 ...

  6. [Flutter + Firebase] Enable Firebase for Flutter

    Anroid Firebase Project setup: 1. In firebase console, cerate a Android app setup you can find in co ...

  7. 洛谷 P2746 [USACO5.3]校园网 Network of Schools 题解

    Tarjan 模板题 第一问就是缩点之后看有多少个入度为零的点就好了. 第二问是在缩点后将每个点的入度和出度都求出(只要有入度或出度就置为1),然后比较哪个有值的多,将多的作为答案输出.原因是由题可得 ...

  8. telegraf 学习二 几个概念

    telegraf 自身包好了自己处理metrics 的数据模型,以及出炉方法 metrics Telegraf指标是用于在处理期间对数据建模的内部表示.这些指标完全基于InfluxDB的数据模型,包含 ...

  9. 洛谷 P1351 联合权值 题解

    P1351 联合权值 题目描述 无向连通图 \(G\) 有 \(n\) 个点,\(n-1\) 条边.点从 \(1\) 到 \(n\) 依次编号,编号为 \(i\) 的点的权值为 \(W_i\)​,每条 ...

  10. vue子组件与子组件之前传值-----最简单办法

    1.在main.js中定义一个值(红色为重点) new Vue({ el: '#app', data: { Bus: new Vue() }, router, store, render: h =&g ...