Ajax跨域访问问题的分析与解决
2020-02-01常艳
常艳
(晋城技师学院 山西省晋城市 048000)
随着计算机网络的发展与普及,网站设计与开发是现在非常热门的一个行业。前后端分离开发方式已成为网站开发的主流开发方式。前后端代码分离导致了Ajax 请求时的跨域访问问题,本文就Ajax 访问机制、Ajax 跨域访问问题产生的原因以及解决方案进行了深入的分析与研究。
1 Ajax访问机制
在传统的web 开发技术中,加载或者刷新数据需要重新请求页面,会造成网页进行重新加载,从而刷新整个页面。Ajax(Asynchronous JavaScript & XML)是一种新的web 开发技术,通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的数据进行更新,使得用户体验更好。
Ajax 的请求步骤如下:
(1)创建XMLHttpRequest 对象,也就是创建一个异步调用对象;
(2)创建一个新的HTTP 请求,并指定该HTTP 请求的方式、URL 及验证信息;
(3)设置响应HTTP 请求状态变化的函数;
(4)发送HTTP 请求;
(5)获取异步调用返回的数据;
(6)使用JavaScript 和DOM 实现局部刷新。
2 前后端分离开发技术
现在前后端分离开发技术已成为网站开发的主流发展方向。前后端分离开发即网站开发前台代码(HTML 和JavaScript)和后台代码(本文中采用PHP)独立开发,前后端代码可放在同一个服务器上,也可分布于不同的服务器上。后端代码负责提供数据接口(以下简称API)用于完成数据处理和交互,前台使用Ajax 技术请求后台API,来实现网站功能、以及数据交互。前后端分离开发技术可以使得前后端代码同时开发,提高开发效率,同时易于排错,扩展性好。
3 Ajax跨域访问问题产生的原因
3.1 域名不一致
前端页面的域名和页面中访问的API 的域名不一致,导致页面无法正常使用API。例如,开发初期前端页面多在本机PC 上进行开发和测试,那么访问前端页面的URL 为http://localhost/ems/yjglxt/login.html,那么前端域名为localhost,后台接口部署于服务器上,API 访问URL 为http://39.106.33.124/ems/public/index.php/login,那么API 的域名为39.106.33.124,前端和后端域名不一致,导致Ajax 访问时发生跨域访问问题,不能正常访问API 接口。
3.2 端口不一致
前后端代码位于同一台服务器或PC,域名一致,但是访问端口不一致,导致Ajax 跨域访问问题,无法正常访问接口。例如访问前端页面的URL 为http:// 39.106.33.124/ems/yjglxt/login.html,那么前端域名是39.106.33.124,端口默认为80。访问API 的URL 为http://39.106.33.124:8080/ems/public/index.php/login,那么后端域名为39.106.33.124,端口为8080,前后端域名一致,但是端口不一致,同样会导致Ajax 跨域访问问题,不能正常访问API 接口。
4 Ajax跨域访问带来的问题
4.1 报错
如果前后端域名不一致或前后端访问端口不一致,在页面中使用Ajax 访问后台API 接口会报错,例如前端访问域名是127.0.0.1,后端API 访问域名是localhost,则会报错,错误信息如下所示:
Access to XMLHttpRequest at 'http://localhost/ems/public/index.php/login' from origin 'http://127.0.0.1' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
4.2 无状态访问
4.2.1 浏览器访问机制
HTTP 是一种无状态的通信协议,它允许将超文本标记语言(HTML)文档从Web 服务器传送到客户端浏览器(以下简称客户端)。HTTP 协议是无状态的协议,一旦数据交换完毕,客户端与服务器端的连接就会关闭,再次交换数据需要建立新的连接。这就意味着服务器无法从连接上跟踪会话。
会话指用户登录网站后的一系列动作,比如浏览商品添加到购物车并购买。会话(session)是web 程序中常用的技术,用来跟踪用户的整个会话。常用的会话跟踪技术是cookie 与session。cookie通过在客户端记录信息确定身份,session 通过在服务器端记录信息确定用户身份。
通常情况下,客户端每次请求服务器时,都会形成一次会话,即创建一个session,服务器端会生成一个sessionId,用来唯一标识一次会话。服务器会把一些登录信息保存在session 中,同时会把sessionId 传给客户端,客户端会把sessionId 存储在cookie 中。客户端下次再请求服务器时,会把cookie 信息传送到服务器,这样服务器会自动获取到客户端cookie 中的sessionId,根据sessionId 去查询服务器端session 中是否保存有客户端的登录信息,如果查询到登录信息,说明客户端已登录;如果查询不到登录信息,说明客户端未登录。
4.2.2 Ajax 跨域访问导致session 失效
cookie 不可跨域名访问,客户端在进行ajax 跨域访问时不会把cookie 值带过去,就会造成服务器获取不到客户端cookie 中的sessionId,因此服务器在每次跨域访问时自动创建一个新的sessionId,这样也等于是开始一次新的会话,导致服务器端无法记住客户端的登录状态。
Ajax 跨域访问后端API 时,会导致每次请求时,服务器都会在客户端的cookie 里设置一个新的sessionId,会将原有的sessionId覆盖,这样会导致客户端每一次请求服务器都相当于是开始一个新的会话,客户端无法记住自己的登录状态,也就是客户端对于服务器来说始终处于未登录状态。
5 Ajax跨域访问问题解决方案
5.1 配置后台PHP的API接口允许跨域访问,方法如下面代码所示
header(“Access-Control-Allow-Origin:*”),表示该API 接口允许所有网站进行跨域访问,这种方法操作简单,但是安全性不高。
这种方法可以使得Ajax 可以正常访问后台API 接口,但是无法解决sessionId 丢失或sessionId 改变的问题,服务器仍然没有办法记住客户端的登录状态。
5.2 允许客户端Ajax跨域访问时,把Cookie带到服务器
在后台数据接口进行修改,加上下面的代码:
5.3 使用其他方式存储客户端的登录数据
5.3.1 使用数据库存储客户端登录数据
Ajax 跨域访问后端API 接口,如果登录成功,服务器端把本次会话的sessionId,以及相关的用户登录信息,保存到数据库中。同时编写代码把sessionId 传送给客户端,客户端使用JS 代码把sessionId 保存到COOKIE 中。
此后客户端每次进行Ajax 跨域访问其他接口时,把sessionId放在header 中发送到服务器,服务器从客户端的header 中获取到sessionId 后,根据sessionId 去数据库查询对应的登录信息,若查询到登录信息,则说明客户端已登录;若查询不到登录信息,则说明客户端处于未登录状态。
5.3.2 使用Redis 存储客户端登录数据
使用数据库存储客户端登录信息的一个弊端是,每次Ajax 跨域访问后端API 时,都要读/写数据库,在服务器并发访问数量较多的情况下,会造成服务器性能下降,用户等待时间变长,用户体验较差,甚至导致服务器崩溃,这种情况下使用数据库存储客户端登录数据变得不可取。
在服务器并发访问量较大的情况下,可以使用Redis 来存储客户端登录数据。Redis(Remote Dictionary Server),即远程字典服务,是一个开源的使用ANSI C 语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库,并提供多种语言的API。Redis 数据库中所有数据都存储在内存中,由于内存的读写速度远快于硬盘和普通数据库,因此把客户端登录数据存储在Redis 中,会使得服务器具有很好的并发性,极大的提升服务器的访问速度和性能。
6 结束语
网站前后端代码分离的开发方法是现阶段主流的网站开发技术,是程序设计与开发的发展方向,但是会导致Ajax 跨域访问问题的发生,使得在网站开发中产生了许多的问题和困难。深入了解浏览器的访问机制,服务器和客户端的交互过程,session 会话的发生机制,以及深入学习Ajax 跨域访问的原因和解决方案,会在以后的网站开发过程中,避免许多可避免错误的发生,起到事半功倍的作用。