【Ajax】跨域问题

先挖个坑,今天的学习内容
今天去体检浪费了一天,赶紧补上 20180428凌晨

验证跨域访问安全问题:

测试中8080是服务端,8081是前台端
spring-boot搭建前台和后台页面(web/dev-tools两个依赖,一个提供web服务,一个提供自动重启服务功能)
被调用方后台代码
- 用到的注解:
- @RestController
- @RequestMapping(“/test”)
- @GetMapping(“/get1”)

调用方前台代码
- 修改application.properties为application.yml(有提示功能),端口改成8081
- 编写ajax测试代码

jasmine测试框架
- 减轻工作量,解脱手动测试

  • 示例代码:不设置超时时间的话,框架会一直等待
// 每一个测试用例的超时时间
        jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000;

        // 请求的接口的前缀 // http://localhost:8080/test
        var base = "/ajaxserverapache";

        //测试模块
        describe("晓风轻-ajax跨越完全讲解", function() {
            // 测试方法
            it("get1请求", function(done) {
                // 服务器返回的结果
                var result;

                $.getJSON(base + "/get1").then(function(jsonObj) {
                    result = jsonObj;
                });

                // 由于是异步请求,需要使用setTimeout来校验
                setTimeout(function() {
                    expect(result).toEqual({
                        "data" : "get1 ok"
                    });

                    // 校验完成,通知jasmine框架
                    done();
                }, 100);
            });
        });

为什么会有跨域问题:

  1. 浏览器限制(跨域的时候返回是200 ok,后台返回也是成功的,但是浏览器限制了跨域访问)
  2. 跨域:协议、域名、端口任何一个不一样就认为是跨域
  3. 发出的是XHR请求,HTTP/XML请求即使跨域浏览器也不会报错
    以上3个条件同时满足才会产生跨域安全问题

解决思路

浏览器限制 ->

指定参数,浏览器不做校验,需要在客户端做改动,价值不大

XHR ->

使用JSONP,无法满足现在的开发要求

跨域 ->

  • 被调用方修改代码-支持跨域,A发送给B,B返回数据带某些字段给浏览器,表明允许A访问此资源,浏览器校验通过即可
  • 调用修改-隐藏跨域,使用代理,浏览器都访问A域名,但是发送到代理服务器,代理服务器再把指定的URL转发到B域名

浏览器禁止检查:

chrome –disable-web-security –user-data-dir=g:\temp3

JSONP:json pending
json的补充使用方式,非官网,利用的是script请求可以跨域的特性
使用jsonp的方式 后台是需要改动的

// 测试方法
            it("jsonp请求", function(done) {
                // 服务器返回的结果
                var result;

                $.ajax({
                    url: base +"/get1",
                    dataType: "jsonp",
                    jsonp: "callback",
                    cache:true,
                    success: function(json){
                        result = json;
                    }
                });

                // 由于是异步请求,需要使用setTimeout来校验
                setTimeout(function() {
                    expect(result).toEqual({
                        "data" : "get1 ok"
                    });

                    // 校验完成,通知jasmine框架
                    done();
                }, 100);
            });

JsonpAdvice.java 被调用端测试

package com.imooc;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.AbstractJsonpResponseBodyAdvice;

@ControllerAdvice
public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice {

    public JsonpAdvice() {
        // TODO Auto-generated constructor stub
        super("callback");
    }
}

jsonp和普通的ajax请求的差别
- 普通发送的是xhr请求,jsonp发送的是script请求
- 普通返回的content-type是json,jsonp返回是javascript脚本
- 普通请求的url不带参数,jsonp加了一个callback参数,后端接收到callback参数就认为是jsonp请求,会返回js代码:callback值作为函数名,返回数据作为参数

小结:jsonp动态创建一个script,发送请求,使用约定好的”callback”作为参数发送到后台,后台收到这个请求之后,就认为请求是jsonp请求,那么就不返回json,而是返回一段js代码,callback值为函数名,返回数据作为参数
完整的jsonp参数是callback后面跟一个带_的随机数值,防止被缓存,可以再发送ajax的时候设置cache为true可以缓存,默认是不缓存的。

jsonp的弊端:
- 服务器需要改动
- 只支持GET方法(请求类型为script只支持get方法)
- 发送的不是XHR请求(XHR的多种特性就无法使用)

