SEC-1469: Initial support for debugging filter.
This commit is contained in:
parent
591bd532bd
commit
1f520b691f
|
@ -6,6 +6,7 @@ package org.springframework.security.config;
|
|||
* These are intended for internal use.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
public abstract class BeanIds {
|
||||
private static final String PREFIX = "org.springframework.security.";
|
||||
|
@ -28,4 +29,6 @@ public abstract class BeanIds {
|
|||
public static final String METHOD_SECURITY_METADATA_SOURCE_ADVISOR = PREFIX + "methodSecurityMetadataSourceAdvisor";
|
||||
public static final String EMBEDDED_APACHE_DS = PREFIX + "apacheDirectoryServerContainer";
|
||||
public static final String CONTEXT_SOURCE = PREFIX + "securityContextSource";
|
||||
|
||||
public static final String DEBUG_FILTER = PREFIX + "debugFilter";
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
package org.springframework.security.config;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.security.config.debug.SecurityDebugBeanFactoryPostProcessor;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
public class DebugBeanDefinitionParser implements BeanDefinitionParser {
|
||||
public BeanDefinition parse(Element element, ParserContext parserContext) {
|
||||
RootBeanDefinition debugPP = new RootBeanDefinition(SecurityDebugBeanFactoryPostProcessor.class);
|
||||
parserContext.getReaderContext().registerWithGeneratedName(debugPP);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -52,4 +52,5 @@ public abstract class Elements {
|
|||
@Deprecated
|
||||
public static final String FILTER_INVOCATION_DEFINITION_SOURCE = "filter-invocation-definition-source";
|
||||
public static final String LDAP_PASSWORD_COMPARE = "password-compare";
|
||||
public static final String DEBUG = "debug";
|
||||
}
|
||||
|
|
|
@ -116,6 +116,7 @@ public final class SecurityNamespaceHandler implements NamespaceHandler {
|
|||
parsers.put(Elements.GLOBAL_METHOD_SECURITY, new GlobalMethodSecurityBeanDefinitionParser());
|
||||
parsers.put(Elements.AUTHENTICATION_MANAGER, new AuthenticationManagerBeanDefinitionParser());
|
||||
parsers.put(Elements.METHOD_SECURITY_METADATA_SOURCE, new MethodSecurityMetadataSourceBeanDefinitionParser());
|
||||
parsers.put(Elements.DEBUG, new DebugBeanDefinitionParser());
|
||||
|
||||
// Only load the web-namespace parsers if the web classes are available
|
||||
if (ClassUtils.isPresent("org.springframework.security.web.FilterChainProxy", getClass().getClassLoader())) {
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
package org.springframework.security.config.debug;
|
||||
|
||||
import org.springframework.security.web.FilterChainProxy;
|
||||
import org.springframework.security.web.util.RequestMatcher;
|
||||
import org.springframework.security.web.util.UrlUtils;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletRequestWrapper;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Spring Security debugging filter.
|
||||
* <p>
|
||||
* Logs information (such as session creation) to help the user understand how requests are being handled
|
||||
* by Spring Security and provide them with other relevant information (such as when sessions are being created).
|
||||
*
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @since 3.1
|
||||
*/
|
||||
class DebugFilter extends OncePerRequestFilter {
|
||||
private final FilterChainProxy fcp;
|
||||
private final Map<RequestMatcher, List<Filter>> filterChainMap;
|
||||
|
||||
public DebugFilter(FilterChainProxy fcp) {
|
||||
this.fcp = fcp;
|
||||
this.filterChainMap = fcp.getFilterChainMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
|
||||
List<Filter> filters = getFilters(request);
|
||||
Logger.log("Request received for '" + UrlUtils.buildRequestUrl(request) + "':\n\n" +
|
||||
request + "\n\n" +
|
||||
formatFilters(filters));
|
||||
|
||||
fcp.doFilter(new DebugRequestWrapper(request), response, filterChain);
|
||||
}
|
||||
|
||||
String formatFilters(List<Filter> filters) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Security filter chain: ");
|
||||
if (filters == null) {
|
||||
sb.append("no match");
|
||||
} else if (filters.isEmpty()) {
|
||||
sb.append("[] empty (bypassed by security='none') ");
|
||||
} else {
|
||||
sb.append("[\n");
|
||||
for (Filter f : filters) {
|
||||
sb.append(" ").append(f.getClass().getSimpleName()).append("\n");
|
||||
}
|
||||
sb.append("]");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private List<Filter> getFilters(HttpServletRequest request) {
|
||||
for (Map.Entry<RequestMatcher, List<Filter>> entry : filterChainMap.entrySet()) {
|
||||
RequestMatcher matcher = entry.getKey();
|
||||
|
||||
if (matcher.matches(request)) {
|
||||
return entry.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class DebugRequestWrapper extends HttpServletRequestWrapper {
|
||||
|
||||
public DebugRequestWrapper(HttpServletRequest request) {
|
||||
super(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpSession getSession() {
|
||||
boolean sessionExists = super.getSession(false) != null;
|
||||
HttpSession session = super.getSession();
|
||||
|
||||
if (!sessionExists) {
|
||||
Logger.log("New HTTP session created: " + session.getId(), true);
|
||||
}
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpSession getSession(boolean create) {
|
||||
if (!create) {
|
||||
return super.getSession(create);
|
||||
}
|
||||
return getSession();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
package org.springframework.security.config.debug;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
|
||||
/**
|
||||
* Controls output for the Spring Security debug feature.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @since 3.1
|
||||
*/
|
||||
class Logger {
|
||||
final static Log logger = LogFactory.getLog("Spring Security Debugger");
|
||||
|
||||
public static void log(String message) {
|
||||
log(message, false);
|
||||
}
|
||||
|
||||
public static void log(String message, boolean dumpStack) {
|
||||
StringBuilder output = new StringBuilder(256);
|
||||
output.append("\n\n************************************************************\n\n");
|
||||
output.append(message).append("\n");
|
||||
|
||||
if (dumpStack) {
|
||||
StringWriter os = new StringWriter();
|
||||
new Exception().printStackTrace(new PrintWriter(os));
|
||||
StringBuffer buffer = os.getBuffer();
|
||||
// Remove the exception in case it scares people.
|
||||
int start = buffer.indexOf("java.lang.Exception");
|
||||
buffer.replace(start, start + 19, "");
|
||||
output.append("\nCall stack: \n").append(os.toString());
|
||||
}
|
||||
|
||||
output.append("\n\n************************************************************\n\n");
|
||||
|
||||
logger.info(output.toString());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package org.springframework.security.config.debug;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.security.config.BeanIds;
|
||||
import org.springframework.security.web.FilterChainProxy;
|
||||
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
public class SecurityDebugBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
|
||||
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
|
||||
Logger.logger.warn("\n\n" +
|
||||
"********************************************************************\n" +
|
||||
"********** Security debugging is enabled. *************\n" +
|
||||
"********** This may include sensitive information. *************\n" +
|
||||
"********** Do not use in a production system! *************\n" +
|
||||
"********************************************************************\n\n");
|
||||
if (beanFactory.getBean(BeanIds.SPRING_SECURITY_FILTER_CHAIN) != null) {
|
||||
FilterChainProxy fcp = beanFactory.getBean(BeanIds.FILTER_CHAIN_PROXY, FilterChainProxy.class);
|
||||
beanFactory.registerSingleton(BeanIds.DEBUG_FILTER, new DebugFilter(fcp));
|
||||
// Overwrite the filter chain alias
|
||||
beanFactory.registerAlias(BeanIds.DEBUG_FILTER, BeanIds.SPRING_SECURITY_FILTER_CHAIN);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -41,6 +41,12 @@ data-source-ref =
|
|||
## A reference to a DataSource bean
|
||||
attribute data-source-ref {xsd:token}
|
||||
|
||||
|
||||
|
||||
debug =
|
||||
## Enables Spring Security debugging infrastructure. This will provide human-readable (multi-line) debugging information to monitor requests coming into the security filters. This may include sensitive information, such as request parameters or headers, and should only be used in a development environment.
|
||||
element debug {empty}
|
||||
|
||||
password-encoder =
|
||||
## element which defines a password encoding strategy. Used by an authentication provider to convert submitted passwords to hashed versions, for example.
|
||||
element password-encoder {password-encoder.attlist, salt-source?}
|
||||
|
@ -673,5 +679,4 @@ position =
|
|||
## The explicit position at which the custom-filter should be placed in the chain. Use if you are replacing a standard filter.
|
||||
attribute position {named-security-filter}
|
||||
|
||||
|
||||
named-security-filter = "FIRST" | "CHANNEL_FILTER" | "CONCURRENT_SESSION_FILTER" | "SECURITY_CONTEXT_FILTER" | "LOGOUT_FILTER" | "X509_FILTER" | "PRE_AUTH_FILTER" | "CAS_FILTER" | "FORM_LOGIN_FILTER" | "OPENID_FILTER" |"BASIC_AUTH_FILTER" | "SERVLET_API_SUPPORT_FILTER" | "REMEMBER_ME_FILTER" | "ANONYMOUS_FILTER" | "EXCEPTION_TRANSLATION_FILTER" | "SESSION_MANAGEMENT_FILTER" | "FILTER_SECURITY_INTERCEPTOR" | "SWITCH_USER_FILTER" | "LAST"
|
||||
|
|
|
@ -104,6 +104,9 @@
|
|||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:attributeGroup>
|
||||
<xs:element name="debug"><xs:annotation>
|
||||
<xs:documentation>Enables Spring Security debugging infrastructure. This will provide human-readable (multi-line) debugging information to monitor requests coming into the security filters. This may include sensitive information, such as request parameters or headers, and should only be used in a development environment.</xs:documentation>
|
||||
</xs:annotation><xs:complexType/></xs:element>
|
||||
|
||||
<xs:attributeGroup name="password-encoder.attlist">
|
||||
<xs:attribute name="ref" type="xs:token">
|
||||
|
|
Loading…
Reference in New Issue