一、搭建服务(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后端跨域的资料请关注代码网其它相关文章!
发表评论