spring security RequestWrapper 源码

  • 2022-08-13
  • 浏览 (770)

spring security RequestWrapper 代码

文件路径:/web/src/main/java/org/springframework/security/web/firewall/RequestWrapper.java

/*
 * Copyright 2002-2016 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.security.web.firewall;

import java.io.IOException;
import java.util.StringTokenizer;

import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;

/**
 * Request wrapper which ensures values of {@code servletPath} and {@code pathInfo} are
 * returned which are suitable for pattern matching against. It strips out path parameters
 * and extra consecutive '/' characters.
 *
 * <h3>Path Parameters</h3> Parameters (as defined in
 * <a href="https://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a>) are stripped from the path
 * segments of the {@code servletPath} and {@code pathInfo} values of the request.
 * <p>
 * The parameter sequence is demarcated by a semi-colon, so each segment is checked for
 * the occurrence of a ";" character and truncated at that point if it is present.
 * <p>
 * The behaviour differs between servlet containers in how they interpret the servlet
 * spec, which does not clearly state what the behaviour should be. For consistency, we
 * make sure they are always removed, to avoid the risk of URL matching rules being
 * bypassed by the malicious addition of parameters to the path component.
 *
 * @author Luke Taylor
 */
final class RequestWrapper extends FirewalledRequest {

	private final String strippedServletPath;

	private final String strippedPathInfo;

	private boolean stripPaths = true;

	RequestWrapper(HttpServletRequest request) {
		super(request);
		this.strippedServletPath = strip(request.getServletPath());
		String pathInfo = strip(request.getPathInfo());
		if (pathInfo != null && pathInfo.length() == 0) {
			pathInfo = null;
		}
		this.strippedPathInfo = pathInfo;
	}

	/**
	 * Removes path parameters from each path segment in the supplied path and truncates
	 * sequences of multiple '/' characters to a single '/'.
	 * @param path either the {@code servletPath} and {@code pathInfo} from the original
	 * request
	 * @return the supplied value, with path parameters removed and sequences of multiple
	 * '/' characters truncated, or null if the supplied path was null.
	 */
	private String strip(String path) {
		if (path == null) {
			return null;
		}
		int semicolonIndex = path.indexOf(';');
		if (semicolonIndex < 0) {
			int doubleSlashIndex = path.indexOf("//");
			if (doubleSlashIndex < 0) {
				// Most likely case, no parameters in any segment and no '//', so no
				// stripping required
				return path;
			}
		}
		StringTokenizer tokenizer = new StringTokenizer(path, "/");
		StringBuilder stripped = new StringBuilder(path.length());
		if (path.charAt(0) == '/') {
			stripped.append('/');
		}
		while (tokenizer.hasMoreTokens()) {
			String segment = tokenizer.nextToken();
			semicolonIndex = segment.indexOf(';');
			if (semicolonIndex >= 0) {
				segment = segment.substring(0, semicolonIndex);
			}
			stripped.append(segment).append('/');
		}
		// Remove the trailing slash if the original path didn't have one
		if (path.charAt(path.length() - 1) != '/') {
			stripped.deleteCharAt(stripped.length() - 1);
		}
		return stripped.toString();
	}

	@Override
	public String getPathInfo() {
		return this.stripPaths ? this.strippedPathInfo : super.getPathInfo();
	}

	@Override
	public String getServletPath() {
		return this.stripPaths ? this.strippedServletPath : super.getServletPath();
	}

	@Override
	public RequestDispatcher getRequestDispatcher(String path) {
		return this.stripPaths ? new FirewalledRequestAwareRequestDispatcher(path) : super.getRequestDispatcher(path);
	}

	@Override
	public void reset() {
		this.stripPaths = false;
	}

	/**
	 * Ensures {@link FirewalledRequest#reset()} is called prior to performing a forward.
	 * It then delegates work to the {@link RequestDispatcher} from the original
	 * {@link HttpServletRequest}.
	 *
	 * @author Rob Winch
	 */
	private class FirewalledRequestAwareRequestDispatcher implements RequestDispatcher {

		private final String path;

		/**
		 * @param path the {@code path} that will be used to obtain the delegate
		 * {@link RequestDispatcher} from the original {@link HttpServletRequest}.
		 */
		FirewalledRequestAwareRequestDispatcher(String path) {
			this.path = path;
		}

		@Override
		public void forward(ServletRequest request, ServletResponse response) throws ServletException, IOException {
			reset();
			getDelegateDispatcher().forward(request, response);
		}

		@Override
		public void include(ServletRequest request, ServletResponse response) throws ServletException, IOException {
			getDelegateDispatcher().include(request, response);
		}

		private RequestDispatcher getDelegateDispatcher() {
			return RequestWrapper.super.getRequestDispatcher(this.path);
		}

	}

}

相关信息

spring security 源码目录

相关文章

spring security CompositeRequestRejectedHandler 源码

spring security DefaultHttpFirewall 源码

spring security DefaultRequestRejectedHandler 源码

spring security FirewalledRequest 源码

spring security FirewalledResponse 源码

spring security HttpFirewall 源码

spring security HttpStatusRequestRejectedHandler 源码

spring security RequestRejectedException 源码

spring security RequestRejectedHandler 源码

spring security StrictHttpFirewall 源码

0  赞