SpringBoot跨域问题
意思是:当allowCredentials为true时,allowingOrigins不能包含特殊值“ *”,因为无法在“ Access-Control-Allow-Origin”响应标头上设置。要允许凭据具有一组来源,请明确列出它们或考虑改用“ allowedOriginPatterns”。
解决:将allowingOrigins
换成allowedOriginPatterns
即可。
修改前:
public CorsFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
//允许所有域名进行跨域调用
config.addAllowedOrigin("*");
//允许跨越发送cookie
config.setAllowCredentials(true);
//放行全部原始头信息
config.addAllowedHeader("*");
//允许所有请求方法跨域调用
config.addAllowedMethod("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
修改后:
public CorsFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
//允许所有域名进行跨域调用
config.addAllowedOriginPattern("*");//替换这个
//允许跨越发送cookie
config.setAllowCredentials(true);
//放行全部原始头信息
config.addAllowedHeader("*");
//允许所有请求方法跨域调用
config.addAllowedMethod("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
Strict-origin-when-cross-origin跨域问题
SpringBoot出现strict-origin-when-cross-origin跨域报错
问题详情:
后端接口通过网关可以访问成功,前端报接口跨域问题
console打印如下:
解决原因:
这个问题的原因是在于服务器端设置了两次跨域,只需要设置一次就可以。
有可能是Nginx/Gateway设置了一次,服务端代码又设置了一次。 解决方法:删除代码中的跨域设置或者删除Nginx的跨域设置。
1. Host
描述请求将被发送的目的地,包括且仅仅包括域名和端口号。 HTTP/1.1 的所有请求报文中必须包含一个Host头字段,且只能设置一个。
2. Origin
请求头origin表明了请求来自于哪个站点。包括且仅仅包括协议、域名和端口,并不包含任何路径信息。经常用于CORS请求或者POST请求,可以看到response有对应的
header:Access-Control-Allow-Origin。
@CrossOrigin(origins="http://wx.xxxx.com:80")
@RequestMapping(value = "list", method = RequestMethod.POST)
public String list(HttpServletRequest request, HttpServletResponse response) {
}
3. Referer
告知服务器请求的原始资源的URI,其用于所有类型的请求,并且包括协议、域名、路径和查询参数。
在以下几种情况Referer不会被发送:
1)来源页面采用的协议为表示本地文件的 "file" 或者 "data" URI;
2)当前请求页面采用的是非安全协议,而来源页面采用的是安全协议(HTTPS);
3)直接输入网址或通过浏览器书签访问;
4)使用 JavaScript 的 Location.href 或者是 Location.replace();
5)使用html5中noreferrer;
6)使用iframe的hack写法去除referer。
请求头中referer与origin功能相似,但有如下几点不同:
1、只有跨域请求,或者同域时发送post请求,才会携带origin请求头,而referer不论何种情况下,只要浏览器能获取到请求源都会携带,除了上面提到的几种情况。
2、如果浏览器不能获取请求源,那么origin满足上面情况也会携带,不过其值为null。referer则不同,浏览器如果不能获取请求源,那么请求头中不会携带referer。
3、origin的值只包括协议、域名和端口,而erferer不但包括协议、域名、端口,还包括路径和参数。
参考文档:
报错信息如下:
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
为true时,allowedOrigins
不能包含特殊值“*”,因为不能在“Access Control Allow Origin
”响应头上设置该值。要允许凭据指向一组源,请显式列出它们,或者考虑改用“allowedOriginPatterns
”。
解决办法:跨域配置报错,将.allowedOrigins
替换成.allowedOriginPatterns
即可。
当你遇到跨域问题,不要立刻就选择复制去尝试。请详细看完这篇文章再处理,我相信它能帮到你。
准备
- 前端网站地址:http://localhost:8080
- 服务端网址:http://localhost:59200
首先保证服务端是没有处理跨域的,其次,先用postman测试服务端接口是正常的
当网站8080去访问服务端接口时,就产生了跨域问题,那么如何解决?接下来我把跨域遇到的各种情况都列举出来并通过nginx代理的方式解决(后台也是一样的,只要你理解的原理)。
跨域主要涉及4个响应头
- Access-Control-Allow-Origin 用于设置允许跨域请求源地址 (预检请求和正式请求在跨域时候都会验证)
- Access-Control-Allow-Headers 跨域允许携带的特殊头信息字段 (只在预检请求验证)
- Access-Control-Allow-Methods 跨域允许的请求方法或者说HTTP动词 (只在预检请求验证)
- Access-Control-Allow-Credentials 是否允许跨域使用cookies,如果要跨域使用cookies,可以添加上此请求响应头,值设为true(设置或者不设置,都不会影响请求发送,只会影响在跨域时候是否要携带cookies,但是如果设置,预检请求和正式请求都需要设置)。不过不建议跨域使用(项目中用到过,不过不稳定,有些浏览器带不过去),除非必要,因为有很多方案可以代替。
网上很多文章都是告诉你直接Nginx添加这几个响应头信息就能解决跨域,当然大部分情况是能解决,但是我相信还是有很多情况,明明配置上了,也同样会报跨域问题。
什么是预检请求?
当发生跨域条件时候,览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。如下图
模拟
Nginx代理端口:22222 ,配置如下
server {
listen 22222;
server_name localhost;
location / {
proxy_pass http://localhost:59200;
}
}
测试代理是否成功,通过Nginx代理端口2222再次访问接口,可以看到如下图通过代理后接口也是能正常访问
接下来开始用网站8080访问Nginx代理后的接口地址,报错情况如下↓↓↓
情况1
Access to XMLHttpRequest at 'http://localhost:22222/api/Login/TestGet' from origin 'http://localhost:8080' 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.
通过错误信息可以很清晰的定位到错误(注意看标红部分)priflight说明是个预请求,CORS 机制跨域会首先进行 preflight(一个 OPTIONS 请求), 该请求成功后才会发送真正的请求。这一设计旨在确保服务器对 CORS 标准知情,以保护不支持 CORS 的旧服务器
通过错误信息,我们可以得到是预检请求的请求响应头缺少了 Access-Control-Allow-Origin,错哪里,我们改哪里就好了。修改Nginx配置信息如下(红色部分为添加部分),缺什么就补什么,很简单明了
server {
listen 22222;
server_name localhost;
location / {
add_header Access-Control-Allow-Origin 'http://localhost:8080';
proxy_pass http://localhost:59200;
}
}
哈哈,当满怀欢喜的以为能解决后,发现还是报了同样的问题
不过我们的配置没什么问题,问题在Nginx,下图链接http://nginx.org/en/docs/http/ngx_http_headers_module.html
add_header
指令用于添加返回头字段,当且仅当状态码为图中列出的那些时有效。如果想要每次响应信息都携带头字段信息,需要在最后添加always(经我测试,只有Access-Control-Allow-Origin这个头信息需要加always,其他的不加always也会携带回来),那我们加上试试
server {
listen 22222;
server_name localhost;
location / {
add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
proxy_pass http://localhost:59200;
}
}
修改了配置后,发现生效了,当然不是跨域就解决了,是上面这个问题已经解决了,因为报错内容已经变了
情况2
Access to XMLHttpRequest at 'http://localhost:22222/api/Login/TestGet' from origin 'http://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
通过报错信息提示可以得知,是跨域浏览器默认行为的预请求(option请求)没有收到ok状态码,此时再修改配置文件,当请求为option请求时候,给浏览器返回一个状态码(一般是204)
server {
listen 22222;
server_name localhost;
location / {
add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
if ($request_method = 'OPTIONS') {
return 204;
}
proxy_pass http://localhost:59200;
}
}
当配置完后,发现报错信息变了
情况3
Access to XMLHttpRequest at 'http://localhost:22222/api/Login/TestGet' from origin 'http://localhost:8080' has been blocked by CORS policy: Request header field authorization is not allowed by Access-Control-Allow-Headers in preflight response.
意思就是预请求响应头Access-Control-Allow-Headers中缺少头信息authorization(各种情况会不一样,在发生跨域后,在自定义添加的头信息是不允许的,需要添加到请求响应头Access-Control-Allow-Headers中,以便浏览器知道此头信息的携带是服务器承认合法的,我这里携带的是authorization,其他的可能是token之类的,缺什么加什么),知道了问题所在,然后修改配置文件,添加对应缺少的部分,再试试
server {
listen 22222;
server_name localhost;
location / {
add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Headers 'authorization'; #为什么写在if里面而不是接着Access-Control-Allow-Origin往下写?因为这里只有预检请求才会检查
return 204;
}
proxy_pass http://localhost:59200;
}
}
此时发现报错问题又回到了情况1
经测试验证,只要if ($request_method = 'OPTIONS')
里面写了 add_header
,当为预检请求时外部配置的都会失效,为什么?↓↓。
官方文档是这样说的:
There could be several add_header directives. These directives are inherited from the previous level if and only if there are no add_header directives defined on the current level.
意思就是当前层级无 add_header 指令时,则继承上一层级的add_header。相反的若当前层级有了add_header,就应该无法继承上一层的add_header。
配置修改如下:
server {
listen 22222;
server_name localhost;
location / {
add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin 'http://localhost:8080';
add_header Access-Control-Allow-Headers 'authorization';
return 204;
}
proxy_pass http://localhost:59200;
}
}
此时改完发现跨域问题已经解决了,
不过以上虽然解决了跨域问题,但是考虑后期可能Nginx版本更新,不知道这个规则会不会被修改,考虑到这样的写法可能会携带上两个 Access-Control-Allow-Origin ,这种情况也是不允许的,下面会说到。所以配置适当修改如下:
server {
listen 22222;
server_name localhost;
location / {
if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin 'http://localhost:8080';
add_header Access-Control-Allow-Headers 'authorization';
return 204;
}
if ($request_method != 'OPTIONS') {
add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
}
proxy_pass http://localhost:59200;
}
}
还没完,继续聊 ↓↓
情况4:
比较早期的API可能只用到了POST和GET请求,而Access-Control-Allow-Methods这个请求响应头跨域默认只支持POST和GET,当出现其他请求类型时候,同样会出现跨域异常。
比如,我这里将请求的API接口请求方式从原来的GET改成PUT,在发起一次试试。在控制台上会抛出错误:
Access to XMLHttpRequest at 'http://localhost:22222/api/Login/TestGet' from origin 'http://localhost:8080' has been blocked by CORS policy: Method PUT is not allowed by Access-Control-Allow-Methods in preflight response.
报错内容也讲的很清楚,在这个预请求中,PUT方法是不允许在跨域中使用的,我们需要改下Access-Control-Allow-Methods的配置(缺什么加上么,这里我只加了PUT,可以自己加全一点),让浏览器知道服务端是允许的
server {
listen 22222;
server_name localhost;
location / {
if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin 'http://localhost:8080';
add_header Access-Control-Allow-Headers 'content-type,authorization';
add_header Access-Control-Allow-Methods 'PUT';#为这么只加在这个if中,不再下面的if也加上?因为这里只有预检请求会校验,当然你加上也没事。
return 204;
}
if ($request_method != 'OPTIONS') {
add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
}
proxy_pass http://localhost:59200;
}
}
这里注意一下,改成PUT类型后,Access-Control-Allow-Headers请求响应头又会自动校验content-type这个请求头,和情况3是一样的,缺啥补啥就行了。如果不加上content-type,则会报如下错误。(想简单的话,Access-Control-Allow-Headers和Access-Control-Allow-Methods可以设置为 * ,表示全都匹配。但是Access-Control-Allow-Origin就不建议设置成 * 了,为了安全考虑,限制域名是很有必要的。)
都加上后,问题就解决了,这里报405是我服务端这个接口只开放了GET,没有开放PUT,而此刻我将此接口用PUT方法去请求,所以接口会返回这个状态码。
情况5:
最后再说一种情况,就是后端处理了跨域,就不需要自己在处理了(这里吐槽下,某些后端工程师自己改服务端代码解决跨域,但是又不理解其中原理,网上随便找段代码黏贴,导致响应信息可能处理不完全,如method没添加全,headers没加到点上,自己用的那个可能复制过来的并不包含实际项目所用到的,没有添加options请求返回状态码等,导致Nginx再用通用的配置就会可能报以下异常)
Access to XMLHttpRequest at 'http://localhost:22222/api/Login/TestGet' from origin 'http://localhost:8080' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header contains multiple values '*, http://localhost:8080', but only one is allowed.
意思就是此刻Access-Control-Allow-Origin请求响应头返回了多个,而只允许有一个,这种情况当然修改配置去掉Access-Control-Allow-Origin这个配置就可以了,不过遇到这种情况,建议Nginx配置和服务端自己解决跨域只选其一。(这里注意如果按我上面的写法,if $request_method = 'OPTIONS' 这个里面的Access-Control-Allow-Origin可不能删除,删除!='OPTIONS'里面的就好了,因为这里如果是预检请求直接就ruturn了,请求不会再转发到59200服务,如果也删除了,就会报和情况1一样的错误。所以为什么说要不服务端代码层面解决跨域,要不就Nginx代理解决,不要混着搞,不然不明白原理的人,网上找一段代码贴就很可能解决不了问题)
再贴一份完整配置(*号根据自己‘喜好’填写):
server {
listen 22222;
server_name localhost;
location / {
if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin 'http://localhost:8080';
add_header Access-Control-Allow-Headers '*';
add_header Access-Control-Allow-Methods '*';
add_header Access-Control-Allow-Credentials 'true';
return 204;
}
if ($request_method != 'OPTIONS') {
add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
add_header Access-Control-Allow-Credentials 'true';
}
proxy_pass http://localhost:59200;
}
}
或者:
server {
listen 22222;
server_name localhost;
location / {
add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
add_header Access-Control-Allow-Headers '*';
add_header Access-Control-Allow-Methods '*';
add_header Access-Control-Allow-Credentials 'true';
if ($request_method = 'OPTIONS') {
return 204;
}
proxy_pass http://localhost:59200;
}
}
最后,这是一篇解决跨域遇到问题解决问题的过程,如果认真看完了,我相信应该都能很容易的理解,并且在实际使用中自己解决该问题,希望能帮助到大家,以上内容都是自己理解自己测试码出来的,如有理解不对的地方,望大家指正。
来源:http://cnblogs.com/fnz0/p/15803011.html
-
在springboot中配置跨域时,出现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.
浏览器显示500服务器报错
需要将配置类中的corsConfiguration.addAllowedOrigin("");修改成为corsConfiguration.addAllowedOriginPattern("");修改前的代码:
package com.oolong.gulimall.gateway.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.reactive.CorsWebFilter; import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource; /** * @author oolong */ @Configuration public class GulimallCorsConfiguration { /** * springboot提供了CorsWebFilter进行跨域 * * @return */ @Bean public CorsWebFilter corsWebFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); // 配置跨域 CorsConfiguration corsConfiguration = new CorsConfiguration(); // 允许哪个请求头 corsConfiguration.addAllowedHeader("*"); // 允许哪个方法进行跨域 corsConfiguration.addAllowedMethod("*"); // 允许哪个请求来源进行跨域 corsConfiguration.addAllowedOrigin("*"); // 是否允许携带cookie进行跨域 corsConfiguration.setAllowCredentials(true); source.registerCorsConfiguration("/**",corsConfiguration); return new CorsWebFilter(source); } }
修改后:
package com.oolong.gulimall.gateway.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.reactive.CorsWebFilter; import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource; /** * @author oolong */ @Configuration public class GulimallCorsConfiguration { /** * springboot提供了CorsWebFilter进行跨域 * * @return */ @Bean public CorsWebFilter corsWebFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); // 配置跨域 CorsConfiguration corsConfiguration = new CorsConfiguration(); // 允许哪个请求头 corsConfiguration.addAllowedHeader("*"); // 允许哪个方法进行跨域 corsConfiguration.addAllowedMethod("*"); // 允许哪个请求来源进行跨域 // corsConfiguration.addAllowedOrigin("*"); corsConfiguration.addAllowedOriginPattern("*"); // 是否允许携带cookie进行跨域 corsConfiguration.setAllowCredentials(true); source.registerCorsConfiguration("/**",corsConfiguration); return new CorsWebFilter(source); } }
这是因为springboot升级成2.4.0以上时对AllowedOrigin设置发生了改变,不能有”*“