Open nguyentriloi opened 4 years ago
See here: https://cloud.spring.io/spring-cloud-gateway/reference/html/#cors-configuration Have you set the property spring.cloud.gateway.globalcors.add-to-simple-url-handler-mapping=true?
See here: https://cloud.spring.io/spring-cloud-gateway/reference/html/#cors-configuration Have you set the property spring.cloud.gateway.globalcors.add-to-simple-url-handler-mapping=true?
I tried removing it earlier, but it still doesn't work
It is something you would add, not remove.
It is something you would add, not remove.
I added it from the beginning but it not work.
It is something you would add, not remove.
I added it from the beginning but it not work.
Maybe you should set ''http.cors()'' in SecurityWebFilterChain
It is something you would add, not remove.
I added it from the beginning but it not work.
Maybe you should set ''http.cors()'' in SecurityWebFilterChain
I have config it in the auth service, but is it needed in the gateway service?
Can you provide a complete, minimal, verifiable sample that reproduces the problem? It should be available as a GitHub (or similar) project or attached to this issue as a zip file.
Can you provide a complete, minimal, verifiable sample that reproduces the problem? It should be available as a GitHub (or similar) project or attached to this issue as a zip file.
This is my project. pls change config url in centralize-config to local before run. https://github.com/nguyentriloi/spring-cloud
As of June 2, 2020 I confirm this issue still exists. Prior to the problem, I was using Spring Boot 2.2.2 and Cloud Hoxton.SR1 and it worked fine at the time so I rolled back to that version.
It currently failed in our dev environments using Spring Boot 2.3.0 and Hoxton.SR5
July 07, 2021, i have the same problem the spring boot version i use is 2.4.8 and cloud version 2020.0.3, my application.yaml
is the following:
##Spring Configuration
base-path: /services/api/v1
gateway-path: ${base-path}/gateway
server:
port: 8888
spring:
application:
name: ms_gateway
devtools:
add-properties: false
cloud:
gateway:
default-filters:
- DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin, RETAIN_UNIQUE
- AddResponseHeader=Access-Control-Allow-Origin, *
globalcors:
cors-configurations:
'[/**]':
allowed-origins: '*'
allowed-methods:
- "GET"
- "POST"
- "PUT"
- "DELETE"
- "HEAD"
- "OPTIONS"
allowed-headers:
- "Origin"
- "Content-Type"
- "Accept"
- "Authorization"
- "User-Key"
- "Request-Tracker"
- "Session-Tracker"
- "X-XSRF-TOKEN"
- "X-IBM-CLIENT-ID"
- "Message-ID"
- "X-IBM-CLIENT-SECRET"
allow-credentials: true
add-to-simple-url-handler-mapping: true
webflux:
base-path: /
sleuth:
reactor:
instrumentation-type: manual
management:
endpoints:
enabled-by-default: true
web:
exposure:
include: health, mappings
base-path: ${gateway-path}
jmx:
exposure:
include: health
---
spring:
profiles:
group:
<netw>: dev, qa, pdn
cloud:
gateway:
routes:
- id: ms_company
uri: ${company-internal-endpoint}
predicates:
- Path= ${gateway-path}/company/**
filters:
- RewritePath=${gateway-path}/company(?<segment>/?.*), ${base-path}/company/$\{segment}
- HeaderCustomizer
and this is my configuration class:
@Configuration
@EnableWebFluxSecurity
@Slf4j
@EnableConfigurationProperties(value = {GlobalCorsProperties.class})
public class SecurityAutoConfiguration {
@Bean
public SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity http,
@Value("${jwt.secret}") String secret) {
log.info("Configuring SecurityWebFilterChain");
return http.securityMatcher(new NegatedServerWebExchangeMatcher(
ServerWebExchangeMatchers.pathMatchers("/services/api/v1/gateway/health"))
)
.formLogin(ServerHttpSecurity.FormLoginSpec::disable)
.httpBasic(ServerHttpSecurity.HttpBasicSpec::disable)
.logout(logout -> logout
.requiresLogout(new PathPatternParserServerWebExchangeMatcher("**/logout")))
.csrf(csrf -> csrf.csrfTokenRepository(new CookieServerCsrfTokenRepository()))
.addFilterBefore(JwtFilter.of(secret), SecurityWebFiltersOrder.AUTHORIZATION)
.cors()
.and()
.build();
}
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
@RefreshScope
public CorsWebFilter corsWebFilter(GlobalCorsProperties globalCorsProperties) {
var source = new UrlBasedCorsConfigurationSource();
globalCorsProperties.getCorsConfigurations().forEach(source::registerCorsConfiguration);
return new CorsWebFilter(source);
}
@PostConstruct
public void postConstruct() {
log.info("Starting SecurityAutoConfiguration");
}
}
and still got the problem
while postman and non browser request are done nicely.
EDIT: i also tried all the suggestions in #840 and it didn't work 😢
2021 年 7 月 7 日,我遇到了同样的问题,我使用的 Spring Boot 版本是 2.4.8 和云版本 2020.0.3,我
application.yaml
的问题如下:## Spring 配置 base-path : /services/api/v1 gateway-path : ${base-path}/gateway server : port : 8888 spring : application : name : ms_gateway devtools : add-properties : false cloud : gateway : default -filters : - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin, RETAIN_UNIQUE - AddResponseHeader=Access-Control-Allow-Origin, * globalcors : cors-configurations : ' [/**] ' : allowed-origins : ' * ' allowed-methods : - “ GET ” - “ POST ” - “ PUT ” - “ DELETE ” - “ HEAD ” - “ OPTIONS ” 允许- 标题: —— ”来源" - " Content-Type " - "接受" - "授权" - " User-Key " - " Request-Tracker " - " Session-Tracker " - " X-XSRF-TOKEN " - " X-IBM-CLIENT- ID " - "消息 ID " - " X-IBM-客户-秘密“ 允许凭据:真 添加到简单网址处理程序映射:真 webflux: 基本路径: / 侦探: 反应器: 仪器类型:手动 管理: 端点: 默认启用:真 网络: 曝光: 包括:健康,映射 基本路径: ${gateway-path} jmx: 曝光: 包括:健康 ---春天: 型材: 组: <NETW> :开发,QA,PDN云: 网关: 路线: - ID:ms_company URI:$ {公司内部端点}断言: - PATH = $ {网关路径} /company/**过滤器: - RewritePath=${gateway-path}/company(?<segment>/?.*), ${base-path}/company/$\{segment} - HeaderCustomizer
这是我的配置类:
@Configuration @EnableWebFluxSecurity @Slf4j @EnableConfigurationProperties(value = {GlobalCorsProperties.class}) public class SecurityAutoConfiguration { @Bean public SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity http, @Value("${jwt.secret}") String secret) { log.info("Configuring SecurityWebFilterChain"); return http.securityMatcher(new NegatedServerWebExchangeMatcher( ServerWebExchangeMatchers.pathMatchers("/services/api/v1/gateway/health")) ) .formLogin(ServerHttpSecurity.FormLoginSpec::disable) .httpBasic(ServerHttpSecurity.HttpBasicSpec::disable) .logout(logout -> logout .requiresLogout(new PathPatternParserServerWebExchangeMatcher("**/logout"))) .csrf(csrf -> csrf.csrfTokenRepository(new CookieServerCsrfTokenRepository())) .addFilterBefore(JwtFilter.of(secret), SecurityWebFiltersOrder.AUTHORIZATION) .cors() .and() .build(); } @Bean @Order(Ordered.HIGHEST_PRECEDENCE) @RefreshScope public CorsWebFilter corsWebFilter(GlobalCorsProperties globalCorsProperties){ VAR源= 新 UrlBasedCorsConfigurationSource(); globalCorsProperties 。getCorsConfigurations() 。forEach(source :: registerCorsConfiguration); 返回 新的 CorsWebFilter(来源); } @PostConstruct public void postConstruct () { 日志。info( “启动SecurityAutoConfiguration ” ); } }
仍然有问题
而邮递员和非浏览器请求做得很好。
编辑:我也尝试了#840 中的所有建议,但没有奏效😢
July 07, 2021, i have the same problem the spring boot version i use is 2.4.8 and cloud version 2020.0.3, my
application.yaml
is the following:##Spring Configuration base-path: /services/api/v1 gateway-path: ${base-path}/gateway server: port: 8888 spring: application: name: ms_gateway devtools: add-properties: false cloud: gateway: default-filters: - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin, RETAIN_UNIQUE - AddResponseHeader=Access-Control-Allow-Origin, * globalcors: cors-configurations: '[/**]': allowed-origins: '*' allowed-methods: - "GET" - "POST" - "PUT" - "DELETE" - "HEAD" - "OPTIONS" allowed-headers: - "Origin" - "Content-Type" - "Accept" - "Authorization" - "User-Key" - "Request-Tracker" - "Session-Tracker" - "X-XSRF-TOKEN" - "X-IBM-CLIENT-ID" - "Message-ID" - "X-IBM-CLIENT-SECRET" allow-credentials: true add-to-simple-url-handler-mapping: true webflux: base-path: / sleuth: reactor: instrumentation-type: manual management: endpoints: enabled-by-default: true web: exposure: include: health, mappings base-path: ${gateway-path} jmx: exposure: include: health --- spring: profiles: group: <netw>: dev, qa, pdn cloud: gateway: routes: - id: ms_company uri: ${company-internal-endpoint} predicates: - Path= ${gateway-path}/company/** filters: - RewritePath=${gateway-path}/company(?<segment>/?.*), ${base-path}/company/$\{segment} - HeaderCustomizer
and this is my configuration class:
@Configuration @EnableWebFluxSecurity @Slf4j @EnableConfigurationProperties(value = {GlobalCorsProperties.class}) public class SecurityAutoConfiguration { @Bean public SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity http, @Value("${jwt.secret}") String secret) { log.info("Configuring SecurityWebFilterChain"); return http.securityMatcher(new NegatedServerWebExchangeMatcher( ServerWebExchangeMatchers.pathMatchers("/services/api/v1/gateway/health")) ) .formLogin(ServerHttpSecurity.FormLoginSpec::disable) .httpBasic(ServerHttpSecurity.HttpBasicSpec::disable) .logout(logout -> logout .requiresLogout(new PathPatternParserServerWebExchangeMatcher("**/logout"))) .csrf(csrf -> csrf.csrfTokenRepository(new CookieServerCsrfTokenRepository())) .addFilterBefore(JwtFilter.of(secret), SecurityWebFiltersOrder.AUTHORIZATION) .cors() .and() .build(); } @Bean @Order(Ordered.HIGHEST_PRECEDENCE) @RefreshScope public CorsWebFilter corsWebFilter(GlobalCorsProperties globalCorsProperties) { var source = new UrlBasedCorsConfigurationSource(); globalCorsProperties.getCorsConfigurations().forEach(source::registerCorsConfiguration); return new CorsWebFilter(source); } @PostConstruct public void postConstruct() { log.info("Starting SecurityAutoConfiguration"); } }
and still got the problem
while postman and non browser request are done nicely.
EDIT: i also tried all the suggestions in #840 and it didn't work 😢
https://docs.spring.io/spring-security/site/docs/current/reference/html5/#cors when spring-webflux in security,you should replace gateway globalCors with explicit declare CorsConfigurationSource.CorsWebFilter is added due to http.cors(withDefaults()).
does it work with last recommendation ? if yes can u please share a sample config for spring cloud gateway with security enabled ?
does it work with last recommendation ? if yes can u please share a sample config for spring cloud gateway with security enabled ?
Yes, actually it worked with the following:
import co.com.retrival.security.filters.JwtFilter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.gateway.config.GlobalCorsProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.SecurityWebFiltersOrder;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.util.matcher.NegatedServerWebExchangeMatcher;
import org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher;
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers;
import org.springframework.web.cors.reactive.CorsConfigurationSource;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import javax.annotation.PostConstruct;
@Configuration
@EnableWebFluxSecurity
@Slf4j
@EnableConfigurationProperties(value = {GlobalCorsProperties.class})
public class ApiGatewaySecurityAutoConfiguration {
@Bean
public SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity http,
@Value("${jwt.secret}") String secret) {
log.info("Configuring SecurityWebFilterChain");
return http.securityMatcher(new NegatedServerWebExchangeMatcher(
ServerWebExchangeMatchers.pathMatchers(
"/excluded/paths/**")))
.formLogin(ServerHttpSecurity.FormLoginSpec::disable)
.httpBasic(ServerHttpSecurity.HttpBasicSpec::disable)
.logout(logout -> logout
.requiresLogout(new PathPatternParserServerWebExchangeMatcher("**/logout")))
.csrf(ServerHttpSecurity.CsrfSpec::disable)
.addFilterAt(JwtFilter.of(secret), SecurityWebFiltersOrder.AUTHORIZATION)
.cors()
.and()
.build();
}
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
@RefreshScope
public CorsWebFilter corsWebFilter(CorsConfigurationSource corsConfigurationSource) {
return new CorsWebFilter(corsConfigurationSource);
}
@Bean
public CorsConfigurationSource corsConfigurationSource(GlobalCorsProperties globalCorsProperties) {
var source = new UrlBasedCorsConfigurationSource();
globalCorsProperties.getCorsConfigurations().forEach(source::registerCorsConfiguration);
return source;
}
@PostConstruct
public void postConstruct() {
log.info("Starting ApiGatewaySecurityAutoConfiguration");
}
}
Both CorsWebFilter
and CorsConfigurationSource
beans were crucial for me, like said @Been24. it was a bit confusing that part from the doc, but after that config everything worked just fine
Also here is my application.yaml
Also note that im working now with Spring Boot 2.4.9
##Spring Configuration
base-path: /retrival/api/v1
gateway-path: ${base-path}/gateway
server:
port: 8888
spring:
application:
name: ms_gateway
devtools:
add-properties: false
h2:
console:
enabled: true
path: /h2
cloud:
gateway:
default-filters:
- DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin, RETAIN_FIRST
globalcors:
cors-configurations:
'[/**]':
allowed-origins: '*'
allowed-methods:
- "GET"
- "POST"
- "PUT"
- "DELETE"
- "HEAD"
- "OPTIONS"
allowed-headers:
- "Origin"
- "Content-Type"
- "Accept"
- "Authorization"
- "User-Key"
- "Request-Tracker"
- "Session-Tracker"
- "X-XSRF-TOKEN"
- "X-IBM-CLIENT-ID"
- "Message-ID"
- "X-IBM-CLIENT-SECRET"
add-to-simple-url-handler-mapping: true
webflux:
base-path: /
sleuth:
reactor:
instrumentation-type: manual
management:
endpoints:
enabled-by-default: true
web:
exposure:
include: health, mappings
base-path: ${gateway-path}
jmx:
exposure:
include: health
aws:
region: us-east-1
jwt:
secret: ${secret_token_var}
---
spring:
profiles:
group:
<netw>: dev, qa, pdn
cloud:
gateway:
routes:
- id: ms_company
uri: ${kube-retrival-internal-endpoint}
predicates:
- Path= ${gateway-path}/retrival/**
filters:
- RewritePath=${gateway-path}/retrival(?<segment>/?.*), ${base-path}/enterprie/$\{segment}
- HeaderCustomizer
@dgallego58 thanks for your reply , it works for me using the global config only in spring cloud gateway with security enabled , i am using spring boot version 2.5.3 and spring cloud 2020.0.3 :
globalcors:
add-to-simple-url-handler-mapping: true
corsConfigurations:
'[/**]':
allowedOrigins: "http://localhost:4200"
allowedOriginPatterns:
- "*.testDomain.com"
allowedMethods:
- GET
- POST
- PUT
- DELETE
- OPTIONS
default-filters:
- TokenRelay
- DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin, RETAIN_FIRST
does it work with last recommendation ? if yes can u please share a sample config for spring cloud gateway with security enabled ?
Yes, actually it worked with the following:
import co.com.retrival.security.filters.JwtFilter; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.cloud.gateway.config.GlobalCorsProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; import org.springframework.security.config.web.server.SecurityWebFiltersOrder; import org.springframework.security.config.web.server.ServerHttpSecurity; import org.springframework.security.web.server.SecurityWebFilterChain; import org.springframework.security.web.server.util.matcher.NegatedServerWebExchangeMatcher; import org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher; import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers; import org.springframework.web.cors.reactive.CorsConfigurationSource; import org.springframework.web.cors.reactive.CorsWebFilter; import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource; import javax.annotation.PostConstruct; @Configuration @EnableWebFluxSecurity @Slf4j @EnableConfigurationProperties(value = {GlobalCorsProperties.class}) public class ApiGatewaySecurityAutoConfiguration { @Bean public SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity http, @Value("${jwt.secret}") String secret) { log.info("Configuring SecurityWebFilterChain"); return http.securityMatcher(new NegatedServerWebExchangeMatcher( ServerWebExchangeMatchers.pathMatchers( "/excluded/paths/**"))) .formLogin(ServerHttpSecurity.FormLoginSpec::disable) .httpBasic(ServerHttpSecurity.HttpBasicSpec::disable) .logout(logout -> logout .requiresLogout(new PathPatternParserServerWebExchangeMatcher("**/logout"))) .csrf(ServerHttpSecurity.CsrfSpec::disable) .addFilterAt(JwtFilter.of(secret), SecurityWebFiltersOrder.AUTHORIZATION) .cors() .and() .build(); } @Bean @Order(Ordered.HIGHEST_PRECEDENCE) @RefreshScope public CorsWebFilter corsWebFilter(CorsConfigurationSource corsConfigurationSource) { return new CorsWebFilter(corsConfigurationSource); } @Bean public CorsConfigurationSource corsConfigurationSource(GlobalCorsProperties globalCorsProperties) { var source = new UrlBasedCorsConfigurationSource(); globalCorsProperties.getCorsConfigurations().forEach(source::registerCorsConfiguration); return source; } @PostConstruct public void postConstruct() { log.info("Starting ApiGatewaySecurityAutoConfiguration"); } }
Both
CorsWebFilter
andCorsConfigurationSource
beans were crucial for me, like said @Been24. it was a bit confusing that part from the doc, but after that config everything worked just fineAlso here is my
application.yaml
Also note that im working now with Spring Boot 2.4.9
##Spring Configuration base-path: /retrival/api/v1 gateway-path: ${base-path}/gateway server: port: 8888 spring: application: name: ms_gateway devtools: add-properties: false h2: console: enabled: true path: /h2 cloud: gateway: default-filters: - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin, RETAIN_FIRST globalcors: cors-configurations: '[/**]': allowed-origins: '*' allowed-methods: - "GET" - "POST" - "PUT" - "DELETE" - "HEAD" - "OPTIONS" allowed-headers: - "Origin" - "Content-Type" - "Accept" - "Authorization" - "User-Key" - "Request-Tracker" - "Session-Tracker" - "X-XSRF-TOKEN" - "X-IBM-CLIENT-ID" - "Message-ID" - "X-IBM-CLIENT-SECRET" add-to-simple-url-handler-mapping: true webflux: base-path: / sleuth: reactor: instrumentation-type: manual management: endpoints: enabled-by-default: true web: exposure: include: health, mappings base-path: ${gateway-path} jmx: exposure: include: health aws: region: us-east-1 jwt: secret: ${secret_token_var} --- spring: profiles: group: <netw>: dev, qa, pdn cloud: gateway: routes: - id: ms_company uri: ${kube-retrival-internal-endpoint} predicates: - Path= ${gateway-path}/retrival/** filters: - RewritePath=${gateway-path}/retrival(?<segment>/?.*), ${base-path}/enterprie/$\{segment} - HeaderCustomizer
Thanks, worked for me.
Describe the bug i'm using spring cloud Hoxton.SR3 and spring boot 2.2.6.RELEASE. i have a problem when using angular 2+ call to api-gateway : "Access to XMLHttpRequest at 'http://localhost:8080/auth/oauth/token' from origin 'http://localhost:4200' 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." config api-gateway : spring: cloud: gateway: default-filters: - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin routes: - id: auth-service uri: lb://auth-service predicates: - Path=/auth/ - id: product-service uri: lb://product-service predicates: - Path=/product/ - id: order-service uri: lb://order-service predicates: - Path=/order/ - id: payment-service uri: lb://payment-service predicates: - Path=/payment/ globalcors: corsConfigurations: '[/*]': allowedOrigins: "" allowedHeader: "" allowCredentials: true allowedMethods: "" add-to-simple-url-handler-mapping: true i have tried add @crossorigin in gateway main class or add method Options in predicates in route[i] but it not work. Please help me . I think the problem lies in the version of the cloud
Maybe you can refer to this: https://github.com/spring-cloud/spring-cloud-gateway/issues/2472#issuecomment-1233659197
If set to "allowCredentials: true", the correct domain must be specified in "allowedOrigins". Or set to "allowCredentials: false". However, if set to "allowCredentials: false", the client's authorization information is not sent to the server. Therefore, the correct domain must be specified for "allowedOrigins".
If set to "allowCredentials: true", the correct domain must be specified in "allowedOrigins". Or set to "allowCredentials: false". However, if set to "allowCredentials: false", the client's authorization information is not sent to the server. Therefore, the correct domain must be specified for "allowedOrigins".
What's the format for the domain? I just spent a few days trying to configure a web product that would not work if I added a trailing slash after the domain url. Better yet, give me an example.
This is driving me crazy, no matter how I configure the oAuth on the Spring Cloud Gateway the OPTIONS calls are all protected by security (returning a 401 every time a preflight request comes in).
How do I disable the security on just the OPTIONS calls?
Standard setup for my gateway:
spring:
cloud:
gateway:
globalcors:
add-to-simple-url-handler-mapping: true
cors-configurations:
"[/**]":
allowedOrigins: "http://localhost:4200"
allowedHeaders: "*"
allowedMethods: "*"
allowCredentials: true
routes:
- id: product_api
uri: http://localhost:8090
predicates:
- Path=/product-api/{segment}
filters:
- RewritePath=/product-api/?(?<segment>.*), /product/v1/$\{segment}
- id: customer_api
uri: http://localhost:8070
predicates:
- Path=/customer-api
filters:
- RewritePath=/customer-api/?(?<segment>.*), /customer/v1/$\{segment}
- id: order-api
uri: http://localhost:8060
predicates:
- Path=/order-api/{segment}
filters:
- RewritePath=/order-api/?(?<segment>.*), /order/v1/$\{segment}
default-filters:
- DedupeResponseHeader=Access-Control-Allow-Origin Access-Control-Allow-Credentials, RETAIN_UNIQUE
security:
oauth2:
resourceserver:
opaquetoken:
client-id: portal-global
client-secret: <<secret>>
introspection-uri: http://localhost:8080/realms/my-realm/protocol/openid-connect/token/introspect
Every call to the OPTIONS preflight ends up with a 401:
❯ http --print=BbHh OPTIONS http://localhost:8010/product-api/67909821 "Origin: http://localhost:4200" Access-Control-Request-Method:GET
OPTIONS /product-api/67909821 HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Access-Control-Request-Method: GET
Connection: keep-alive
Host: localhost:8010
Origin: http://localhost:4200
User-Agent: HTTPie/3.2.2
HTTP/1.1 401 Unauthorized
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Expires: 0
Pragma: no-cache
Referrer-Policy: no-referrer
WWW-Authenticate: Bearer
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 0
content-length: 0
@anthonyikeda did you declare the beans of type CorsWebFilter and CorsConfigurationSource in your security config? those were the ones that helped me.
@anthonyikeda did you declare the beans of type CorsWebFilter and CorsConfigurationSource in your security config? those were the ones that helped me.
@dgallego58 I've tried those options, however, it seems that the Spring Security oAuth does a blanket protection of all methods and, while I've seen people overriding those options in code, it hasn't proven successful for me as yet (not sure if this a spring security version or webflux version problem).
Don't get me wrong, if a Bearer token is supplied with the OPTIONS calls, CORS works, though I'm not sure what the contract is here: if OPTIONS should be secured (which the Angular Keycloak client says it shouldn't) or if it should be secured (as per Spring Boot).
There don't seem to be any specs that outline this as a rule, nor do the different frameworks have an agreed upon approach.
❯ http --print=BbHh OPTIONS http://localhost:8010/product-api/67909821 "Origin: http://localhost:4200" Access-Control-Request-Method:GET "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCIg..."
OPTIONS /product-api/67909821 HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Access-Control-Request-Method: GET
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCIg...
Connection: keep-alive
Host: localhost:8010
Origin: http://localhost:4200
User-Agent: HTTPie/3.2.2
Access-Control-Allow-Methods: GET
Access-Control-Allow-Origin: *
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Expires: 0
Pragma: no-cache
Referrer-Policy: no-referrer
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 0
content-length: 0
Let me ask you this question: if it’s a secured endpoint, why would you allow unauthenticated access to the endpoint with an OPTIONS call?
The OPTIONS request is typically triggered by a browser because of cross-origin resource sharing (CORS). If I remember correctly the browser passes the same headers from the original call, thus also passing the Authorization
header.
Yeah I don't know whats going on, it's working again today. I'm going to keep monitoring the application.
As for browser sending the auth header on an options call, from the web browser console, from what's being logged, calls don't use the Authorization header when making pre-flight OPTIONS call - at least not that is being logged. Which is also being made through the framework making the pre-flight call.
There is some config options that I discovered for Angular Keycloak that determines if the Authorization header is added (documentation positions it as omitting the Authorization header - https://github.com/mauriciovigolo/keycloak-angular#httpclient-interceptor) With or without the Angular configuration change I'm still not seeing the Authorization header sent on the pre-flight call.
Name | Value |
---|---|
Accept: | / |
Accept-Encoding: | gzip, deflate, br |
Accept-Language: | en-US,en;q=0.9 |
Access-Control-Request-Headers: | authorization |
Access-Control-Request-Method: | GET |
Connection: | keep-alive |
Host: | localhost:8010 |
Origin: | http://localhost:4200 |
Referer: | http://localhost:4200/ |
Sec-Fetch-Dest: | empty |
Sec-Fetch-Mode: | cors |
Sec-Fetch-Site: | same-site |
User-Agent: | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36 Edg/117.0.2045.35 |
Okay so it started working again because I'd taken the gateway resource server security config out (no longer authenticating at the gateway)
I did come across this though:
https://stackoverflow.com/questions/68143581/how-to-force-authentication-in-preflight-request
There’s no way to force authentication in a preflight request. The preflight is controlled totally by the browser, and nothing about it is exposed in any way that you can manipulate from frontend JavaScript code. And the requirements for the CORS protocol explicitly prohibit browsers from including any credentials in preflight requests.
For a detailed explanation, see the answer at https://stackoverflow.com/a/45406085/.
You need to configure the server to not require authorization for the
OPTIONS
requests...
- Your browser sends the
OPTIONS
reuquest without theAuthorization
header, because the whole purpose of theOPTIONS
check is to see if it's okay to include that header.
So the question is, @TYsewyn, how do I remove the authentication on the OPTIONS requests?
This finally fixed it, it wasn't just adding a CorsWebFilter Bean, but also setting the @Order(Ordered.HIGHEST_PRECEDENCE)
:
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
CorsWebFilter corsWebFilter(GlobalCorsProperties properties) {
CorsConfiguration corsConfig = properties.getCorsConfigurations().get("/**");
UrlBasedCorsConfigurationSource source =
new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", corsConfig);
return new CorsWebFilter(source);
}
Leaves me to wonder how to re-prioritize the globalcors
configuration.
Okay so I'm assuming the CorsWebFilter
is not automatically created when you declare globalcors
in the application.yaml
file. By injecting the GlobalCorsProperties
, however, can ensure the order and configuration gets applied.
I have no idea why there is no CorsFilter by default or if the add-to-simple-url-handler-mapping
actually does anything, but I'm guessing there is something missing in the Spring Cloud Gateway code to wire this all up.
A search for uses of GlobalCorsProperties yields zero (0) uses in the local classpath - is there a specific dependency that it is tied to?
If set to "allowCredentials: true", the correct domain must be specified in "allowedOrigins". Or set to "allowCredentials: false". However, if set to "allowCredentials: false", the client's authorization information is not sent to the server. Therefore, the correct domain must be specified for "allowedOrigins".
This was the solution that worked for me. I was putting * in allowedOrigins and always had the same "Access-Control-Allow-Origin" error.
I think this requirement should be in the documentation.
IIRC the Access-Control-Allow-Origin header wasn't being added to each subsequent request, e.g. GET /endpoint/path
and I somewhat had to hardcode it.
Describe the bug i'm using spring cloud Hoxton.SR3 and spring boot 2.2.6.RELEASE. i have a problem when using angular 2+ call to api-gateway : "Access to XMLHttpRequest at 'http://localhost:8080/auth/oauth/token' from origin 'http://localhost:4200' 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." config api-gateway : spring: cloud: gateway: default-filters: