跨域请求的常用解决方案JSONP&CORS

一、什么是跨域问题?

1.同源策略:协议+域名+端口一致,否则就算跨域。(localhost与127.0.0.1也算跨域)

2.DOM同源策略:禁止对不同源页面DOM进行操作

3.XmlHttpRequest同源策略:禁止使用XHR对象向不同源的服务器地址发起HTTP请求

二、跨域解决方案之JSOP

1.原理:利用script标签src属性中的链接却可以访问跨域的js脚本的特性,服务端不再返回JSON格式的数据,而是返回一段调用某个函数的js代码,在src中进行了调用,这样实现了跨域。

2.优点&局限性:没有浏览器兼容性问题;只支持GET方法

3.JSONP请求示例:

3.1、前端AJAX请求,注意type、dataType、jsonp字段(jsonp可以不指定,默认就是callback)

    $.ajax({
        type: "GET",
        url: url,
        dataType: "jsonp",
        jsonp: "callback",
        data: {
            dataType: "1", //告诉服务器端这是JSONP请求
            json: json //业务请求数据,JSON格式
        },
        success: function(data) {
            //请求成功后的处理
        },
        error: function(status) {
            //请求失败后的处理
        }
    })

3.2、后端返回的数据格式处理

    /**
     * 将指定对象封装成JSONPObject,错误时可以指定错误码.
     * 
     * @param pObj 被封装对象
     * @return 封装后的JSONPObject
     */
    public Object getJSONResultObj(Object pObj, Boolean pIsSuccess, String pMessage, String pErrorCode) {

        HttpServletRequest request = this.getRequest();
        String callBackFun = request.getParameter("callback");
        //0:JSON、1:JSONP
        String dataType = request.getParameter("dataType");

        // 作成JSON处理结果对象
        JSONResultVo result = new JSONResultVo();
        result.setResult(pIsSuccess ? JSONResultVo.RESULT_SUCCESS : JSONResultVo.RESULT_FAIL);
        result.setMessage(pMessage);
        result.setErrorCode(pErrorCode);
        result.setData(pObj);

        if ("1".equals(dataType)) {
            //回调方法未指定时
            if (StringUtil.isEmpty(callBackFun)) {
                result.setResult(JSONResultVo.RESULT_FAIL);
                result.setMessage("请求错误:未指定callback");
            }
            return callBackFun + "(" + getJSON(result).toString() + ");";
        } else {
            return getJSON(result);
        }
    }

3.3、请求返回的数据格式:

  

    可以看到,返回的一个调用jsonp1方法的JS脚本,传入方法的参数才是我们真正想要的数据;提取数据并转换成对象类型这些Jquery的ajax已经封装好了,并作为success方法的参数,我们可以直接使用。


三、跨域解决方案之CORS

CORS分为简单请求和非简单请求两种。

1、简单请求:

1.1、简单请求必需满足的条件

①.请求方法:只能是HEAD、GET、POST中的一种

②.请求头必须在下面的范围内:

    · Accept

    · Accept-Languange

    · Content-Language

    · Last-event-ID

    · Content-Type(只限于三个值application/x-www-form-urlencode、multipart/form-data、text/plain)

1.2、浏览器发送的请求

        请求头增加Origin字段,该字段表示请求源的地址即当前页面的域名(服务器端一般以此判断是否跨域)。

1.3、服务器的响应信息

    ①.Access-Control-Allow-Origin:允许跨域请求时,该值应该包含请求的Origin字段的值(只能是单个),或者一个【*】 ,表示接受任意域名的请求。对于只能设置单个请求源的限制,可以通过设置变量值动态设置Origin的值解决。


     ②.Access-Control-Allow-Credentials(可选):是否允许发送Cookie。如果要把Cookie发到服务器,一方面要服务器同意,指定Access-Control-Allow-Credentials字段为true,另一方面,开发者必须在AJAX请求中设置withCredentials属性为true(此时Access-Control-Allow-Origin不能为 * )。

    

2、非简单请求

2.1、用于哪些场合?

    请求方法或头信息有特殊要求的;如请求方法是DELETE,或Content-Type字段的类型application/json。
    
2.2、 预检
2.2.1、什么是预检
    正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)。浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。
2.2.2、预检请求有什么特殊的地方
(1)请求方法是OPTIONS;
(2)请求头除了Origin字段,包含另外两个特殊的字段Access-Control-Request-Method:请求使用哪种方法、Access-Control-Request-Headers:额外发送的头信息字段
(3)除了ccess-Control-Allow-Origin,响应头包含一些特殊的字段:Access-Control-Allow-Methods、Access-Control-Allow-Headers、Access-Control-Allow-Credentials(可选)、Access-Control-Max-Age(可选)

2.2.3、预检请求成功示例:
ajax请求信息(正式请求)
    $.ajax({
        type: "DELETE",
        url: url,
        data: {
            dataType: "2"
        },
        headers: "My-Custom-Header", //自定义头
        success: function(data) {
            $(".result").text("ID:" + data.data.id + " NAME:" + data.data.name);
        },
        error: function(status) {
            $.alert("您的网络不给力哦");
        }
    })

下面是浏览器自动发送的预检请求

  
    这个请求对应的正式请求的请求方法是DELETE,而且发送一个自定的头my-custom-header,因为不满足简单请求的条件,所以浏览器在发送正式请求之前发送的一个预请求。浏览器会根据预请求返回的一些头信息判断是否有权访问该资源,通过的话才发送正式的请求,正式请求与简单请求差不多。预请求有时效性,在预请求有效期内(可通过响应的Access-Control-Max-Age字段设置),不会重复发送预请求。

  如果预检没通过,浏览器会报下面额错,告诉预检失败

      

2.2.4、SpringMVC支持CORS的三种方式

(1)使用@CrossOrigin注解(4.2及以上)

    @RequestMapping(value = "/{id}", produces = CHARSET_PRODUCES, method=RequestMethod.DELETE)
    @ResponseBody
    @CrossOrigin(origins="*",allowedHeaders="My-Custom-Header",methods=RequestMethod.DELETE)
    public Object testCors(String json, @PathVariable("id")String id,
                            HttpServletResponse response, HttpServletRequest request) {
        Map<String, String> data = new HashMap<String, String>();
        data.put("id", id);
        data.put("name", "测试");

        return success(data);
    }

(2)CORS全局配置:可以以path为单位定义多个映射(4.2及以上)

基于XML的配置

    <mvc:cors>
        <mvc:mapping path="/api/**"
            allowed-origins="http://djf.eastallcar.com"
            allowed-methods="POST"
            allowed-headers="My-Custom-Header"
            allow-credentials="true"
            max-age="1800"/>
        <mvc:mapping path="/**"
            allowed-origins="*"
            allowed-methods="GET,POST,DELETE"
            allowed-headers="My-Custom-Header"
            allow-credentials="true"
            max-age="1800"/>
    </mvc:cors>

 基于JAVA代码的配置

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/**")
            .allowedOrigins("http://djf.eastallcar.com")
            .allowedMethods("PUT", "DELETE")
            .allowedHeaders("My-Custom-Header1", "My-Custom-Header2")
            .exposedHeaders("My-Custom-Header1", "My-Custom-Header2")
            .allowCredentials(false).maxAge(3600);
    }
}

(3)增加一个继承自OncePerRequestFilter的过滤器

/**
 * 跨域请求过滤器.
 */
public class CorsFilter extends OncePerRequestFilter {


    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {
        response.setHeader("Access-Control-Allow-Origin", "http://djf.eastallcar.com");
        response.setHeader("Access-Control-Allow-Methods", "GET,POST,DELETE,PUT,OPTIONS");
        response.setHeader("Access-Control-Allow-Headers", "My-Custom-Header");
        response.setHeader("Access-Control-Max-Age", "60");
        filterChain.doFilter(request, response);
    }
}

2.2.5、CORS个人使用体会:

(1)使用简单请求还是非简单请求是浏览器根据请求方法和头信息决定的,与服务器端无关;
(2)CORS是为了解决浏览器端的跨域限制,不是解决服务器资源安全的方式;可以认为浏览器对跨域资源请求的流程是对资源请求的安全的一个二次校验,这个校验需要服务器端提供支持情况(通过响应头告诉浏览器支持的请求源、方法、头)。
(3)部分浏览器的老版本不支持CORS,适用于对兼容性要求不高的项目。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值