All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.web.server.adapter.ForwardedHeaderTransformer Maven / Gradle / Ivy

There is a newer version: 6.1.6
Show newest version
/*
 * Copyright 2002-2020 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.web.server.adapter;

import java.net.InetSocketAddress;
import java.net.URI;
import java.util.Collections;
import java.util.Locale;
import java.util.Set;
import java.util.function.Function;

import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.lang.Nullable;
import org.springframework.util.LinkedCaseInsensitiveMap;
import org.springframework.util.StringUtils;
import org.springframework.web.util.UriComponentsBuilder;

/**
 * Extract values from "Forwarded" and "X-Forwarded-*" headers to override
 * the request URI (i.e. {@link ServerHttpRequest#getURI()}) so it reflects
 * the client-originated protocol and address.
 *
 * 

An instance of this class is typically declared as a bean with the name * "forwardedHeaderTransformer" and detected by * {@link WebHttpHandlerBuilder#applicationContext(ApplicationContext)}, or it * can also be registered directly via * {@link WebHttpHandlerBuilder#forwardedHeaderTransformer(ForwardedHeaderTransformer)}. * *

There are security considerations for forwarded headers since an application * cannot know if the headers were added by a proxy, as intended, or by a malicious * client. This is why a proxy at the boundary of trust should be configured to * remove untrusted Forwarded headers that come from the outside. * *

You can also configure the ForwardedHeaderFilter with {@link #setRemoveOnly removeOnly}, * in which case it removes but does not use the headers. * * @author Rossen Stoyanchev * @since 5.1 * @see https://tools.ietf.org/html/rfc7239 */ public class ForwardedHeaderTransformer implements Function { static final Set FORWARDED_HEADER_NAMES = Collections.newSetFromMap(new LinkedCaseInsensitiveMap<>(10, Locale.ENGLISH)); static { FORWARDED_HEADER_NAMES.add("Forwarded"); FORWARDED_HEADER_NAMES.add("X-Forwarded-Host"); FORWARDED_HEADER_NAMES.add("X-Forwarded-Port"); FORWARDED_HEADER_NAMES.add("X-Forwarded-Proto"); FORWARDED_HEADER_NAMES.add("X-Forwarded-Prefix"); FORWARDED_HEADER_NAMES.add("X-Forwarded-Ssl"); FORWARDED_HEADER_NAMES.add("X-Forwarded-For"); } private boolean removeOnly; /** * Enable mode in which any "Forwarded" or "X-Forwarded-*" headers are * removed only and the information in them ignored. * @param removeOnly whether to discard and ignore forwarded headers */ public void setRemoveOnly(boolean removeOnly) { this.removeOnly = removeOnly; } /** * Whether the "remove only" mode is on. * @see #setRemoveOnly */ public boolean isRemoveOnly() { return this.removeOnly; } /** * Apply and remove, or remove Forwarded type headers. * @param request the request */ @Override public ServerHttpRequest apply(ServerHttpRequest request) { if (hasForwardedHeaders(request)) { ServerHttpRequest.Builder builder = request.mutate(); if (!this.removeOnly) { URI uri = UriComponentsBuilder.fromHttpRequest(request).build(true).toUri(); builder.uri(uri); String prefix = getForwardedPrefix(request); if (prefix != null) { builder.path(prefix + uri.getRawPath()); builder.contextPath(prefix); } InetSocketAddress remoteAddress = request.getRemoteAddress(); remoteAddress = UriComponentsBuilder.parseForwardedFor(request, remoteAddress); if (remoteAddress != null) { builder.remoteAddress(remoteAddress); } } removeForwardedHeaders(builder); request = builder.build(); } return request; } /** * Whether the request has any Forwarded headers. * @param request the request */ protected boolean hasForwardedHeaders(ServerHttpRequest request) { HttpHeaders headers = request.getHeaders(); for (String headerName : FORWARDED_HEADER_NAMES) { if (headers.containsKey(headerName)) { return true; } } return false; } private void removeForwardedHeaders(ServerHttpRequest.Builder builder) { builder.headers(map -> FORWARDED_HEADER_NAMES.forEach(map::remove)); } @Nullable private static String getForwardedPrefix(ServerHttpRequest request) { HttpHeaders headers = request.getHeaders(); String header = headers.getFirst("X-Forwarded-Prefix"); if (header == null) { return null; } StringBuilder prefix = new StringBuilder(header.length()); String[] rawPrefixes = StringUtils.tokenizeToStringArray(header, ","); for (String rawPrefix : rawPrefixes) { int endIndex = rawPrefix.length(); while (endIndex > 1 && rawPrefix.charAt(endIndex - 1) == '/') { endIndex--; } prefix.append((endIndex != rawPrefix.length() ? rawPrefix.substring(0, endIndex) : rawPrefix)); } return prefix.toString(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy