专栏首页一杯82年的JAVA跨域Access-Control-Allow-Origin解决方案

跨域Access-Control-Allow-Origin解决方案

前端访问其它域名的资源往往会失败,那是因为浏览器出于安全考虑禁止了不同源的资源。

同源策略

同源策略,它是由Netscape提出的一个著名的安全策略。现在所有支持JavaScript的浏览器都会使用这个策略。所谓同源是指,域名,协议,端口相同。同源策略是浏览器的行为,是为了保护本地数据不被JavaScript代码获取回来的数据污染,因此拦截的是客户端发出的请求回来的数据接收,即请求发送了,服务器响应了,但是无法被浏览器接收。

同源:协议 + 域名 + 端口

既然是浏览器的策略,则说明资源请求是可以正常返回的,只是浏览器不给用。

跨域报错

本地启动了一个web服务,地址为 127.0.0.1:8882 ,然后通过一个本地静态页面去请求这个接口。虽然在同一台电脑,但依然是跨域的。

上面也说了这个限制是浏览器做的,看看接口,其实已经请求成功了,后端是执行了相关代码的。

后端修改Response支持跨域

从上面控制台的输出可以看到,错误原因是请求的资源(接口)的header中没有”Access-Control-Allow-Origin“,那我们可以给它加上。在哪加?既然说是请求的资源没有,那当然是在请求的资源上加,也就是服务端。

@SpringBootApplication
@Configuration
@RestController
public class ApplicationA {

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

    @RequestMapping("/test")
    public Object test(HttpServletRequest request, HttpServletResponse response) {
        // 跨域支持
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "POST,GET,PUT,DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "*");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        Map<String, Object> map = new HashMap<>();
        map.put("success", true);
        map.put("msg", "我来自服务端");
        return map;
    }
}

再看看浏览器,已经可以正常访问接口了。

如果觉得每个接口里面都要配置一下response很麻烦,可以在一个拦截器里面做这个事情。

springboot支持跨域

测试用例是一个springboot项目,可以用更简单的方式。通过一个继承了WebMvcConfigurerAdapter的bean,重写addCorsMappings方法,在方法里配置。

@SpringBootApplication
@Configuration
@RestController
public class ApplicationA extends WebMvcConfigurerAdapter {

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

    @RequestMapping("/test")
    public Object test(HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<>();
        map.put("success", true);
        map.put("msg", "我来自服务端");
        return map;
    }

    // 跨域支持
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowCredentials(true)
                .allowedMethods("GET", "POST", "DELETE", "PUT")
                .maxAge(3600);
    }

jsonp支持跨域

有前端经验的童鞋知道,有时我们会在自己的代码里直接引入其它域名的js、css等静态文件。为啥这些静态文件没被浏览器限制呢?通常为了减轻web服务器的压力,我们会把js、css,img等静态资源分离到另一台独立域名的服务器上,使其和前端分离开。基于这个原因,浏览器并没有限制这类静态资源的跨域访问。

我们可以动态地创建一个script,让浏览器以为我们要获取静态资源,从而网开一面。而服务器端也需要做一点改变,不能直接返回json,而是返回一个立即执行的函数,而前端请求的结果就作为函数的参数。

后端接口返回

@SpringBootApplication
@Configuration
@RestController
public class ApplicationA {

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

    @RequestMapping("/test")
    public String test(HttpServletRequest request, HttpServletResponse response, String callback)
            throws IOException {
        Map<String, Object> map = new HashMap<>();
        map.put("success", true);
        map.put("msg", "我来自服务端");
        // 返回值如下:
        // callback({"msg":"我来自服务端","success":true});
        return String.format("%s(%s);", callback, JsonUtil.toJson(map));
    }

js原生实现jsonp

function test() {
    // 外部域名,参数是和后端接口约定的callback指定接口返回后的回调函数
    url = "http://localhost:8882/test?callback=_ajax_callback";
    // 创建一个script元素
    var script = document.createElement('script');
    script.type = 'text/javascript';
    script.src = url;
    document.head.appendChild(script);
}

// 接口回调
function _ajax_callback(res) {
    console.log("被回调了");
    console.log(res);
}

接口返回:

回调函数执行:

jQuery实现jsonp

一般我们会使用jQuery来做ajax请求,这样需要增加一个jQuery的引用。

// 没测,懒得测
$.ajax({
    url: 'http://localhost:8882/test',
    type: 'get',
    dataType: 'jsonp',  // 请求方式
    jsonpCallback: "_ajax_callback",    // 回调函数名
    data: {}
});

vue.js实现jsonp

现在前端vue.js用的也很多,再记录一个vue.js的用法。

// 没测,懒得测
this.$http.jsonp('http://localhost:8882/test', {
    params: {},
    jsonp: '_ajax_callback'
}).then((res) => {
    console.log(res); 
})

jsonp缺点

只能实现get请求。

其它方式支持跨域

  • nginx反向代理:前端访问相同域名,nginx再根据需要把请求转发到外部域名;
  • 后端代理:在后端接口里先请求外部资源(比如用HttpClient),然后把结果返回给前端,这样就不是跨域了;
  • 其它:借助iframe、postMessage等也可实现跨域。

本文分享自微信公众号 - 一杯82年的JAVA(acupjava),作者:acupt

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-08-17

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • JAVA中有趣的位运算

    当我们看一些源码的时候,经常会看到诸如 &、|、^、~ 的符号,这些就是位运算符。

    acupt
  • 探索JAVA并发 - 如何优雅地取消线程任务

    一种常用的方法是在任务代码中加入一个“是否取消”的标志,任务定期去查看这个标志是否改变,如果被改变了就取消剩下的任务,此时如果想取消这个任务只需要修改它的标志,...

    acupt
  • 从0.5到1写个rpc框架 - 2:远程服务调用(grpc)

    gRPC是Google开源的跨语言远程服务调用(RPC)框架,通信协议用的HTTP/2,数据传输默认用的protocol buffers(一种轻便高效的结构化数...

    acupt
  • 点击一个web网页的流程和所用协议(计算机网络)

    客户方启动浏览器; 客户在浏览器的地址栏键入URL; 浏览器分析URL,找到信息资源所在主机地址; 与该主机(服务器)建立TCP连接(端口号80); 向...

    葆宁
  • python面向对象之多态与多态性

    2、定义统一的接口,可以传入不同类型的值, 但是调用的逻辑都一样,执行的结果却不一样

    菲宇
  • Java Servlet 做一个简单的分IP访问统计系统

    本文由 Alone88 创作,采用 知识共享署名4.0 国际许可协议进行许可 本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名 最后编辑时间为:...

    Alone88
  • android.graphics.Matrix类用法分析

    本文实例讲述了android.graphics.Matrix类用法。分享给大家供大家参考,具体如下:

    砸漏
  • 再添嘉誉!微洱科技斩获“年度零售AI解决方案创新奖”|腾讯SaaS加速器·学员动态

    ? 来源 |  腾讯SaaS加速器二期项目-微洱科技 ---- 2020 年11 月20-21日,第三届中国零售科技决策者峰会暨2020 中国零售行业 CIO...

    腾讯SaaS加速器
  • 我的ElasticSearch认证工程师之路

    我是2020年4月30日通过的认证,应群主之邀,写一篇经验分享,也是给我的认证之路做一个小结。其实和很多群友想的不一样,我平常在工作中要写DSL的机会不多,和E...

    铭毅天下
  • Android ExpandableListView双层嵌套实现三级树形菜单

    在Android开发中,列表可以说是最常见的了,一般都是使用ListView,当涉及到二维数组时,更多的使用到ExpandableListView,然而当数据结...

    砸漏

扫码关注云+社区

领取腾讯云代金券