Skip to content

Instantly share code, notes, and snippets.

@yukihane
Last active July 28, 2021 03:03
Show Gist options
  • Save yukihane/2a8fde4c5e37aafacf638c4a7e25700c to your computer and use it in GitHub Desktop.
Save yukihane/2a8fde4c5e37aafacf638c4a7e25700c to your computer and use it in GitHub Desktop.
X-Forwarded-Proto, Port 書き換え
import java.io.IOException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedCaseInsensitiveMap;
import org.springframework.web.filter.ForwardedHeaderFilter;
/**
* 設定されているリクエストヘッダ {@code X-Forwarded-Proto}, {@code X-Forwarded-Port}
* の値を強制的に {@code https}, {@code 443} に書き換えるフィルタです。
*
* @see ForwardedHeaderFilter
*/
public class AltForwardedHeaderFilter extends ForwardedHeaderFilter {
@Override
protected void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response,
final FilterChain filterChain) throws ServletException, IOException {
final HttpServletRequest wrappedReq = new AltHttpServletRequest(request);
super.doFilterInternal(wrappedReq, response, filterChain);
}
/**
* 設定されているリクエストヘッダ {@code X-Forwarded-Proto}, {@code X-Forwarded-Port}
* の値を強制的に {@code https}, {@code 443} に書き換えます。
* Spring MVC の {@code ForwardedHeaderRemovingRequest} を参考に実装しています。
*
* @see AltForwardedHeaderFilter
*/
private static class AltHttpServletRequest extends HttpServletRequestWrapper {
private static final String HEADER_PROTO = "X-Forwarded-Proto";
private static final String HEADER_PORT = "X-Forwarded-Port";
/** 本来設定されるべき X-Forwarded-Proto の値 */
private static final String CORRECT_PROTO = "https";
/** 本来設定されるべき X-Forwarded-Port の値 */
private static final String CORRECT_PORT = "443";
private final Map<String, List<String>> headers;
public AltHttpServletRequest(final HttpServletRequest request) {
super(request);
headers = initHeaders(request);
}
private static Map<String, List<String>> initHeaders(final HttpServletRequest request) {
final Map<String, List<String>> headers = new LinkedCaseInsensitiveMap<>(Locale.ENGLISH);
final Enumeration<String> names = request.getHeaderNames();
while (names.hasMoreElements()) {
final String name = names.nextElement();
if (HEADER_PROTO.equalsIgnoreCase(name)) {
headers.put(name, List.of(CORRECT_PROTO));
} else if (HEADER_PORT.equalsIgnoreCase(name)) {
headers.put(name, List.of(CORRECT_PORT));
} else {
headers.put(name, Collections.list(request.getHeaders(name)));
}
}
return headers;
}
@Override
@Nullable
public String getHeader(final String name) {
final List<String> value = this.headers.get(name);
return (CollectionUtils.isEmpty(value) ? null : value.get(0));
}
@Override
public Enumeration<String> getHeaders(final String name) {
final List<String> value = this.headers.get(name);
return (Collections.enumeration(value != null ? value : Collections.emptySet()));
}
@Override
public Enumeration<String> getHeaderNames() {
return Collections.enumeration(this.headers.keySet());
}
}
}
server.forward-headers-strategy: framework
import java.util.Map;
import javax.servlet.DispatcherType;
import org.slf4j.MDC;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.core.task.TaskDecorator;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.session.web.http.CookieSerializer;
import org.springframework.session.web.http.DefaultCookieSerializer;
import org.springframework.web.filter.ForwardedHeaderFilter;
/**
* カスタムBean登録用コンフィグレーションクラス。
*/
@Configuration
public class MyConfig {
/**
* {@link ForwardedHeaderFilter} の代わりに {@link AltForwardedHeaderFilter}
* を利用するための設定です。
* デフォルトでは
* {@link ServletWebServerFactoryAutoConfiguration#forwardedHeaderFilter()}
* で実装されていますが、それを上書きしています。
*/
@Bean
@ConditionalOnProperty(value = "server.forward-headers-strategy", havingValue = "framework")
public FilterRegistrationBean<ForwardedHeaderFilter> altForwardedHeaderFilter() {
final ForwardedHeaderFilter filter = new AltForwardedHeaderFilter();
final FilterRegistrationBean<ForwardedHeaderFilter> registration = new FilterRegistrationBean<>(filter);
registration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC, DispatcherType.ERROR);
registration.setOrder(Ordered.HIGHEST_PRECEDENCE);
return registration;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment