跨域是出于浏览器的同源策略限制。同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域。
同源策略是浏览器的一个安全功能,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源
场景:
前端调用接口接收文件流下载文件,以本地环境为例,系统域名为http://localhost:8090,接口域名为'http://127.0.0.1:8080,后端使用springboot
此时浏览器控制台报错:
Access to XMLHttpRequest at 'http://127.0.0.1:8080/zyfbpt/dbreport/export?domainKey=zyfzyfbpt' from origin 'http://localhost:8090' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
尝试一:接口返回文件流时响应头设置Access-Control-Allow-Origin
String origin = request.getHeader("Origin");
response.addHeader("Access-Control-Allow-Origin", origin);
重启服务后发现并没有生效,报错内容不变(有没有大佬帮忙分析下这样为什么不生效)
尝试二:使用@CrossOrigin注解
//1.在controller类上使用
@Controller
@CrossOrigin
public class MyController {
}
//2.在方法上使用
@CrossOrigin
@RequestMapping(value = "/test",method = RequestMethod.POST)
public void test(@RequestBody TestRequest testRequest){
}
直接加上后发现依然不生效,搜索后总结原因有以下几点:
情况一:在方法上使用@CrossOrigin注解,但@RequestMapping注解中没有指定Get、Post方式,通过method = RequestMethod.POST/GET指定或者指定@CrossOrigin注解中的methods = {RequestMethod.POST}后,问题解决。
//在@CrossOrigin中methods指定方式
@CrossOrigin(methods = {RequestMethod.POST})
//或者@RequestMapping注解定义method
@RequestMapping(value = "/test",method = {RequestMethod.POST})
情况二:跨域访问响应状态码是405-Method Not Allowed,请求行中指定的请求方法不能被用于请求相应的资源。这个是请求方式不正确,检查代码。
情况三:查看springboot版本,如果是2.0以后版本,allowCredentials属性的默认值为false,返回的响应头AccessControlAllowCredentials属性值也为false,如果客户端携带cookie的请求这时是不能跨域访问的,所以需要手动在注解中设置allowCredentials为true
例如:前端ajax请求参数withCredentials=true,表示请求带上cookie信息
//a标签下载文件
axios({
method: 'POST',
url: downloadUrl,
data: params,
withCredentials:true,
responseType: 'blob'
}).then(res => {
let url = window.URL.createObjectURL(new Blob([res.data]));
let link = document.createElement("a");
link.style.display = "none";
link.href = url;
link.setAttribute("download", `report-${Date.now()}.docx`);
document.body.appendChild(link);
link.click();
});
此时就需要后端设置allowCredentials=true解决跨域,然而这里如下配置后再请求报错了:
@CrossOrigin(allowCredentials = "true")
@RequestMapping(value = "/dbreport/export", method = RequestMethod.POST)
@ResponseBody
public Object export(@RequestBody JSONObject json HttpServletResponse response) {
}
后端报错:
java.lang.IllegalArgumentException: When allowCredentials is true, allowedOrigins cannot contain the special value "*" since that cannot be set on the "Access-Control-Allow-Origin" response header. To allow credentials to a set of origins, list them explicitly or consider using "allowedOriginPatterns" instead.
翻译过来就是:
当allowCredentials为真时,allowedorigin不能包含特殊值"*",因为不能在Access-Control-Allow-Origin响应头中设置该值。要允许凭证到一组origins,显示地列出它们,或者考虑使用"allowedOriginPatterns”代替。
解决:
按提示加上originPatterns = "*"后问题解决
@CrossOrigin( originPatterns = "*", allowCredentials = "true", allowedHeaders = "*")
@RequestMapping(value = "/dbreport/export", method = RequestMethod.POST)
@ResponseBody
public Object export(@RequestBody JSONObject json HttpServletResponse response) {
}
尝试三:全局CORS配置
配置后无需每个controller或方法上加@Configuration注解了
@Configuration
public class SpringMvcConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
// 放行哪些域名,可以多个
.allowedOriginPatterns("*")
// 是否发送Cookie信息
.allowCredentials(true)
// 允许的模式
.allowedOriginPatterns("*");
// 放行哪些请求方式
.allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS", "PATCH")
// 放行哪些原始域(头部信息)
.allowedHeaders("*")
// 暴露哪些头部信息(因为跨域访问默认不能获取全部头部信息)
.exposedHeaders("Header1", "Header2")
// 预请求的结果有效期,默认1800分钟,3600是一小时
.maxAge(3600);
}
}
搜索后发现有网友说此种配置有以下情况,但是我自己的接口就是用HttpServletResponse返回的文件流,没有加下边的响应头也成功了,供参考
适用情况:
1返回字符串或对象的接口
2.通过ResponseEntity提供文件下载功能的接口
不适用的情况:
直接适用HttpServletResponse进行内容返回的接口,需要在响应头加上下边内容
response.addHeader("Access-Control-Allow-Origin", "*");
response.addHeader("Access-Control-Allow-Methods", "POST,GET,OPTIONS,DELETE");
response.addHeader("Access-Control-Max-Age", "9600");
response.addHeader("Access-Control-Allow-Headers", "x-requested-with");
解决问题途中还遇到了下边的错误:
错误一:
此时前端请求带withCredentials=true,携带cookie请求
后端配置为:
@Configuration
public class SpringMvcConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**");
}
}
Access to XMLHttpRequest at 'http://127.0.0.1:8080/zyfbpt/dbreport/export?domainKey=zyfzyfbpt' from origin 'http://localhost:8090' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
原因:如果跨域请求设置了withCredentials:true, 浏览器发起请求的时候会携带上所在域的cookie信息,同时跨域标准规定请求响应header中Access-Control-Allow-Origin不能设置*,必须设置为具体的跨域域名。
解决方法一:
前端去掉withCredentials=true参数
解决方法二:
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowCredentials(true)
.allowedOriginPatterns("*");
}
错误二:
Access to XMLHttpRequest at 'http://127.0.0.1:8080/zyfbpt/dbreport/export?domainKey=zyfzyfbpt' from origin 'http://localhost:8090' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header contains multiple values '*, http://localhost:8090', but only one is allowed.
原因:Access-Control-Allow-Origin响应header重复添加了导致报错,一般出现该原因是因为运维 和 接口方同时添加了Access-Control-Allow-Origin的响应header。
解决:检查是否多处配置Access-Control-Allow-Origin,如CROS全局配置、contraller中的@CrossOrigin注解、代码中设置Access-Control-Allow-Origin响应头、nginx等网关层添加等,文章来源:https://www.toymoban.com/news/detail-759399.html
只保留一处即可文章来源地址https://www.toymoban.com/news/detail-759399.html
到了这里,关于跨域问题踩坑记录——附多种报错详细解决方案的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!