修改被调用方的思路

解决方法:返回头增加字段
可以修改的地方:
服务器代码修改
或者
HTTP服务器修改(Nginx/Apache)

服务器代码实现

Filter解决CrosFilter.java

浏览器是先执行,再判断
如果发现是跨域请求,那么浏览器会在请求头里面增加Origin:http://locathos:8081字段
等待响应头返回2个字段:
Access-Control-Allow-Origin
Access-Control-Allow-Methods

代码实现:

res.addHeader("Access-Control-Allow-Origin", origin);
res.addHeader("Access-Control-Allow-Methods", "*");
简单请求和非简单请求

简单请求:
- 方法为
- GET
- HEAD
- POST

  • 请求头
    • 无自定义头
    • Content-Type是一下几种
      • text/plain
      • multipart/form-data
      • application/x-www-form-urlencoded

非简单请求:(浏览器)
- put/delete方法的ajax请求
- 发送json格式的ajax请求
- 带自定义头的ajax请求

非简单请求会发送一个预检命令

使用post发送json

比如使用post发送json,那么会添加一个Access-Control-Request-Headers:content-type字段
在filter中增加一个头res.addHeader("Access-Control-Allow-Headers", headers);就可以了
这样的话,浏览器会发出2个请求
第一个是Options请求,预检命令,检查服务器是否允许这么跨域
第二个是真正的POST请求

预检命令是可以缓存的,比如缓存一个小时
res.addHeader("Access-Control-Max-Age", "3600");

cookie的跨域

cookie存放sessionid,session依赖于cookies实现
如果把下面这个设置为星号,cookie跨域会失败,带cookie必须是全匹配
res.addHeader("Access-Control-Allow-Origin", "*");
解决方法:filter获取字段,再填充回去

String origin = req.getHeader("Origin");

if (!org.springframework.util.StringUtils.isEmpty(origin)) {
    //带cookie的时候,origin必须是全匹配,不能使用*
    res.addHeader("Access-Control-Allow-Origin", origin);           
}
// enable cookie
res.addHeader("Access-Control-Allow-Credentials", "true");

这里的cookie是在被调用方的!!

发送自定义头

Access-Control-Request-Headers
返回头增加同样字段即可
filter中实现:

String headers = req.getHeader("Access-Control-Request-Headers");

        // 支持所有自定义头
        if (!org.springframework.util.StringUtils.isEmpty(headers)) {
            res.addHeader("Access-Control-Allow-Headers", headers);         
        }

被调用方的nginx配置

虚拟主机:多个域名指向同一个服务器

hosts:
127.0.0.1 b.com

创建目录 nginx/conf/vhost/

在nginx/conf/nginx.conf末尾增加一句
include vhost/*.conf;

创建文件 nginx/conf/vhost/b.com.conf

server {
    listen 80;
    server_name b.com;

    location /{
        proxy_pass http://localhost:8080/;
    }
}

把b.com 80端口的访问映射到8080

注意这里不要使用filter,把AjaxserverApplication.java中关于filter的代码注释掉

配置nginx增加响应头,横杠改成下划线,首字母改为小写,if后面有空格

server {
    listen 80;
    server_name b.com;

    location /{
        proxy_pass http://localhost:8080/;

        add_header Access-Control-Allow-Methods *;
        add_header Access-Control-Max-Age 3600;
        add_header Access-Control-Allow-Credentials true;

        add_header Access-Control-Allow-Origin $http_origin;
        add_header Access-Control-Allow-Headers $http_access_control_allow_headers;

        if ($request_method = OPTIONS){
            return 200;
        }
    }
}

被调用方的Apache配置

实现原理和Nginx一样,配置比较复杂

被调用方使用Spring框架配置

增加一个注解即可,TestController.java
@CrossOrigin

调用方解决

反向代理

nginx配置
a.com.conf

server {
    listen 80;
    server_name a.com;
    location /{
        proxy_pass http://localhost:8081/;
    }
    location /ajaxserver{
        proxy_pass http://localhost:8080/test;
    }
}

这样,浏览器访问a.com的时候,经过http服务器之后,是去访问8081,而8081访问的是ajaxsever,也就是8080

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值