No ‘Access-Control-Allow-Origin‘ header is present跨域及解决

 更新时间:2023年02月17日 10:18:10   作者:brucelwl  
这篇文章主要介绍了No ‘Access-Control-Allow-Origin‘ header is present跨域及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

No ‘Access-Control-Allow-Origin‘ header is present

  • 1 浏览器的限制
  • 2 跨域
  • 3 浏览器发送的是 XHR (XMLHttpRequest)请求

当以上三个条件都满足时浏览器会抛出跨域请求异常(记住是浏览器抛出的异常,和服务端没太大关系),在讲跨域请求解决方案前先了解几个问题。

http请求中,哪些是常见的简单请求,哪些是非简单请求

常见的简单请求:请求方法为:GET ,HEAD,POST,请求header里面无自定义头,Content-Type为以下几种:text/plain  multipart/form-data application/x-www-form-urlencoded

常见的非简单请求 :请求方法为:put delete的ajax请求,发送json格式的ajax请求,带自定义头的ajax请求

浏览器在发送跨域请求时候,会有哪些过程

如果是简单请求,浏览器会先发送请求,然后判断服务器返的返回头中是否支持跨域请求,否则抛出跨域异常

如果是非简单请求,浏览器会先发出OPTIONS请求方法的检测命令,判断服务器是否支持跨域请求,如果支持则发送真正的请求,如果不支持则抛出跨域异常,因此一个非简单请求每次会发送两个请求,后面跨域解决方案会讲到缓存OPTIONS预检请求

跨域解决方案

方案1:禁用浏览器跨域校验,即允许跨域访问,(这种方案不可取,不可能让所有的浏览器设置允许跨域访问)

谷歌浏览器禁用跨域校验: 创建一个快捷方式发送到桌面 ,快捷方式--》右键---》属性页面中的目标输入框里追加  --disable-web-security --user-data-dir=C:\Program Files (x86)\Google\Chrome\Application (注意:--user-data-dir的值就是浏览器安装目录。)不一定生效

方案2:采用jsonp方式,需要后台和前台同时改动代码,

1 前台需要设置callback参数,如果使用的是jquery ajax 那么dateType属性设置为jsonp,jquery框架会自动设置参数名为callback的请求参数,也可以通过jsonp属性修改jsonp请求参数名,其他js框架根据具体api使用,

2 后台接收到callback参数后认为是jsonp请求,需要返回jsonp格式,普通json请求返回的content-Type是application/json,而jsonp返回的是application/javascript,同时也证明了jsonp请求服务端返回的是js脚本

3 jsonp请求参数名前后约定需要相同,例如jquery默认使用的是callback

弊端:jsonp 需要前后端都去修改代码,且jsonp是通过动态创建script脚本发送请求,仅支持 GET方法,jsonp发出的请求不是xhr请求,也是能解决跨域的原因

方案3:服务端解决跨域问题

通过编写filter在response对象中添加响应头,告诉浏览器允许跨域访问,* 号代码允许所有的请求域名,所有的请求方法跨域访问

@WebFilter("/*")
public class CORSFilter implements Filter {
 
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse resp = (HttpServletResponse) servletResponse;
 
        // 告诉浏览器允许所有的域访问
        // 注意 * 不能满足带有cookie的访问,Origin 必须是全匹配
        // resp.addHeader("Access-Control-Allow-Origin", "*");
        // 解决办法通过获取Origin请求头来动态设置
        String origin = request.getHeader("Origin");
        if (StringUtils.hasText(origin)) {
            resp.addHeader("Access-Control-Allow-Origin", origin);
        }
        // 允许带有cookie访问
        resp.addHeader("Access-Control-Allow-Credentials", "true");
 
        // 告诉浏览器允许跨域访问的方法
        resp.addHeader("Access-Control-Allow-Methods", "*");
 
        // 告诉浏览器允许带有Content-Type,header1,header2头的请求访问
        // resp.addHeader("Access-Control-Allow-Headers", "Content-Type,header1,header2");
        // 设置支持所有的自定义请求头
        String headers = request.getHeader("Access-Control-Request-Headers");
        if (StringUtils.hasText(headers)) {
            resp.addHeader("Access-Control-Allow-Headers", headers);
        }
 
