ajax跨域请求完整解决方案

ajax跨域请求完整解决方案

简介

在发送Ajax请求的时候,由于浏览器的同源策略,会对跨域请求进行检测。

什么情况属于跨域请求:

  1. 协议不同 ,例如http与https;
  2. 域名不同 ,例如www.a.com与www.b.com;
  3. 端口号不同 ,例如http://localhost:8080与http://localhost:8081;
    以上三种情况符合一种即为跨域请求。

跨域请求解决方案

JSONP

jsonp实现跨域请求的原理简单的说就是动态台创建script标签,然后利用script的src不受同源策略约束来跨域获取数据(script中src请求静态资源例如图片等都不受同源策略约束
jsonp由两部分组成:回调函数和数据。回调函数是当响应到来时应该在页面中调用的函数。回调函数的名字一般是在请求中指定的。而数据就是传入回调函数中的json数据。
前端ajax请求

function getinfo(){
	$.ajax(
	    {
	        url: "http://127.0.0.1:8082/test/submit",
	        type: "GET",
	        contentType: "jsonp",
	        data:JSON.stringify({
	            "username": username,
	            "password": password
	        }) ,
	        jsonp: "callback",//传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(一般默认为:callback)
	        jsonpCallback:"flightHandler",//自定义的jsonp回调函数名称,默认为jQuery自动生成的随机函数名,也可以写"?",jQuery会自动为你处理数据
	        success:function (data) {
	            alert(data["username"]);
	        },
	        error:function (data) {
	            alert("请求失败")
	        }
	    }
	)
}
//也可以在回调函数中获取返回值与ajax中的success的函数作用相同
function flightHandler(){
	alert(data.username);
}

Java后台代码
我后台用的spring框架,因此新建一个类添加@ControllerAdvice注解用于操作controller层
(PS:spring boot2.0以后不推荐使用jsonp,因此没有AbstractJsonResponseBodyAdvice类)

@RestControllerAdvice
public class JsonpAdvice extends AbstractJsonResponseBodyAdvice{
	public JsonAdvice(){
		super("callback");//参数与前端ajax请求中jsonp参数相同
	}
}

需要注意的一点:使用jsonp时只能发送get请求,但数据量大时jsonp方法就不适用了,因此可以采用cros方法

CORS

cros是W3C颁发的一个标准,全称是“跨域资源共享”。它允许浏览器向跨院服务器发出XMLHttpRequest请求,在响应头header中添加Access-Control-Allow-Origin,当浏览器检测到header中的Access-Control-Allow-Origin就可以进行跨域操作,从而克服了ajax只能同源使用的限制。
浏览器将cors请求分为两类:简单请求和非简单请求

简单请求:
请求方法是以下三种方法之一:
  • HEAD
  • GET
  • POST
HTTP的头信息不超出以下几种字段:
  • Accept
  • Accept-Language
  • Content-Language
  • Last-Event-ID
  • Content-Type:只限于三个值application/x-www-form-urlencode、multipart/form-data、text/plain
如果上述两个条件没有同时满足则属于非简单请求
被调用方代码实现

下述代码中我将简单请求和非简单请求,携带自定义代码头,携带cookie等信息整合在一起,大家可以按照自己的需求删减部分代码
ajax代码

function submit() {
    var username = $("input[name='username']").val();
    var password = $("input[name='password']").val();
    $.ajax(
        {
            url: "http://127.0.0.1:8082/test/submit",
            type: "POST",
            contentType: "application/json;charset=utf-8",
            headers:{
                "age":"18", //第一种方式添加自定义的请求头
            },
            data:JSON.stringify({
                "username": username,
                "password": password
            }) ,
            beforeSend:function (xhr) {
                xhr.setRequestHeader("grade","大学");//第二种方式添加自定义的请求头
                xhr.withCredentials=true;//cros跨域请求时默认是不携带cookie,将此参数设置true标准表示浏览器需要获取被调用服务的cookie
                xhr.domain=true;
            },
            success:function (data) {
                alert(data["username"]);
            },
            error:function (data) {
                alert("请求失败")
            }
        }
    )
}

实现cros,需要修改被调用方的后台代码,在后台中添加过滤器

@WebFilter("/*")
@Component
public class AjaxFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        Cookie cookie = new Cookie("cookie1","11111" );
        HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
        httpServletResponse.addCookie( cookie );//在返回信息中添加cookie用于测试
        String origin =  httpServletRequest.getHeader( "Origin" );//获取浏览器中请求跨域的源
        if(!StringUtils.isEmpty( origin ))
        {
            httpServletResponse.setHeader( "Access-Control-Allow-Origin",origin );//设置请求源
        }
        String headers = httpServletRequest.getHeader("Access-Control-Request-Headers");//获取浏览器中自定义添加的头信息
        if(!StringUtils.isEmpty( origin ))
        {
            httpServletResponse.setHeader("Access-Control-Allow-Headers", headers);//设置请求头
        }
       	httpServletResponse.setHeader( "Access-Control-Allow-Methods","*" );// *表示接受所有类型的请求,也可以写入实际需要的请求,但是经过实际测试后发现这行代码不起作用。
        httpServletResponse.setHeader( "Access-Control-Allow-Credentials","true" );// 表示允许携带cookie
        httpServletResponse.setHeader( "Access-Control-Max-Age","3600" );//预检请求的有效期
        filterChain.doFilter(servletRequest,servletResponse);
    }
    @Override
    public void destroy() {
    }
}

我们能看到第一次发送请求时一共发出两条请求,其中第一条OPTIONS为我们的预检请求,及检测我们的请求是否符合跨域要求

OPTIONS中的请求头和响应头

如果我们设置了Access-Control-Max-Age,请求信息会被缓存,当第二次请求时,会看到网络中只有一个POST请求命令
在这里插入图片描述

也可以使用spring注解在controller层加入@CrossOrigin注解,以上步骤都不用配置可直接实现跨域请求,很方便吧!

nginx实现

和代码实现的原理相同在配置文件中添加:

location / {  
    add_header Access-Control-Allow-Origin $http_origin;
    add_header Access-Control-Max-Age 3600;
    add_header Access-Control-Allow-Methods '*';
    add_header Access-Control-Allow-Credentials true;
    add_header Access-Control-Allow-Headers $http_acess_control_request_headers;
    if ($request_method = OPTIONS) {
        return 200;
    }
} 

隐藏跨域

在nginx中通过反向代理将跨域请求映射到同一域下实现隐藏跨域

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

通过反向代理将8080和8081端口映射为同一域名的80端口,这样就不存在不同源的情况,及可实现隐藏跨域

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值