Java实现后端跨域的常见解决方式
创始人
2024-03-25 21:07:28

目录

    • 一、搭建服务(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

org.springframework.bootspring-boot-starter-weborg.projectlomboklombok1.16.14

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

org.springframework.bootspring-boot-starter-weborg.projectlomboklombok1.16.14

2.2、接口

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

2.3、页面



跨域测试页面


跨域测试

2.4、配置

application.properties

server.port=8081
server.servlet.context-path=/crossWebspring.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 {@Overridepublic void addCorsMappings(CorsRegistry registry) {registry.addMapping("/**") // 所有接口.allowCredentials(true) // 是否发送 Cookie.allowedOriginPatterns("*") // 支持域.allowedMethods("GET", "POST", "PUT", "DELETE") // 支持方法.allowedHeaders("*").exposedHeaders("*");}
}

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

@Configuration
public class CorsConfig implements WebMvcConfigurer {@Overridepublic 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 {@Beanpublic CorsFilter corsFilter() {// 创建cors配置对象CorsConfiguration config = new CorsConfiguration();// 支持域config.addAllowedOriginPattern("*");// 是否发送 Cookieconfig.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 {@Beanpublic CorsFilter corsFilter() {// 创建cors配置对象CorsConfiguration config = new CorsConfiguration();// 支持指定域config.addAllowedOriginPattern("http://localhost:8081");// 是否发送 Cookieconfig.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*/@Overridepublic boolean supports(MethodParameter returnType, Class converterType) {return true;}/*** 放回结果前调用** @param body* @param returnType* @param selectedContentType* @param selectedConverterType* @param request* @param response* @return*/@Overridepublic 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



test cross


跨域接口测试

  • dataType:该参数必须要设置成jsonp
  • jsonp:该参数的值需要与服务器端约定(默认值为callback
  • 成功进入到success

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

@RequestParam("callback") String callback

3.6.3、前端方法二

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



test cross


跨域接口测试

  • 首先你得指定你的jsonp方法,比如我这里是jsonpCallBack,也就是回调方法
  • 同时我们请求里需要把回调方法名传递到后台,比如我这里callback=jsonpCallBack
  • 回调就会直接进入到我们的jsonpCallBack方法

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

  两者的结果如下:

在这里插入图片描述

总结

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

相关内容

热门资讯

应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...