1616
1717package com .google .adk .web .config ;
1818
19+ import jakarta .servlet .DispatcherType ;
1920import org .slf4j .Logger ;
2021import org .slf4j .LoggerFactory ;
22+ import org .springframework .beans .factory .ObjectProvider ;
23+ import org .springframework .boot .autoconfigure .condition .ConditionalOnProperty ;
24+ import org .springframework .boot .web .server .autoconfigure .servlet .ForwardedHeaderFilterCustomizer ;
25+ import org .springframework .boot .web .servlet .FilterRegistrationBean ;
2126import org .springframework .context .annotation .Bean ;
2227import org .springframework .context .annotation .Configuration ;
28+ import org .springframework .core .Ordered ;
2329import org .springframework .web .cors .CorsConfiguration ;
2430import org .springframework .web .cors .CorsConfigurationSource ;
2531import org .springframework .web .cors .UrlBasedCorsConfigurationSource ;
2632import org .springframework .web .filter .CorsFilter ;
33+ import org .springframework .web .filter .ForwardedHeaderFilter ;
2734
2835/**
2936 * Configuration class for setting up Cross-Origin Resource Sharing (CORS) in the ADK Web
3946 * <li>{@link CorsConfigurationSource}: Configures CORS settings such as allowed origins, methods,
4047 * headers, credentials, and max age.
4148 * <li>{@link CorsFilter}: Applies the CORS configuration to incoming requests.
49+ * <li>{@link ForwardedHeaderFilter}: Applies reverse proxy headers to the URL of every request,
50+ * so the same-origin checks see the URL the browser used.
4251 * </ul>
4352 */
4453@ Configuration
@@ -50,10 +59,10 @@ public class AdkWebCorsConfig {
5059 public CorsConfigurationSource corsConfigurationSource (AdkWebCorsProperties corsProperties ) {
5160 if (corsProperties .origins ().contains ("*" )) {
5261 logger .warn (
53- "CORS is configured to allow all origins (\" *\" ), which is insecure and intended for "
54- + " local development only. This also applies to the /run_live WebSocket endpoint."
55- + " Set 'adk.web.cors.origins' to an explicit allowlist to restrict which origins"
56- + " may call the server." );
62+ "CORS is configured to allow all origins (\" *\" ), so any website open in a browser can "
63+ + " call this server, including the /run_live WebSocket endpoint. Set "
64+ + " 'adk.web.cors.origins' to an explicit allowlist to restrict which origins may "
65+ + " call the server." );
5766 }
5867
5968 CorsConfiguration configuration = new CorsConfiguration ();
@@ -74,4 +83,26 @@ public CorsConfigurationSource corsConfigurationSource(AdkWebCorsProperties cors
7483 public CorsFilter corsFilter (CorsConfigurationSource corsConfigurationSource ) {
7584 return new CorsFilter (corsConfigurationSource );
7685 }
86+
87+ /**
88+ * Makes {@code server.forward-headers-strategy=framework} the default, so same-origin checks see
89+ * the URL the browser used behind a TLS-terminating proxy. Registered like Spring Boot's own
90+ * filter, which then backs off; set the property to {@code native} or {@code none} to opt out.
91+ */
92+ @ Bean
93+ @ ConditionalOnProperty (
94+ name = "server.forward-headers-strategy" ,
95+ havingValue = "framework" ,
96+ matchIfMissing = true )
97+ FilterRegistrationBean <ForwardedHeaderFilter > adkForwardedHeaderFilter (
98+ ObjectProvider <ForwardedHeaderFilterCustomizer > customizers ) {
99+ ForwardedHeaderFilter filter = new ForwardedHeaderFilter ();
100+ customizers .orderedStream ().forEach (customizer -> customizer .customize (filter ));
101+ FilterRegistrationBean <ForwardedHeaderFilter > registration =
102+ new FilterRegistrationBean <>(filter );
103+ registration .setDispatcherTypes (
104+ DispatcherType .REQUEST , DispatcherType .ASYNC , DispatcherType .ERROR );
105+ registration .setOrder (Ordered .HIGHEST_PRECEDENCE );
106+ return registration ;
107+ }
77108}
0 commit comments