一、什么是跨域、为什么会跨域?
我们把问题分解
- 谁出现的跨域?
==》 浏览器!
- 为何出现?
==》 同源策略
- 什么是同源策略?
-
-
- 根据百度百科 同源策略/SOP(Same origin policy)是一种约定,由Netscape公司1995年引入浏览器,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击。
- 所谓同源 ==》指的是 “协议+域名+端口” 三者的相同 只要有一个不同就会导致跨域问题
-
所谓同源指的是:
协议、域名、端口号都相同,只要有一个不相同,那么都是非同源。
浏览器在执行脚本的时候,都会检查这个脚本属于哪个页面,即检查是否同源,只有同源的脚本才会被执行;而非同源的脚本在请求数据的时候,浏览器会报一个异常,提示拒绝访问。
①、http://www.123.com/index.html 调用 http://www.123.com/welcome.jsp 协议、域名、端口号都相同,同源。
②、https://www.123.com/index.html 调用 http://www.123.com/welcome.jsp 协议不同,非同源。
③、http://www.123.com:8080/index.html 调用 http://www.123.com:8081/welcome.jsp 端口不同,非同源。
④、http://www.123.com/index.html 调用 http://www.456.com/welcome.jsp 域名不同,非同源。
⑤、http://localhost:8080/index.html 调用 http://127.0.0.1:8080/welcom.jsp 虽然localhost等同于 127.0.0.1 但是也是非同源的。
同源策略限制的情况:
1、Cookie、LocalStorage 和 IndexDB 无法读取
2、DOM 和 Js对象无法获得
3、AJAX 请求不能发送
注意:对于像 img、iframe、script 等标签的 src 属性是特例,它们是可以访问非同源网站的资源的。
二、跨域解决方案
- JSONP(JSON with Padding)JSONP是一种非官方的跨域解决方案,通过动态创建<script>标签的方式来绕过同源策略的限制。服务器端需要按照约定好的回调函数格式(jsonobject)返回数据,客户端通过定义同名的回调函数来接收数据。
- 优点: 兼容性好,支持老版本浏览器。
- 缺点: 前后端代码耦合度高,只支持GET请求,安全性较差,容易受到XSS攻击。
首先我们要修改 index.jsp 页面的 ajax 请求:
1 $.ajax({2 type:"get",3 async:false,4 url:"http://localhost:8080/JavaWeb01/getPassWordByUserNameServlet?userName=Tom",5 dataType:"jsonp",//数据类型为jsonp6 jsonp:"backFunction",//服务端用于接收callBack调用的function名的参数7 success:function (data) {8 alert(data["passWord"]);9 },
10 error:function () {
11 alert("error");
12 }
13
14 });
注意:我们修改了 dataType 的数据类型为 jsonp,并且新增了 jsop 属性值为 “backFunction”。
接着在 项目的 Servlet 中进行如下修改:
1 @WebServlet("/getPassWordByUserNameServlet")2 public class UserServlet extends HttpServlet{3 @Override4 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {5 String userName = req.getParameter("userName");6 String passWord = null;7 if(userName != null){8 passWord = "123";9 }
10 JSONObject jsonObject = new JSONObject();
11 jsonObject.put("passWord",passWord);
12 //1、第一种方法:*表示支持所有网站访问,也可以额外配置相应网站
13 //resp.setHeader("Access-Control-Allow-Origin", "*");
14
15 //2、第二种方法:jsonp
16 String backFunction = req.getParameter("backFunction");
17 resp.getWriter().println(backFunction+"("+jsonObject.toJSONString()+")");
18
19 //resp.getWriter().println(jsonObject.toJSONString());
20 }
21 }
- CORS(Cross-Origin Resource Sharing)是一种机制,它允许来自不同源的Web页面访问服务器上的资源,解决了由于浏览器的同源策略而引起的跨域访问限制问题。CORS 通过在服务器端添加特定的HTTP响应头,来告知浏览器允许来自特定源的Web应用访问服务器上的资源。
- 优点: 支持所有类型的HTTP请求,安全性较高。
- 缺点: 老版本浏览器不支持。
- CORS 通信过程都是浏览器自动完成的,不需要用户参与
- 对于开发者一样,CORS 通信与普通的 AJAX 通信没有差别,代码完全一样。浏览器一旦发现 AJAX 请求跨域,就会自动添加一些附加的头信息
- 因此 实现CORS的关键是服务器,只要服务器实现了CORS接口 就可以跨域通信
工作原理
- 当一个Web应用尝试发起一个跨源请求时(例如,使用 XMLHttpRequest 或 Fetch API),浏览器会自动在请求中添加一个
Origin
头,该头包含了发起请求的页面的源信息(协议、域名和端口)。服务器接收到请求后,会检查Origin
头,并根据自身的CORS策略决定是否允许该跨源请求。
- 如果服务器允许该请求,它会在响应中添加一个
Access-Control-Allow-Origin
头,该头的值可以是*
(表示允许所有源)或者是与请求中Origin
头相匹配的特定源。浏览器接收到响应后,会检查Access-Control-Allow-Origin
头,如果源被允许,浏览器就会处理响应;如果不被允许,浏览器会阻止响应,并抛出一个错误。
后端需要做的工作
- 分为三个场景
- 专门支持某个接口跨域:通过@CrossOrigin注解
- 批量支持某一批接口跨域:实现一个Web MVC Configure 这样的一个Bean,重写跨域的映射的方法,通过注册器来添加那一批接口可以实现跨域
- 支持所有接口都跨域:设置一个过滤器来限制所有接口跨域请求
- 代理服务器 使用代理服务器(如 Nginx)进行请求转发是一种常见的解决跨域问题的方法。通过配置代理服务器,可以使客户端请求看起来像是发往同源服务器,而实际上是由代理服务器转发到目标服务器。这样就绕过了浏览器的同源策略限制。
- 优点: 可以解决所有跨域问题,安全性较高。
- 缺点: 需要服务器端额外配置和维护。
使用代理服务器(如 Nginx)进行请求转发是一种常见的解决跨域问题的方法。通过配置代理服务器,可以使客户端请求看起来像是发往同源服务器,而实际上是由代理服务器转发到目标服务器。这样就绕过了浏览器的同源策略限制。
工作原理
- 客户端请求:客户端(例如浏览器)发送一个请求到代理服务器,这个请求的URL看起来是同源的,因为它指向代理服务器所在的域。
- 代理转发:代理服务器接收到请求后,根据配置将请求转发到实际的目标服务器。这个转发过程对客户端是透明的。
- 目标服务器响应:目标服务器处理请求并返回响应给代理服务器。
- 代理返回响应:代理服务器再将目标服务器的响应转发回客户端。
Nginx 配置示例
假设有一个前端应用部署在 http://www.example.com
上,需要访问一个部署在 http://api.another-domain.com
的后端API。可以在 Nginx 中配置一个代理来实现跨域请求:
server {listen 8081;server_name www.example.com;location /api/ {proxy_pass http://api.another-domain.com/;//只要是前缀是api的请求都转发到这个地址zproxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;proxy_set_header X-Forwarded-Proto $scheme;}
}
在这个配置中:
server_name
指定了前端应用的域名。location /api/
指定了一个路径,所有以/api/
开头的请求都会被转发。proxy_pass
指定了目标服务器的地址。proxy_set_header
指令用于设置请求头,这些请求头会被转发到目标服务器。
使用场景
代理服务器的跨域解决方案适用于以下场景:
- 开发环境:在本地开发时,可以使用代理服务器将前端请求转发到后端API,避免跨域问题。
- 生产环境:在生产环境中,可以使用代理服务器来统一前端和后端的域名,从而避免跨域问题。
- 跨域资源共享:当需要访问第三方服务或API时,可以通过代理服务器进行请求转发,实现跨域资源的访问。