五种方式彻底解决跨域问题

1. 跨域原因&代码示例

跨域原因产生:在当前域名请求网站中,默认不允许通过ajax请求发送其他域
名。

服务器端代码如下,为网站a.com

@WebServlet("/FromServlet")
public class FromServlet extends HttpServlet {
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		String userName = req.getParameter("userName");
		JSONObject jsonObject = new JSONObject();
		jsonObject.put("userName", userName);
		resp.getWriter().println(jsonObject.toJSONString());
	}
}

前端代码,为网站 b.com/test.jsp,现要试图通过ajax来访问a.com上的数据

<script type="text/javascript">
	$(document).ready(function() {
		$.ajax({
			type : "GET",
			async : false,
			url : "http://a.com/FromServlet?userName=644064",
			dataType : "json",
			success : function(data) {
				alert(data["userName"]);
			},
			error : function() {
				alert('fail');
			}
		});
	});
</script>

此时 b.com/test.jsp 就会出现跨域问题:XMLHttpRequest cannot load

2. 解决方案

2.1 使用后台response添加header

后台response添加header,支持所有网站

response.setHeader("Access-Control-Allow-Origin", "*"); 

下面是在SpringBoot项目中利用请求头配置的全局跨域:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

@Configuration
public class CorsConfig {
    /**
     * 允许跨域调用的过滤器
     */
    @Bean
    public CorsFilter corsFilter() {
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true); // 允许cookies跨域
        config.addAllowedOrigin("*");// #允许向该服务器提交请求的URI,*表示全部允许,在SpringMVC中,如果设成*,会自动转成当前请求头中的Origin
        config.addAllowedHeader("*");// #允许访问的头信息,*表示全部
        config.setMaxAge(7200L);// 预检请求的缓存时间(秒),即在这个时间段里,对于相同的跨域请求不会再预检了
        config.addAllowedMethod("*");// 允许提交请求的方法,*表示全部允许
        source.registerCorsConfiguration("/**", config);
        return new CorsFilter(source);
    }
}

2.2 使用JSONP

在同源策略下,在某个服务器下的页面是无法获取到该服务器以外的数据的,即一般的ajax是不能进行跨域请求的。但 img、iframe 、script等标签是个例外,这些标签可以通过src属性请求到其他服务器上的数据。利用 script标签的开放策略,我们可以实现跨域请求数据,当然这需要服务器端的配合。 Jquery中ajax 的核心是通过 XmlHttpRequest获取非本页内容,而jsonp的核心则是动态添加 <script>标签来调用服务器提供的 js脚本。
 
当我们正常地请求一个JSON数据的时候,服务端返回的是一串 JSON类型的数据,而我们使用 JSONP模式来请求数据的时候服务端返回的是一段可执行的 JavaScript代码。因为jsonp 跨域的原理就是用的动态加载 script的src ,所以我们只能把参数通过 url的方式传递,所以jsonp的 type类型只能是get !
示例:

$.ajax({
    url: 'http://192.168.5.133/demos01', //不同的域
    type: 'GET', // jsonp模式只有GET 是合法的
    data: {
        'userName': '小明',
        'ageAge':18
    },
    dataType: 'jsonp', // 数据类型
    jsonp: 'backfunc', // 指定回调函数名,与服务器端接收的一致,并回传回来
})

其实jquery 内部会转化成

http://192.168.5.133/demos01?
backfunc=jQuery2030038573939353227615_1402643146875&action=aaron

然后动态加载

<script type="text/javascript"
	src="http://192.168.5.133/demos01?
	backfunc= jQuery2030038573939353227615_1402643146875&action=aaron">
</script>

然后后端就会执行backfunc(传递参数 ),把数据通过实参的形式发送出去。

使用JSONP 模式来请求数据的整个流程:客户端发送一个请求,规定一个可执行的函数名(这里就是 jQuery做了封装的处理,自动帮你生成回调函数并把数据取出来供success属性方法来调用,而不是传递的一个回调句柄),服务器端接受了这个 backfunc函数名,然后把数据通过实参的形式发送出去(在jquery 源码中, jsonp的实现方式是动态添加<script>标签来调用服务器提供的 js脚本。jquery 会在window对象中加载一个全局的函数,当 <script>代码插入时函数执行,执行完毕后就 <script>会被移除。同时jquery还对非跨域的请求进行了优化,如果这个请求是在同一个域名下那么他就会像正常的 Ajax请求一样工作。)

后端代码:

@WebServlet("/demos01")
public class FromUserServlet extends HttpServlet {

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		doPost(req, resp);
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		resp.setCharacterEncoding("UTF-8");
		// resp.setHeader("Access-Control-Allow-Origin", "*");
		 String userName = req.getParameter("userName");
		 String userAge = req.getParameter("userAge");
		 System.out.println(userName + "----" + userAge+"---"+req.getMethod());
		// JSONObject JSONObject1 = new JSONObject();
		// JSONObject1.put("success", "添加成功!");
		// resp.getWriter().write("callbackparam(" + JSONObject1.toJSONString()
		// + ")");

		try {
			resp.setContentType("text/plain");
			resp.setHeader("Pragma", "No-cache");
			resp.setHeader("Cache-Control", "no-cache");
			resp.setDateHeader("Expires", 0);
			PrintWriter out = resp.getWriter();
			JSONObject resultJSON = new JSONObject(); // 根据需要拼装json
			resultJSON.put("result", "content");
			String jsonpCallback = req.getParameter("jsonpCallback");// 客户端请求参数
			out.println(jsonpCallback + "(" + resultJSON.toJSONString() + ")");// 返回jsonp格式数据
			out.flush();
			out.close();
		} catch (Exception e) {
			// TODO: handle exception
		}
	}
}

注意:JSONP只支持get请求不支持psot请求

2.3 后台Http请求转发

使用HttpClient进行转发,HttpClient的用法可以参考 这个文章中的6.2部分,此处做转发的思路是,如果B网站想要访问A网站的资源那就在B网站的服务端中使用HttpClient请求到A网站的服务端。

2.4 使用接口网关Nginx

即使用nginx搭建的网关

配置一:利用反向代理统一域名

结合文章开头的代码示例,nginx配置如下:

server {
	listen       80;
	server_name  test.com;
	location /A {
		proxy_pass  http://a.com;
		index  index.html index.htm;
	}
	location /B {
		proxy_pass  http://b.com;
		index  index.html index.htm;
	}
}

更改ajax的请求地址,注意此时的页面路径已经从b.com/test.jsp变成了 test.com/B/test.jsp

<script type="text/javascript">
	$(document).ready(function() {
		$.ajax({
			type : "GET",
			async : false,
			url : "http://test.com/A/FromServlet?userName=644064",
			dataType : "json",
			success : function(data) {
				alert(data["userName"]);
			},
			error : function() {
				alert('fail');
			}
		});
	});
</script>

这样我们网站就是http://test.com/B/test.jsp然后其中访问A网站的url变成了"http://test.com/A/FromServlet?userName=644064",域名都变成了test.com,跨域问题解决。

配置二:不同域名下的静态资源访问

需求mrstar.site 需要访问test.com下的静态资源

server {
	listen       80;
	server_name  test.com;
	location / {
		proxy_pass  D:/Users/Administrator/IdeaProjects/example-project/;
		index  index.html;
	}
	#静态资源,包括系统所需要的图片,js、css等静态资源
	location /static/plugins/ {
		alias D:/Users/Administrator/IdeaProjects/example-project/plugins/;
		add_header Access-Control-Allow-Origin http://mrstar.site;
		add_header Access-Control-Allow-Credentials true;
		add_header Access-Control-Allow-Methods GET;
	}
}

配置三:使用map指令

#学成网媒体服务代理
	map $http_origin $origin_list{
		default http://face.a.com;
		"~http://face.a.com" http://face.a.com;
		"~http://img.a.com" http://img.a.com;
	}
	server {
		listen 80;
		server_name video.a.com;
		location /video {
			proxy_pass http://video_server_pool;
			add_header Access-Control-Allow-Origin $origin_list;
			#add_header Access-Control-Allow-Origin *;
			#add_header Access-Control-Allow-Origin *;
			add_header Access-Control-Allow-Credentials true;
			add_header Access-Control-Allow-Methods GET;
		}
	}

其中如果需要配置跨域的域名只有一个则可直接配置为:

add_header Access-Control-Allow-Origin "http://face.a.com";

上面的配置只允许map中配置的域名访问,如果要配置允许所有访问如下:

add_header Access-Control-Allow-Origin "*";

但这样做并不安全。

2.5 使用SpringCloud网关

SpringCloud中 zuul 和springboot 要同时配置才能实现网关处理跨域
SpringBoot代码上面2.1中的全局配置代码即可使用
zull网关yaml如下

zuul:
  #需要忽略的头部信息,不在传播到其他服务
  sensitive-headers: Access-Control-Allow-Origin
  ignored-headers: Access-Control-Allow-Origin,H-APP-Id,Token,APPToken
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值