js ajax 跨域问题 解决方案[通俗易懂]

js ajax 跨域问题 解决方案[通俗易懂]什么是跨域问题?跨域问题来源于JavaScript的”同源策略”,即只有协议+主机名+端口号(如存在)相同,则允许相互访问。也就是说JavaScript只能访问和操作自己域下的资源,不能访问和操作其他域下的资源。跨域问题是针对JS和ajax的,html本身没有跨域问题。查看浏览器开发者工具Console报错:Failedtoloadhttp://a.a.com:8080/A/FromServlet?userName=123:No’Access-Control-Allow-Origi.

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

什么是跨域问题?

跨域问题来源于JavaScript的”同源策略”,即只有 协议+主机名+端口号 (如存在)相同,则允许相互访问。也就是说JavaScript只能访问和操作自己域下的资源,不能访问和操作其他域下的资源。跨域问题是针对JS和ajax的,html本身没有跨域问题。

查看浏览器开发者工具Console报错:

Failed to load http://a.a.com:8080/A/FromServlet?userName=123: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘http://b.b.com:8080’ is therefore not allowed access.

http://www.abc.com/a/b 调用 http://www.abc.com/d/c(非跨域)

http://www.abc.com/a/b 调用 http://www.def.com/a/b (跨域:域名不一致)

http://www.abc.com:8080/a/b 调用 http://www.abc.com:8081/d/c (跨域:端口不一致)

http://www.abc.com/a/b 调用 https://www.abc.com/d/c (跨域:协议不同)

请注意:localhost和127.0.0.1虽然都指向本机,但也属于跨域。

 

 

跨域问题怎么解决?

1、响应头添加Header允许访问

2、jsonp 只支持get请求不支持post请求

3、httpClient内部转发

4、使用接口网关——nginx、springcloud zuul   (互联网公司常规解决方案)

 

解决方式1:响应头添加Header允许访问

跨域资源共享(CORS)Cross-Origin Resource Sharing

这个跨域访问的解决方案的安全基础是基于”JavaScript无法控制该HTTP头”

它需要通过目标域返回的HTTP头来授权是否允许跨域访问。

response.addHeader(‘Access-Control-Allow-Origin:*’);//允许所有来源访问
response.addHeader(‘Access-Control-Allow-Method:POST,GET’);//允许访问的方式

js ajax 跨域问题 解决方案[通俗易懂]

 

解决方式2:jsonp 只支持get请求不支持post请求

用法:①dataType改为jsonp     ②jsonp : “jsonpCallback”————发送到后端实际为http://a.a.com/a/FromServlet?userName=644064&jsonpCallback=jQueryxxx     ③后端获取get请求中的jsonpCallback    ④构造回调结构

$.ajax({
	type : "GET",
	async : false,
	url : "http://a.a.com/a/FromServlet?userName=644064",
	dataType : "jsonp",//数据类型为jsonp  
	jsonp : "jsonpCallback",//服务端用于接收callback调用的function名的参数
	success : function(data) {
		alert(data["userName"]);
	},
	error : function() {
		alert('fail');
	}
});

//后端
String jsonpCallback = request.getParameter("jsonpCallback");
//构造回调函数格式jsonpCallback(数据)
resp.getWriter().println(jsonpCallback+"("+jsonObject.toJSONString()+")");

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.10.46/demo/test.jsp',        //不同的域
    type: 'GET',                                                        // jsonp模式只有GET 是合法的
    data: {
        'action': 'aaron'
    },
    dataType: 'jsonp',                                              // 数据类型
    jsonp: 'jsonpCallback',                                     // 指定回调函数名,与服务器端接收的一致,并回传回来
})

其实jquery 内部会转化成

http://192.168.10.46/demo/test.jsp?jsonpCallback=jQuery202003573935762227615_1402643146875&action=aaron

然后动态加载

<script type=”text/javascript”src=”http://192.168.10.46/demo/test.jsp?jsonpCallback= jQuery202003573935762227615_1402643146875&action=aaron”></script>

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

  使用JSONP 模式来请求数据的整个流程:客户端发送一个请求,规定一个可执行的函数名(这里就是 jQuery做了封装的处理,自动帮你生成回调函数并把数据取出来供success属性方法来调用,而不是传递的一个回调句柄),服务器端接受了这个 jsonpCallback函数名,然后把数据通过实参的形式发送出去

 

(在jquery 源码中, jsonp的实现方式是动态添加<script>标签来调用服务器提供的 js脚本。jquery 会在window对象中加载一个全局的函数,当 <script>代码插入时函数执行,执行完毕后就 <script>会被移除。同时jquery还对非跨域的请求进行了优化,如果这个请求是在同一个域名下那么他就会像正常的 Ajax请求一样工作。)

 

解决方式3:httpClient内部转发

实现原理很简单,若想在B站点中通过Ajax访问A站点获取结果,固然有ajax跨域问题,但在B站点中访问B站点获取结果,不存在跨域问题,这种方式实际上是在B站点中ajax请求访问B站点的HttpClient,再通过HttpClient转发请求获取A站点的数据结果。但这种方式产生了两次请求,效率低,但内部请求,抓包工具无法分析,安全。

$.ajax({
			type : "GET",
			async : false,
			url : "http://b.b.com:8080/B/FromAjaxservlet?userName=644064",
			dataType : "json",
			success : function(data) {
				alert(data["userName"]);
			},
			error : function() {
				alert('fail');
			}
		});

