ajax跨域问题

本文探讨了Ajax跨域的起因,包括浏览器限制、跨域定义和xhr请求限制。介绍了解决方法,如禁用浏览器限制(不推荐),使用jsonp,以及服务端通过设置响应头或使用代理服务器(如Nginx、Apache、Spring框架)支持跨域。重点讲解了通过添加Access-Control-Allow-Origin响应头来允许跨域,并展示了在Java中使用Filter实现的示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ajax跨域问题

1.为什么会出现ajax跨域
ajax跨域问题的出现本身跟后台服务无关,而是浏览器行为,是因为该ajax请求违反了浏览器的同源策略从而导致的。

ajax跨域的三个原因,只有同时满足了才会发生ajax跨域问题:
1.1浏览器限制:
即使请求被后台正确处理,返回了正确的结果,但是没有通过浏览器的安全检查而错误,这是浏览器对ajax请求的限制。

1.2 跨域
调用和被调用的接口不在同域,而同域就是说协议、域名、端口号三者都要相同,只要其一不同,都为跨域。

1.3 xmlhttprequest(xhr)请求
如何请求不是xmlhttprequest请求,即使跨域,也不会出现ajax跨域问题。

解决ajax跨域的思路
既然需要满足三个条件才会出现ajax跨域问题,那么只要打破其中之一条件就行。
首先:
针对“浏览器限制”问题,可以通过浏览器端关闭相关的安全检查即可,但是对于要求用户做这种繁琐又不友好的操作,显得有些不切实际,可行性太差,基本放弃。
然后:
针对“xhr请求”问题,通常的做法是通过jsonp的方式实现,jsonp不是一种标准协议,可以说是一种约定,通过这种约定,前后台可以根据这种约定来正确的传输数据。jsonp方式就是改变请求方式,通过动态的创建一个script标签,发送请求,带上规定的callback参数,后台就可以根据该参数来识别该请求为jsonp,从而正确处理,然后返回一个带相应内容的js函数。jsonp现在已经无法满足开发需求,用的比较少了,因为服务端需要修改代码、只支持get请求、不是xmlhttprequest请求(xhr支持很多新的特性就没有了),最终还是倾向于解决跨域问题。
最后:
针对“跨域”问题,可以从两个方面出发:1.如果可以修改被调用方代码,可以让被调用方支持ajax跨域。2.也可以通过调用方实现隐藏跨域,隐藏跨域可以通过代理的方式实现,代理服务器和调用方在同域内便可。
解决方法
1. 禁用浏览器限制
通过命令行参数禁用web安全检查启动浏览器。

2.使用jsonp
josn方式使用过在ajax请求中设置dataType:”jsonp”来实现前端的设置,后台也要做相应的改动,以针对jsonp做相应的改动,否则返回的json对象是不能解析。

3.跨域实现
3.1 被调用方支持跨域
3.1.1 服务器端实现
filter解决方案:
浏览器的跨域安全检查顺序?
–简单请求是先执行后检查,非简单命令是先检查后执行,通过首先发送请求类型为OPTIONS的预检请求,判断是否安全,在发送实际请求。
浏览器是怎么判断跨域?
–通过添加请求头(Origin:http://localhost:8080),如果没有响应头没有(Access-Control-Allow-Origin),就会出现跨域安全问题。
filter解决?
–通过创建过滤器,对响应添加响应头(Access-Control-Allow-Origin)实现支持跨域。
出现ajax跨域问题如下图

服务端返回正确处理,后浏览器进行判断
这里写图片描述
浏览器通过判断响应头中是否含有(Access-Control-Allow-Origin),没有则判断为跨域问题
这里写图片描述
使用filter解决方案,通过增加响应头

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class SpringbootDemo1Application {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootDemo1Application.class, args);
    }

    /**
     * 注册过滤器
     * @return
     */
    @Bean
    public FilterRegistrationBean registFilter() {
        FilterRegistrationBean bean = new FilterRegistrationBean();
        bean.addUrlPatterns("/*");
        bean.setFilter(new ScroFilter());
        return bean;
    }
}

