欢迎访问悦橙教程(wld5.com),关注java教程。悦橙教程  java问答|  每日更新
页面导航 : > > 文章正文

Java实现后端跨域的常见解决方案,

来源: javaer 分享于  点击 34785 次 点评:112

Java实现后端跨域的常见解决方案,


目录
  • 一、搭建服务(cross-server)
    • 1.1、maven依赖
    • 1.2、接口
    • 1.3、配置
  • 二、搭建服务(cross-web)
    • 2.1、maven依赖
    • 2.2、接口
    • 2.3、页面
    • 2.4、配置
    • 2.5、跨域请求结果
    • 2.6、常见跨域情况
  • 三、解决方案
    • 3.1、通过 @CrossOrigin 注解
    • 3.2、通过配置类
    • 3.3、通过 CorsFilter 对象
    • 3.4、通过 Response 对象
    • 3.5、通过实现 ResponseBodyAdvice 对象
    • 3.6、通过 jsonp 方式
      • 3.6.1、后端
      • 3.6.2、前端方法一
      • 3.6.3、前端方法二
  • 总结

    一、搭建服务(cross-server)

    1.1、maven依赖

    pom.xml

    <dependencies>
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-web</artifactId>
       </dependency>
    
       <dependency>
           <groupId>org.projectlombok</groupId>
           <artifactId>lombok</artifactId>
           <version>1.16.14</version>
       </dependency>
    </dependencies>
    

    1.2、接口

    @Slf4j
    @RestController
    @RequestMapping("/cross")
    public class CrossController {
    
        @PostMapping("/request")
        public String crossRequest(HttpServletRequest request) {
            log.info("接收到跨域请求参数:{}",request.getParameter("name"));
            return "Request CrossServer Success";
        }
    
    }
    

    1.3、配置

    application.properties

    server.port=8080
    server.servlet.context-path=/crossServer
    

    至此我们就提供了一个接口:http://localhost:8080/crossServer/cross/request

    二、搭建服务(cross-web)

    2.1、maven依赖

    pom.xml

    <dependencies>
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-web</artifactId>
       </dependency>
    
       <dependency>
           <groupId>org.projectlombok</groupId>
           <artifactId>lombok</artifactId>
           <version>1.16.14</version>
       </dependency>
    </dependencies>
    

    2.2、接口

    @Controller
    @RequestMapping("/test")
    public class WebController {
    
        @RequestMapping(value = "/request")
        public String request(HttpServletRequest request) {
            return "/cross";
        }
    }
    

    2.3、页面

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>跨域测试页面</title>
        <script src="js/jquery.min.js"></script>
    </head>
    <body>
    <h1>跨域测试</h1>
    <div>
        <input type="button" onclick="mySubmit()" value=" 发送跨域请求 ">
    </div>
    <script>
        function mySubmit() {
            // 发送跨域请求
            jQuery.ajax({
                url: "http://localhost:8086/test",
                type: "POST",
                data: {"name": "Java"},
                success: function (result) {
                    alert("返回数据:" + result.data);
                }
            });
        }
    </script>
    </body>
    </html>
    

    2.4、配置

    application.properties

    server.port=8081
    server.servlet.context-path=/crossWeb
    
    spring.freemarker.request-context-attribute=request
    spring.freemarker.charset=UTF-8
    spring.freemarker.template-loader-path=classpath:/templates/
    spring.freemarker.suffix=.ftl
    spring.freemarker.prefix=/views
    

    至此我们就提供了一个接口:http://localhost:8081/crossWeb/test/request,访问此页面可以打开如下测试页面:

    2.5、跨域请求结果

    在没有任何跨域处理的情况下,我们点击按钮,发起请求,得到结果如下:

    Access to XMLHttpRequest at 'http://localhost:8080/crossServer/cross/request' from origin 
    'http://localhost:8081' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' 
    header is present on the requested resource.
    POST http://localhost:8080/crossServer/cross/request net::ERR_FAILED 200
    

    2.6、常见跨域情况

    请求地址说明是否跨域
    http://www.alian.com/a
    http://www.alian.com/b
    同域名
    http://www.alian.com/a/b
    http://www.alian.com/c/d
    同域名不同文件夹
    http://www.alian.com/a
    https://www.alian.com/a
    同域名不同协议
    http://www.alian.com/a
    http://www.alian.com:8080/a
    同域名下不同端口
    http://www.alian.com/a
    http://yun.alian.com/a
    同顶级域名不同二级域名
    http://www.alian.com/a
    http://www.yang.com/a
    不同域名
    http://www.alian.com/a
    http://10.130.1.88:80/a
    域名和对应的ip

    三、解决方案

    3.1、通过 @CrossOrigin 注解

    最简单的方式是通过使用注解@CrossOrigin,但是需要注意的是这个注解版本,这个注解是从Spring Framework 4.2 GA开始有的。如果你项目的版本低于这个版本是用不了的。

    package com.alian.crossserver.controller;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.web.bind.annotation.CrossOrigin;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.servlet.http.HttpServletRequest;
    
    @Slf4j
    @RestController
    @RequestMapping("/cross")
    public class CrossController {
    
        @CrossOrigin(origins = "*")
        @PostMapping("/request")
        public String crossRequest(HttpServletRequest request) {
            log.info("接收到跨域请求参数:{}",request.getParameter("name"));
            return "Request CrossServer Success";
        }
    
    }
    

    3.2、通过配置类

    为了避免方法间相互影响,我们先在我们的方法crossRequest,去除注解@CrossOrigin(origins = “*”),后面也是一样就不一一提示了。

    package com.alian.crossserver.config;
    
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.CorsRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    @Configuration
    public class CorsConfig implements WebMvcConfigurer {
    
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/**") // 所有接口
                    .allowCredentials(true) // 是否发送 Cookie
                    .allowedOriginPatterns("*") // 支持域
                    .allowedMethods("GET", "POST", "PUT", "DELETE") // 支持方法
                    .allowedHeaders("*")
                    .exposedHeaders("*");
    
        }
    }
    

    上面是对全部方法都支持跨域,我们也可以指定某个请求支持跨域,如下:

    @Configuration
    public class CorsConfig implements WebMvcConfigurer {
    
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/cross/request") // 指定接口
                    .allowCredentials(true) // 是否发送 Cookie
                    .allowedOriginPatterns("*") // 支持域
                    .allowedMethods("GET", "POST", "PUT", "DELETE") // 支持方法
                    .allowedHeaders("*")
                    .exposedHeaders("*");
    
        }
    }
    

    3.3、通过 CorsFilter 对象

    再没有使用以上方式解决跨域的情况下,我们使用过滤器也可以实现。

    package com.alian.crossserver.filter;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.cors.CorsConfiguration;
    import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
    import org.springframework.web.filter.CorsFilter;
    
    @Configuration
    public class CustomCorsFilter {
    
        @Bean
        public CorsFilter corsFilter() {
            // 创建cors配置对象
            CorsConfiguration config = new CorsConfiguration();
            // 支持域
            config.addAllowedOriginPattern("*");
            // 是否发送 Cookie
            config.setAllowCredentials(true);
            // 支持请求方式
            config.addAllowedMethod("*");
            // 允许的原始请求头部信息
            config.addAllowedHeader("*");
            // 暴露的头部信息
            config.addExposedHeader("*");
            // 添加地址映射
            UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
            corsConfigurationSource.registerCorsConfiguration("/**", config);
            // 返回 CorsFilter 对象
            return new CorsFilter(corsConfigurationSource);
        }
    }
    

    上面是对所有域名请求都支持跨域,我们也可以指定某个域名,意思就是只有指定域名可以跨域请求。比如:我们只允许http://localhost:8081的跨域请求。

    @Configuration
    public class CustomCorsFilter {
    
        @Bean
        public CorsFilter corsFilter() {
            // 创建cors配置对象
            CorsConfiguration config = new CorsConfiguration();
            // 支持指定域
            config.addAllowedOriginPattern("http://localhost:8081");
            // 是否发送 Cookie
            config.setAllowCredentials(true);
            // 支持请求方式
            config.addAllowedMethod("*");
            // 允许的原始请求头部信息
            config.addAllowedHeader("*");
            // 暴露的头部信息
            config.addExposedHeader("*");
            // 添加地址映射
            UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
            corsConfigurationSource.registerCorsConfiguration("/**", config);
            // 返回 CorsFilter 对象
            return new CorsFilter(corsConfigurationSource);
        }
    }
    

    3.4、通过 Response 对象

    在没有使用以上方式解决跨域的情况下,我们通过Response对象也可以实现跨域。

    @Slf4j
    @RestController
    @RequestMapping("/cross")
    public class CrossController {
    
        @PostMapping("/request")
        public String crossRequest(HttpServletRequest request, HttpServletResponse response) {
            response.setHeader("Access-Control-Allow-Origin", "*");
            log.info("接收到跨域请求参数:{}",request.getParameter("name"));
            return "Request CrossServer Success";
        }
    
    }
    

    3.5、通过实现 ResponseBodyAdvice 对象

    如果是嫌上述方法在每个方法里都要设置response.setHeader(“Access-Control-Allow-Origin”, “*”),那就可以统一处理返回。可以利用@ControllerAdvice和ResponseBodyAdvice通知完成。

    package com.alian.crossserver.advice;
    
    import org.springframework.core.MethodParameter;
    import org.springframework.http.MediaType;
    import org.springframework.http.server.ServerHttpRequest;
    import org.springframework.http.server.ServerHttpResponse;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
    
    @ControllerAdvice // 织入通知
    public class ResponseAdvice implements ResponseBodyAdvice {
    
        /**
         * 内容是否需要重写(返回true表示重写)
         *
         * @param returnType
         * @param converterType
         * @return
         */
        @Override
        public boolean supports(MethodParameter returnType, Class converterType) {
            return true;
        }
    
        /**
         * 放回结果前调用
         *
         * @param body
         * @param returnType
         * @param selectedContentType
         * @param selectedConverterType
         * @param request
         * @param response
         * @return
         */
        @Override
        public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
                                      Class selectedConverterType, ServerHttpRequest request,
                                      ServerHttpResponse response) {
            // 设置跨域
            response.getHeaders().set("Access-Control-Allow-Origin", "*");
            return body;
        }
    
    }
    
    • supports:判断是否要交给 beforeBodyWrite 方法执行,ture:需要;false:不需要
    • beforeBodyWrite:对 response 进行具体的处理

    3.6、通过 jsonp 方式

    可能还有些老系统spring版本是Spring Framework 4.2 GA之前的,比如3.X。可能上面有些方式也用不了,比如注解。这里我们可以使用jsonp方式,这里我们先改下我们的前端和后端。

    3.6.1、后端

    在之前的控制层增加一个接口jsonpRequest

    @Slf4j
    @RestController
    @RequestMapping("/cross")
    public class CrossController {
    
        @RequestMapping("/jsonpRequest")
        public String jsonpRequest(HttpServletRequest request, HttpServletResponse response,@RequestParam("callback") String callback) {
            log.info("1接收到跨域请求参数:姓名:{},年龄:{}",request.getParameter("name"),request.getParameter("age"));
            log.info("2接收到跨域请求参数:{}",callback);
            String json="{\"msg\":\"Request CrossServer Success\"}";
            return callback+"("+ json +")";
        }
    }
    

    这里需要注意的两个点:

    • 一个是请求的方法得支持GET方法(@RequestMapping就是POST、GET都支持)
    • 返回的结果转为json字符串,作为回调方法的参数

    3.6.2、前端方法一

    对应前端第一种方法是jsonp约定俗成的默认值为callback

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>test cross</title>
        <script type="text/javascript" src="https://code.jquery.com/jquery-3.0.0.min.js"></script>
    </head>
    <body>
    <h1>跨域接口测试</h1>
    <div>
        <input type="button" onclick="crossRequest()" value="发送跨域请求">
        <input type="button" onclick="crossCallBack()" value="老版本发送跨域请求">
    </div>
    <script>
        function crossRequest() {
            // 发送跨域请求
            jQuery.ajax({
                url: "http://localhost:8080/crossServer/cross/request",
                type: "POST",
                data: {"name": "alian"},
                success: function (result) {
                    alert("请求返回数据:" + result);
                }
            });
        }
    
    	// 只看这个方法
        function crossCallBack() {
            // 发送跨域请求
            jQuery.ajax({
                url: "http://localhost:8080/crossServer/cross/jsonpRequest",
                type: "POST",
                data: {"name": "alian","age": "28"},
                dataType: "jsonp", //指定服务器返回的数据类型
                jsonp: "callback",
                success: function (data) {
                    alert("success回调:"+JSON.stringify(data));
                }
            });
        }
    
    </script>
    </body>
    </html>
    
    • dataType:该参数必须要设置成jsonp
    • jsonp:该参数的值需要与服务器端约定(默认值为callback
    • 成功进入到success

    所以我的后端接口就有一个参数接收

    @RequestParam("callback") String callback
    

    3.6.3、前端方法二

    假设你不想用默认的方法,比如方法名占用了,怎么办呢?那就像下面这样自定义方法名:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>test cross</title>
        <script type="text/javascript" src="https://code.jquery.com/jquery-3.0.0.min.js"></script>
    </head>
    <body>
    <h1>跨域接口测试</h1>
    <div>
        <input type="button" onclick="crossRequest()" value="发送跨域请求">
        <input type="button" onclick="crossCallBack()" value="老版本发送跨域请求">
    </div>
    <script>
        function crossRequest() {
            // 发送跨域请求
            jQuery.ajax({
                url: "http://localhost:8080/crossServer/cross/request",
                type: "POST",
                data: {"name": "alian"},
                success: function (result) {
                    alert("请求返回数据:" + result);
                }
            });
        }
    
    	// 看这个方法
        function crossCallBack() {
            // 发送跨域请求
            jQuery.ajax({
                url: "http://localhost:8080/crossServer/cross/jsonpRequest?callback=jsonpCallBack",
                type: "POST",
                data: {"name": "alian","age": "28"},
                dataType: "jsonp", //指定服务器返回的数据类型
                jsonp: "jsonpCallBack",
                success: function (data) {
    
                }
            });
        }
    
    	// 这个是回调方法了,不会进入到success了
        function jsonpCallBack(data){
            var result = JSON.stringify(data); //json对象转成字符串
            alert("Jsonp请求返回数据:" + result);
        }
    </script>
    </body>
    </html>
    
    • 首先你得指定你的jsonp方法,比如我这里是jsonpCallBack,也就是回调方法
    • 同时我们请求里需要把回调方法名传递到后台,比如我这里callback=jsonpCallBack
    • 回调就会直接进入到我们的jsonpCallBack方法

    jsonpCallBack是自定义方法名,你也可以改成其他的名字,记得请求参数,请求地址,及其方法定义,三个一起改哦。

    两者的结果如下:

    总结

    跨域是浏览器的行为,跨域问题其实就是浏览器的同源策略所导致的,所以后端就没有跨域一说了。以上几种跨域解决方法单独用一个就行了,尽量不要混用,以免出现其他重复冲突的问题。还有其他一些通过nginx或者网关等方法都可以解决跨域问题,就不一一介绍了。

    以上就是Java实现后端跨域的常见解决方案的详细内容,更多关于Java后端跨域的资料请关注3672js教程其它相关文章!

    您可能感兴趣的文章:
    • Java前后端分离项目跨域问题解决方案
    • Java跨域问题的几种后端解决方式举例详解
    • java后端进行跨域的几种方式小结
    • java后端解决跨域的几种问题解决
    • vue+Java后端进行调试时解决跨域问题的方式
    • Cors实现java后端完全跨域实例
    相关栏目:

    用户点评