@WebServlet("/FromAjaxservlet")
public class FromAjaxservlet extends HttpServlet{
	
	
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		try {
			//创建默认连接
			CloseableHttpClient httpClient = HttpClients.createDefault();
			//创建HttpGet对象,处理get请求,转发到A站点
			HttpGet httpGet = new HttpGet("http://a.a.com:8080/A/FromServlet?userName="+req.getParameter("userName")); 
                        //执行
			CloseableHttpResponse response = httpClient.execute(httpGet);
			int code = response.getStatusLine().getStatusCode();
			//获取状态
			System.out.println("http请求结果为:"+code);
			if(code == 200){
                                //获取A站点返回的结果
				String result = EntityUtils.toString(response.getEntity());
				System.out.println(result);
                                //把结果返回给B站点
				resp.getWriter().print(result);
			}
			response.close();
			httpClient.close();
		} catch (Exception e) {
		}
	}
}

解决方式4:使用nginx搭建企业级接口网关方式

www.a.a.com不能直接请求www.b.b.com的内容,可以通过nginx,根据同域名,但项目名不同进行区分。什么意思呢?这么说可能有点抽象。假设我们公司域名叫www.nginxtest.com

当我们需要访问www.a.a.com通过www.nginxtest.com/A访问,并通过nginx转发到www.a.a.com

当我们需要访问www.b.b.com通过www.nginxtest.com/B访问,并通过nginx转发到www.a.a.com

我们访问公司的域名时,是”同源”的,只是项目名不同,此时项目名的作用只是为了区分,方便转发。如果你还不理解的话,先看看我是怎么进行配置的:

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

我们访问以www.nginxtest.com开头且端口为80的网址,nginx将会进行拦截匹配,若项目名为A,则分发到a.a.com:81。实际上就是通过”同源”的域名,不同的项目名进行区分,通过nginx拦截匹配,转发到对应的网址。整个过程,两次请求,第一次请求nginx服务器,第二次nginx服务器通过拦截匹配分发到对应的网址。

 

解决方式5:使用Spring Cloud zuul接口网关

 

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/172571.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 记忆化搜索(Memory Search)

    记忆化搜索(Memory Search)Question输入n,符合要求的序列为:第一个数为n,第二个数不大于n,从第三个数起小于前两个数的差的绝对值,后面以此类推。求有多少种序列?(数据:n最大为1000)Sampleinput:4/output:7input:5/output:14input:6/output:26Hintn为4时有如下序列:4142434441141242…

    2022年7月26日
    11
  • 浅谈一下学Java和python哪个好(个人观点)「建议收藏」

    浅谈一下学Java和python哪个好(个人观点)「建议收藏」其实这是一篇容易引起撕逼的文章,java是一种覆盖范围广,可跨平台的编程语言,python也是近几年火遍全世界的语言。先说结论,java是基础,另外一个是加分项,我仅代表我个人观点,为了祖国和谐,人民安康,请各位看官尽量理性讨论。java和python哪个好?很多朋友碰到了一个很共性的问题,那就是编程语言的选择。虽然Python这两年确实很火,但如果你的学历不是硕士以上,数学能力也一般,就无脑选java,不要选择Python作为就业方向。单单只会Python这门语言的是找不到工作的!Pyth

    2022年7月9日
    24
  • 用户访问路径分析_访问路径是什么意思

    用户访问路径分析_访问路径是什么意思1971年,在英国伦敦召开的国际园林艺术研讨会上,迪斯尼乐园的路径设计获得世界最佳设计奖。可是,你知道吗?这条路径却并非出自某个设计大师之手,完全是游人"自行设计"(实际就…

    2022年8月24日
    7
  • lldp协议代码阅读_软件实现LLDP协议HaneWin LLDP Service[通俗易懂]

    lldp协议代码阅读_软件实现LLDP协议HaneWin LLDP Service[通俗易懂]这是软件实现LLDP协议HaneWinLLDPService,软件实现基于IEEE802.1AB标准的链路层发现协议LLDP代理。链路层发现协议(LLDP)是一种协议为物理拓扑发现在802Lan。相邻站发现并存储用于检索的LLDP代理由基于SNMP网络管理系统。软件介绍软件实现LLDP协议HaneWinLLDPService软件基础上的链路层发现协议符合IEEE…

    2022年5月25日
    45
  • js判断一个字符串是否包含某个字符_正则不包含某个字符串

    js判断一个字符串是否包含某个字符_正则不包含某个字符串Q2:JS判断字符串变量是否含有某个字串的实现方法JS判断字符串变量是否含有某个字串的实现方法varCts=”bblText”;if(Cts.indexOf(“Text”)>0){alert(Cts中包含Text字符串);}indexOf用法:返回String对象内第一次出现子字符串的字符位置。strObj.indexOf(subString[,startIndex])参数s…

    2022年10月7日
    1
  • VS 2017 产品密钥

    VS 2017 产品密钥个人分类 nbsp vs2010Visual VS2017 企业版 Enterprise 注册码 NJVYC BMHX2 G77MM 4XJMR 6Q8QFVisualS VS2017 专业版 Professional 激活码 key KBJFW NXHK6 W4WJM CRMQB G3CDH 启动 VS 之后 在菜单栏有个帮助的下拉框 选择注册产品

    2025年7月16日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号