        // 告诉浏览器缓存OPTIONS预检请求1小时,避免非简单请求每次发送预检请求,提升性能
        resp.addHeader("Access-Control-Max-Age", "3600");
 
        chain.doFilter(request, resp);
    }
}

方案4:Spring框架提供了跨域解决方案

spring提供了 @CrossOrigin注解用户解决跨域问题,同时支持全局配置

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
	@Override
	public void addCorsMappings(CorsRegistry registry) {
		registry.addMapping("/api/**")
			.allowedOrigins("http://domain2.com")
			.allowedMethods("PUT", "DELETE")
			.allowedHeaders("header1", "header2", "header3")
			.exposedHeaders("header1", "header2")
			.allowCredentials(false).maxAge(3600);
	}
}

方案5 服务端通过ngnix解决跨域问题 

location /{
            proxy_pass http://localhost:8080/;
           
            #告诉浏览器允许跨域访问的方法
            add_header Access-Control-Allow-Methods *;
            # 告诉浏览器缓存OPTIONS预检请求1小时
            add_header Access-Control-Max-Age 3600;
            #允许带有cookie访问
            add_header Access-Control-Allow-Credentials true;
            #注意 * 不能满足带有cookie的访问,Origin 必须是全匹配,这里通过变量获取
            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;
            }
        }

方案6 客户端通过nginx隐藏跨域

#转发全部以/api开头的请求到web服务器
   location  /api
   {
        proxy_pass http://127.0.0.1:8080/api;
   }

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Java中的排序与内部比较器Compareable解析

    Java中的排序与内部比较器Compareable解析

    这篇文章主要介绍了Java中的排序与内部比较器Compareable解析,一般没有特殊要求时,直接调用(底层默认的升序排列)就可以得到想要的结果,所谓的 sort 方法排序底层都是基于这两种排序,故如果需要设计成所想要的排序就需要了解底层排序原理,需要的朋友可以参考下
    2023-11-11
  • Java输入数据的知识点整理

    Java输入数据的知识点整理

    在本篇文章里小编给大家整理的是关于Java如何输入数据的相关知识点内容,有兴趣的朋友们学习参考下。
    2020-01-01
  • 谈谈我对Spring Bean 生命周期的理解

    谈谈我对Spring Bean 生命周期的理解

    Spring Bean 的生命周期在整个 Spring 中占有很重要的位置,掌握这些可以加深对 Spring 的理解。这篇文章主要介绍了Spring Bean 生命周期,需要的朋友可以参考下
    2018-03-03
  • Java结构型模式之桥接模式详解

    Java结构型模式之桥接模式详解

    桥接模式是一种很实用的结构型模式,如果系统中某个类存在两个独立变化的维度,通过桥接模式将这两个维度分离出来,使两者可以独立扩展
    2023-02-02
  • Java 实现完整功能的学生管理系统实例

    Java 实现完整功能的学生管理系统实例

    读万卷书不如行万里路,只学书上的理论是远远不够的,只有在实战中才能获得能力的提升,本篇文章手把手带你用Java实现一个完整版学生管理系统,大家可以在过程中查缺补漏,提升水平
    2021-11-11
  • Java 重载、重写、构造函数的实例详解

    Java 重载、重写、构造函数的实例详解

    这篇文章主要介绍了Java 重载、重写、构造函数的实例详解的相关资料,希望通过本文大家能理解掌握java 面向对象的方法,需要的朋友可以参考下
    2017-09-09
  • 一个处理用户登陆的servlet简单实例

    一个处理用户登陆的servlet简单实例

    这篇文章主要介绍了一个处理用户登陆的servlet简单实例,可通过servlet实现处理用户登录的功能,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-01-01
  • java必学必会之this关键字

    java必学必会之this关键字

    java必学必会之this关键字,java中this的用法进行了详细的分析介绍,感兴趣的小伙伴们可以参考一下
    2015-12-12
  • maven springboot如何将jar包打包到指定目录

    maven springboot如何将jar包打包到指定目录

    这篇文章主要介绍了maven springboot如何将jar包打包到指定目录,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • Java基于MySQL实现学生管理系统

    Java基于MySQL实现学生管理系统

    这篇文章主要为大家详细介绍了Java基于MySQL实现学生管理系统,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-01-01

最新评论