过滤器实现类,添加具体响应头

package com.example.demo;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;

public class ScroFilter implements Filter {

    @Override
    public void destroy() {
        // TODO Auto-generated method stub

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletResponse res= (HttpServletResponse) response;
        res.addHeader("Access-Control-Allow-Origin", "*");
        res.addHeader("Access-Control-Allow-Mothods", "*");
        chain.doFilter(request, response);

    }

    @Override
    public void init(FilterConfig arg0) throws ServletException {
        // TODO Auto-generated method stub

    }

}

启动服务,刷新浏览器,重新请求
这里写图片描述
成功添加了响应头,ajax跨域问题得到解决!

如果带cookie的跨域就不能这么做了,而且Access-Control-Allow-Origin:*是不允许的;

package com.example.demo;

import java.awt.PageAttributes.OriginType;
import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ScroFilter implements Filter {

    @Override
    public void destroy() {
        // TODO Auto-generated method stub

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletResponse res= (HttpServletResponse) response;
        //可以判断跨域来获取调用方地址
        HttpServletRequest req = (HttpServletRequest) request;
        //获取origin
        String origin = req.getHeader("Origin");
        if (origin.length() != 0) {
            //带cookie时,Access-Control-Allow-Origin必须是全匹配,不能是*
            res.addHeader("Access-Control-Allow-Origin", origin);

        }
        res.addHeader("Access-Control-Allow-Mothods", "*");
        res.addHeader("Access-Control-Allow-Headers", "Content-Type");
        //缓存预检命令,否则非简单请求每次都会调用预检命令
        res.addHeader("Access-Control-Max-Age", "3600");
        chain.doFilter(request, response);

    }

    @Override
    public void init(FilterConfig arg0) throws ServletException {
        // TODO Auto-generated method stub

    }

}

如果带有自定义请求头,则响应头要设置为

package com.example.demo;

import java.awt.PageAttributes.OriginType;
import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.boot.autoconfigure.security.SecurityProperties.Headers;

public class ScroFilter implements Filter {

    @Override
    public void destroy() {
        // TODO Auto-generated method stub

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletResponse res= (HttpServletResponse) response;
        //可以判断跨域来获取调用方地址
        HttpServletRequest req = (HttpServletRequest) request;
        //获取origin
        String origin = req.getHeader("Origin");
        if (origin.length() != 0) {
            //带cookie时,Access-Control-Allow-Origin必须是全匹配,不能是*
            res.addHeader("Access-Control-Allow-Origin", origin);
        }

        //带自定义请求头
        String headers = req.getHeader("Access-Control-Allow-Headers");
        if (headers.length() != 0) {
            res.addHeader("Access-Control-Allow-Headers",headers);
        }
        res.addHeader("Access-Control-Allow-Credentials", "true");
        res.addHeader("Access-Control-Allow-Mothods", "*");
        //缓存预检命令,否则非简单请求每次都会调用预检命令
        res.addHeader("Access-Control-Max-Age", "3600");
        chain.doFilter(request, response);

    }

    @Override
    public void init(FilterConfig arg0) throws ServletException {
        // TODO Auto-generated method stub

    }

}

2 niginx实现
Nginx实现是将前台请求通过http处理,服务端不需要处理响应头。Nginx的配置如下

  server
      {
        listen 80;
        server_name 172.0.01;
        location /{
            proxy_pass http://localhost:8080/;
            add_header Access-Control-Allow-Mothods *;
            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-request-headers;

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

3.1.3 Apache实现
原理跟Nginx差不多,把相关的响应头配置在Apache的配置文件中配置即可。
3.1.4 spring框架实现
只需要在controller层添加@CossOrigin注解即可,可注解在类和具体的方法上。
3.2 调用方隐藏跨域
通过在前端使用反向代理(nginx)实现,比如:使用a.com访问前台服务,a.com/ajax访问后台服务,对于浏览器来说请求在是同域。

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

        location /ajax{
            proxy_pass http://localhost:8081/test;
        }
      }

同理,Apache的配置也差不多。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值