前面文章《架构设计-web项目中跨域问题涉及到的后端和前端配置》中说明了处理跨域问题的一种方式,本文详细说明下产生跨域问题的原因及处理方式。
浏览器的同源策略:这是跨域问题的根本原因。同源策略是浏览器对JavaScript施加的安全限制,目的是出于浏览器安全考虑,防止恶意网站窃取数据。所谓“同源”指的是协议、域名、端口号都相同,只要有一个不相同,则被视为非同源。
跨域就是在浏览器请求资源的过程中发生的。
利用了 script 标签没有跨域限制的特性,通过 src 属性发送一个带回调参数的 get 请求,实现跨域。
一种略显古老的处理方式
- $.ajax({
- type : "GET",
- url : "http://abc.com/detail/",
- data:"id=100",
- dataType:"jsonp",
- jsonp:"callback",
- jsonpCallback:"showDetail",
- success : function(data){
- alert("ok");
- },
- error : function(data){
- alert("no");
- }
- });
以上代码转换成 dom 文档
- html>
- <html>
- <head>
- <meta charset="utf-8">
- head>
- <body>
- <script type='text/javascript'>
- // 后端返回直接执行的方法,相当于执行这个方法,由于后端把返回的数据放在方法的参数里,所以这里能拿到res。
- window.showDetail = function (res) {
- console.log(res)
- // ajax回调
- }
- script>
- <script src='http://abc.com/detail?id=100&callback=showDetail' type='text/javascript'>script>
- body>
- html>
页面的 src 标签访问 abc.com 的服务端资源,由于 标签不受 xmlHttpRequest 限制,后端接收到请求后返回约定好的 json 对象
- showDetail({
- status: 0,
- result: {
- id: 1,
- name: "san",
- price: 99.9,
- level: 2
- }
- })
接下来,浏览器执行 showDetail 的过程,就相当于执行了上面定义的 window.showDetail 方法并传入了后端返回的 json ,然后可以应用到 success 方法中。
跨域资源共享。Cross-Origin Resource Sharing 后端人员在返回客户端的数据上加上对应的响应头,告知浏览器可以放行此数据给客户端。
对 http 的 response 属性配置头部信息:
- @Component
- public class CorsFilter implements WebFilter {
-
- private static final String ALL = "*";
- private static final String MAX_AGE = "3600L";
-
- @Override
- public Mono
filter(ServerWebExchange exchange, WebFilterChain chain) { - // 非跨域请求,直接放行
- ServerHttpRequest request = exchange.getRequest();
- if (!CorsUtils.isCorsRequest(request)) {
- return chain.filter(exchange);
- }
-
- // 设置跨域响应头
- ServerHttpResponse response = exchange.getResponse();
- HttpHeaders headers = response.getHeaders();
- headers.add("Access-Control-Allow-Origin", ALL);
- headers.add("Access-Control-Allow-Methods", ALL);
- headers.add("Access-Control-Allow-Headers", ALL);
- headers.add("Access-Control-Max-Age", MAX_AGE);
- if (request.getMethod() == HttpMethod.OPTIONS) {
- response.setStatusCode(HttpStatus.OK);
- return Mono.empty();
- }
- return chain.filter(exchange);
- }
-
- }
注意:
如果使用了 nginx 反向代理,可以直接在 nginx 反向代理上配置
- location /{
- proxy_pass http://abc.com;
-
- add_header Access-Control-Allow-Methods *;
- add_header Access-Control-Allow-Credentials true;
- add_header Access-Control-Allow-Origin $http_origin;
- add_header Access-Control-Allow-Headers *;
-
- }
使用 vue-cli 的跨域代理配置。本质是通过 代理服务器 充当 本地请求 和 目标服务器 之间的桥梁,代理服务器 和 本地浏览器 同源,不存在跨域,所以浏览器能正常接收数据,通过避开浏览器的同源策略完成跨域请求。
- server: {
- port: 3000, // 设置服务启动端口号
- open: true, // 设置服务启动时是否自动打开浏览器
- host: "0.0.0.0",
- proxy: {
- '^/order': {
- target: config.VITE_ORDERURL,
- changeOrigin: true,
- rewrite: (path) => path.replace(/^\/order/, ''),
- },
- '^/sale': {
- target: config.VITE_SALEURL,
- changeOrigin: true,
- rewrite: (path) => path.replace(/^\/sale/, ''),
- },
-
- },
- }
如果静态页面在 xyz.com 上,动态请求访问 pqm.com ,将对应服务部署在不同机器上,使用公共的 abc.com 域名 作为 nginx 反向代理的入口,再将 静态服务、动态服务 分别挂在被代理局域网服务器内。
- server{
- listen:80;
- server_name: abc.com;
-
- # 静态资源
- location /{
- proxy_pass http://xyz.com/;
- }
-
- # 动态请求
- location /api{
- proxy_pass http://pqm.com/;
-
- }
-
- }
