SpringBoot-12-之Ajax跨域访问全解析

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 一.什么是跨域呢?1.引入:先讲个故事:从前一个叫8080的大佬和一个8081的大佬各占一方天地,还有一个叫浏览器的大佬和8080还有8081关系都不错。

一.什么是跨域呢?

1.引入:

先讲个故事:从前一个叫8080的大佬和一个8081的大佬各占一方天地,还有一个叫浏览器的大佬和8080还有8081关系都不错。浏览器和8080做着一件事(8080端应用),浏览器和8081做着另一件事(8081端应用),但8080和8081却没有什么交集。有一天8081什么话也没说,就跑到8080的地盘拿东西(ajax返回的数据),浏览器手下的警卫员说:"这种珍贵的东西,无凭无据的,我们可不能给你"(跨域访问错误)。

8081气愤离去,心想:"老子可是大佬,还要凭据,于是打电话给8080。"他们商量了一下,8080说:“警卫也是完成自己分内的事,那好吧,我把东西用保险箱(javascript)包裹起来(json转化为jsonp),警卫就不认得了,你回去用我们约定的密码(callback)打开就行了。”(使用jsonp实现跨域)。

这种方法确实可行,一段时间后,两个大佬觉得挺麻烦的,8080说,给你个令牌(响应头上增加相应字段)算了,那着令牌警卫就不会拦你了。果然,简单了许多。

又过了一段时间,8081想:"我是大佬哎,让我每天拿着令牌进进出出,这不损我形象吗?"打电话给8080,说:"既然咱们都是大佬,还让警卫操心干嘛,以后我直接去找你,咱俩喝喝茶,聊聊天不是更好。"经过一段时间的接触,8080和8081关系也不错了,8080爽快地答应了。(隐藏跨域,大佬背后交接)

跨域错误.png
2.为什么?
[1] 浏览器出于安全的限制,而不是服务器
[2] 跨域:协议/域名/端口必须一致
[3] XHR请求(XMLHttpRequest)

二.解决思路

1: 浏览器放方:8080大佬让浏览器警卫队不要阻拦
浏览器不校验跨域.png
2: jsonp:需要后端修改数据格式,前端修改接受方式
普通ajax请求的Type是:xhr           返回的是json字符串      
jsonp的ajax请求的Type是:script     返回的是js脚本          url后有一段callback参数
json和jsonp.png

点击各种url查看:

jsonp返回的:动态创建是一段js脚本,用完再删除
/**/jQuery33103437422192155124_1532262222426({"data":"say Ok"});
json返回的:只是json
{"data":"say Ok"}

jsonp实现步骤

后端:AbstractJsonpResponseBodyAdvice方法过时,没查到新的方法,但也能用

@ControllerAdvice
public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice {
    public JsonpAdvice() {
        super("callback");
    }
}

8080端页面中:

<script>
    var baseUrl = 'http://localhost:8080/ajax';//
    jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000;//每个测试用例耗时时间
        //单元测试用例
        it("jsonp", function (done) {
            var result;//保存返回结果
            $.ajax({
                url: baseUrl,
                dataType: "jsonp",//jsonp格式
                // cache: true,//结果可被缓存
                jsonp:'callback',//默认参数,与后端的callback字符串相对于
                success: function (json) {
                    result = json;
                }
            });

            $.getJSON(baseUrl, function (data) {
                result = data;
            });

            setTimeout(function () {
                expect(result).toEqual({
                    "data": "say Ok"
                });
                done()//完成校验
            }, 100);
        })
    });
</script>
jsonp.png

弊端

[1]服务器需要改动,若后端非己主宰,则无能为力
[2]只支持GET
[3]发的不是XHR请求

3.令牌模式:

被调用方(服务端):响应头上增加相应字段告诉浏览器允许

8081跨域的请求头有:Origin:http://localhost:8081

服务端打造令牌:Filter
com.toly1994.ajaxser.AjaxserApplication

@SpringBootApplication
public class AjaxserApplication {

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

    @Bean
    public FilterRegistrationBean registerFilter() {
        FilterRegistrationBean bean = new FilterRegistrationBean();
        bean.addUrlPatterns("/*");//所有请求都经过这个Filter
        bean.setFilter(new CrosFilter());//设置过滤器
        return bean;
    }
}

com.toly1994.ajaxser.CrosFilter

package com.toly1994.ajaxser;

import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 作者:张风捷特烈
 * 时间:2018/7/22:21:44
 * 邮箱:1981462002@qq.com
 * 说明:CrosFilter
 */
public class CrosFilter implements javax.servlet.Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletResponse rep = (HttpServletResponse) servletResponse;
        //允许8081访问:"http://localhost:8081"换为*表示允许所有
        rep.addHeader("Access-Control-Allow-Origin", "http://localhost:8081");
        //允许访问方法GET:GET"换为*表示允许所有
        rep.addHeader("Access-Control-Allow-Methods", "GET");
        filterChain.doFilter(servletRequest, rep);
    }

    @Override
    public void destroy() {

    }
}

跨域方式.png

4.非简单请求:post json

4-1:简单请求:先执行,后判断

方法:GET HEAD POST
header:无自定义头 
        Content-Type:rext/plain||multipart/form-data||application/x-www-form-urlencoded

4-2:非简单请求:后判断,先执行

put delete 方法的ajax
发送带有json格式的ajax请求
带自定义头的ajax

4-3:Post请求传Json

8080服务端暴露接口:com.toly1994.ajaxser.controller.AjaxController
    @PostMapping(value = "/postJson")
    public ResultBean postJson(@RequestBody User user) {
        System.out.println(user);
        return new ResultBean("post:"+user.getName());
    }
8081服务端调用接口:
        it("postJson", function (done) {
            var result;//保存返回结果
                $.ajax({
                    type:"post",//请求类型
                    url: baseUrl+"/postJson",
                    contentType:"application/json;charset=UTF-8",//请求内容类型
                    data:JSON.stringify({name: "toly"}),//数据JOSN.stringify
                    // cache: true,//结果可被缓存
                    success: function (json) {
                        result = json;
                    }
                });

            setTimeout(function () {
                expect(result).toEqual({
                    "data": "post:toly"//预期结果
                });
                done()//完成校验
            }, 100);
        });
    });
post发送带有json格式的ajax请求.png

4-4:既然是Header原因,那就放行呗:com.toly1994.ajaxser.CrosFilter#doFilter

rep.addHeader("Access-Control-Allow-Headers", "Content-Type");
请求成功.png

可以看到有两个请求,其中一个是OPTIONS的预检请求,下面一句对这个请求做缓存

  • com.toly1994.ajaxser.CrosFilter#doFilter
 rep.addHeader("Access-Control-Max-Age","3600");//一小时内缓存预检请求

5.带Cookie的跨域
8080服务端暴露接口:com.toly1994.ajaxser.controller.AjaxController
    @GetMapping("/getCookie")
    private ResultBean getCookie(@CookieValue(value = "cookie") String cookie) {
        System.out.println("//////////////////////");
        return new ResultBean("getCookie:"+cookie);
    }
8081服务端调用接口:
   //单元测试用例
        it("getCookie", function (done) {
            var result;//保存返回结果
            $.ajax({
                type:"get",//请求类型
                url: baseUrl+"/getCookie",
                xhrFields:{
                    withCredentials: true//发送ajax请求时加cookie
                },
                success: function (json) {
                    result = json;
                }
            });

            setTimeout(function () {
                expect(result).toEqual({
                    "data": "getCookie:toly"//预期结果
                });
                done()//完成校验
            }, 100);
        });
    });
8080种cookie.png
cookie.png

既然是Credentials原因,那就放行呗:com.toly1994.ajaxser.CrosFilter#doFilter

rep.addHeader("Access-Control-Allow-Credentials","true");//允许cookie
请求成功.png

这只解决了8081的跨域,怎么能实现其他的呢?可以获取请求头中的Origin,动态设置。

        HttpServletRequest req = (HttpServletRequest) servletRequest;
        String origin = req.getHeader("Origin");
        if (!StringUtils.isEmpty(origin)) {
            rep.addHeader("Access-Control-Allow-Origin", origin);
        }

6.跨域带自定义头

8080服务端暴露接口:com.toly1994.ajaxser.controller.AjaxController
    @GetMapping("/getHeader")//
    private ResultBean getHeader(@RequestHeader("x-header1") String header1,
    @RequestHeader("x-header2") String header2) {
        System.out.println(header1 + " " + header2);
        return new ResultBean(header1 + " " + header2);
    }
8081服务端调用接口:
 it("getHeaders", function (done) {
            var result;//保存返回结果
            $.ajax({
                type:"get",//请求类型
                url: baseUrl+"/getHeader",
                headers:{
                    "x-header1": "AAA"
                },
                beforeSend: function (xhr) {
                    xhr.setRequestHeader("x-header2","BBB")
                },
                success: function (json) {
                    result = json;
                }
            });

            setTimeout(function () {
                expect(result).toEqual({
                    "data": "AAA BBB"//预期结果
                });
                done()//完成校验
            }, 100);
        });
自定义头错误.png
解决方案:添加头
        //动态添加自定义头
        String headers = req.getHeader("Access-Control-Request-Headers");
        if (!StringUtils.isEmpty(headers)) {
            System.out.println(headers);
            rep.addHeader("Access-Control-Allow-Headers", headers);
        }

7.调用方:隐藏跨域--越过浏览器
暂略
相关文章
|
2月前
|
并行计算 Java 数据处理
SpringBoot高级并发实践:自定义线程池与@Async异步调用深度解析
SpringBoot高级并发实践:自定义线程池与@Async异步调用深度解析
210 0
|
2月前
|
人工智能 自然语言处理 前端开发
SpringBoot + 通义千问 + 自定义React组件:支持EventStream数据解析的技术实践
【10月更文挑战第7天】在现代Web开发中,集成多种技术栈以实现复杂的功能需求已成为常态。本文将详细介绍如何使用SpringBoot作为后端框架,结合阿里巴巴的通义千问(一个强大的自然语言处理服务),并通过自定义React组件来支持服务器发送事件(SSE, Server-Sent Events)的EventStream数据解析。这一组合不仅能够实现高效的实时通信,还能利用AI技术提升用户体验。
210 2
|
19天前
|
前端开发 Java Maven
深入解析:如何用 Spring Boot 实现分页和排序
深入解析:如何用 Spring Boot 实现分页和排序
40 2
|
25天前
|
JSON JavaScript 前端开发
springboot中使用knife4j访问接口文档的一系列问题
本文作者是一位自学前端两年半的大一学生,分享了在Spring Boot项目中使用Knife4j遇到的问题及解决方案,包括解决Swagger请求404错误、JS错误等,详细介绍了依赖升级、注解替换及配置修改的方法。
25 1
|
1月前
|
JSON JavaScript 前端开发
springboot中使用knife4j访问接口文档的一系列问题
本文介绍了在Spring Boot项目中使用Knife4j访问接口文档时遇到的一系列问题及其解决方案。作者首先介绍了自己是一名自学前端的大一学生,熟悉JavaScript和Vue,正在向全栈方向发展。接着详细说明了如何解决Swagger请求404错误,包括升级Knife4j依赖、替换Swagger 2注解为Swagger 3注解以及修改配置类中的代码。最后,针对报JS错误的问题,提供了删除消息转换器代码的解决方法。希望这些内容能对读者有所帮助。
218 5
|
5月前
|
缓存 监控 Java
优化Spring Boot应用的数据库访问性能
优化Spring Boot应用的数据库访问性能
|
3月前
|
设计模式 Java 关系型数据库
【Java笔记+踩坑汇总】Java基础+JavaWeb+SSM+SpringBoot+SpringCloud+瑞吉外卖/谷粒商城/学成在线+设计模式+面试题汇总+性能调优/架构设计+源码解析
本文是“Java学习路线”专栏的导航文章,目标是为Java初学者和初中高级工程师提供一套完整的Java学习路线。
472 37
|
3月前
|
Java Spring
springboot静态资源目录访问,及自定义静态资源路径,index页面的访问
本文介绍了Spring Boot中静态资源的访问位置、如何进行静态资源访问测试、自定义静态资源路径和静态资源请求映射,以及如何处理自定义静态资源映射对index页面访问的影响。提供了两种解决方案:取消自定义静态资源映射或编写Controller来截获index.html的请求并重定向。
springboot静态资源目录访问,及自定义静态资源路径,index页面的访问
|
2月前
|
前端开发 Java
学习SpringMVC,建立连接,请求,响应 SpringBoot初学,如何前后端交互(后端版)?最简单的能通过网址访问的后端服务器代码举例
文章介绍了如何使用SpringBoot创建简单的后端服务器来处理HTTP请求,包括建立连接、编写Controller处理请求,并返回响应给前端或网址。
58 0
学习SpringMVC,建立连接,请求,响应 SpringBoot初学,如何前后端交互(后端版)?最简单的能通过网址访问的后端服务器代码举例
|
3月前
|
存储 缓存 Java
在Spring Boot中使用缓存的技术解析
通过利用Spring Boot中的缓存支持,开发者可以轻松地实现高效和可扩展的缓存策略,进而提升应用的性能和用户体验。Spring Boot的声明式缓存抽象和对多种缓存技术的支持,使得集成和使用缓存变得前所未有的简单。无论是在开发新应用还是优化现有应用,合理地使用缓存都是提高性能的有效手段。
47 1

推荐镜像

更多