瀏覽代碼

Add debug logging

Goal is to provide insight to devs on:
- Authentication & Authorization success/failures
- WebSession & SecurityContext
- Request matchers, cache & authn/authz flow

Fixes gh-5758
Mathieu Ouellet 5 年之前
父節點
當前提交
cd08102b93
共有 14 個文件被更改,包括 231 次插入50 次删除
  1. 10 2
      web/src/main/java/org/springframework/security/web/server/DefaultServerRedirectStrategy.java
  2. 21 2
      web/src/main/java/org/springframework/security/web/server/DelegatingServerAuthenticationEntryPoint.java
  3. 17 9
      web/src/main/java/org/springframework/security/web/server/authentication/AnonymousAuthenticationWebFilter.java
  4. 10 1
      web/src/main/java/org/springframework/security/web/server/authentication/AuthenticationWebFilter.java
  5. 10 1
      web/src/main/java/org/springframework/security/web/server/authentication/logout/LogoutWebFilter.java
  6. 20 6
      web/src/main/java/org/springframework/security/web/server/authorization/AuthorizationWebFilter.java
  7. 17 7
      web/src/main/java/org/springframework/security/web/server/authorization/DelegatingReactiveAuthorizationManager.java
  8. 22 5
      web/src/main/java/org/springframework/security/web/server/context/WebSessionServerSecurityContextRepository.java
  9. 12 1
      web/src/main/java/org/springframework/security/web/server/savedrequest/CookieServerRequestCache.java
  10. 22 5
      web/src/main/java/org/springframework/security/web/server/savedrequest/WebSessionServerRequestCache.java
  11. 16 2
      web/src/main/java/org/springframework/security/web/server/util/matcher/AndServerWebExchangeMatcher.java
  12. 11 2
      web/src/main/java/org/springframework/security/web/server/util/matcher/NegatedServerWebExchangeMatcher.java
  13. 17 3
      web/src/main/java/org/springframework/security/web/server/util/matcher/OrServerWebExchangeMatcher.java
  14. 26 4
      web/src/main/java/org/springframework/security/web/server/util/matcher/PathPatternParserServerWebExchangeMatcher.java

+ 10 - 2
web/src/main/java/org/springframework/security/web/server/DefaultServerRedirectStrategy.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2018 the original author or authors.
+ * 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.
@@ -16,6 +16,8 @@
 
 package org.springframework.security.web.server;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.server.reactive.ServerHttpResponse;
 import org.springframework.util.Assert;
@@ -28,9 +30,11 @@ import java.net.URI;
  * The default {@link ServerRedirectStrategy} to use.
  *
  * @author Rob Winch
+ * @author Mathieu Ouellet
  * @since 5.0
  */
 public class DefaultServerRedirectStrategy implements ServerRedirectStrategy {
+	private static final Log logger = LogFactory.getLog(DefaultServerRedirectStrategy.class);
 	private HttpStatus httpStatus = HttpStatus.FOUND;
 
 	private boolean contextRelative = true;
@@ -41,7 +45,11 @@ public class DefaultServerRedirectStrategy implements ServerRedirectStrategy {
 		return Mono.fromRunnable(() -> {
 			ServerHttpResponse response = exchange.getResponse();
 			response.setStatusCode(this.httpStatus);
-			response.getHeaders().setLocation(createLocation(exchange, location));
+			URI newLocation = createLocation(exchange, location);
+			if (logger.isDebugEnabled()) {
+				logger.debug("Redirecting to '" + newLocation + "'");
+			}
+			response.getHeaders().setLocation(newLocation);
 		});
 	}
 

+ 21 - 2
web/src/main/java/org/springframework/security/web/server/DelegatingServerAuthenticationEntryPoint.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2017 the original author or authors.
+ * 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.
@@ -16,6 +16,8 @@
 
 package org.springframework.security.web.server;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import org.springframework.http.HttpStatus;
 import org.springframework.util.Assert;
 import reactor.core.publisher.Flux;
@@ -33,10 +35,13 @@ import java.util.List;
  * on a {@link ServerWebExchangeMatcher}
  *
  * @author Rob Winch
+ * @author Mathieu Ouellet
  * @since 5.0
  */
 public class DelegatingServerAuthenticationEntryPoint
 	implements ServerAuthenticationEntryPoint {
+	private static final Log logger = LogFactory.getLog(DelegatingServerAuthenticationEntryPoint.class);
+
 	private final List<DelegateEntry> entryPoints;
 
 	private ServerAuthenticationEntryPoint defaultEntryPoint = (exchange, e) -> {
@@ -61,12 +66,26 @@ public class DelegatingServerAuthenticationEntryPoint
 				.filterWhen( entry -> isMatch(exchange, entry))
 				.next()
 				.map( entry -> entry.getEntryPoint())
-				.defaultIfEmpty(this.defaultEntryPoint)
+				.doOnNext(it -> {
+					if (logger.isDebugEnabled()) {
+						logger.debug("Match found! Executing " + it);
+					}
+				})
+				.switchIfEmpty(Mono.just(this.defaultEntryPoint)
+					.doOnNext(it -> {
+						if (logger.isDebugEnabled()) {
+							logger.debug("No match found. Using default entry point " + defaultEntryPoint);
+						}
+					})
+				)
 				.flatMap( entryPoint -> entryPoint.commence(exchange, e));
 	}
 
 	private Mono<Boolean> isMatch(ServerWebExchange exchange, DelegateEntry entry) {
 		ServerWebExchangeMatcher matcher = entry.getMatcher();
+		if (logger.isDebugEnabled()) {
+			logger.debug("Trying to match using " + matcher);
+		}
 		return matcher.matches(exchange)
 			.map( result -> result.isMatch());
 	}

+ 17 - 9
web/src/main/java/org/springframework/security/web/server/authentication/AnonymousAuthenticationWebFilter.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2018 the original author or authors.
+ * 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.
@@ -18,6 +18,8 @@ package org.springframework.security.web.server.authentication;
 
 import java.util.List;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import reactor.core.publisher.Mono;
 
 import org.springframework.security.authentication.AnonymousAuthenticationToken;
@@ -37,12 +39,11 @@ import org.springframework.web.server.WebFilterChain;
  * {@code ReactiveSecurityContextHolder}, and populates it with one if needed.
  *
  * @author Ankur Pathak
+ * @author Mathieu Ouellet
  * @since 5.2.0
  */
 public class AnonymousAuthenticationWebFilter implements WebFilter {
-	// ~ Instance fields
-	// ================================================================================================
-
+	private static final Log logger = LogFactory.getLog(AnonymousAuthenticationWebFilter.class);
 	private String key;
 	private Object principal;
 	private List<GrantedAuthority> authorities;
@@ -72,18 +73,25 @@ public class AnonymousAuthenticationWebFilter implements WebFilter {
 		this.authorities = authorities;
 	}
 
-
 	@Override
 	public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
 		return ReactiveSecurityContextHolder.getContext()
 				.switchIfEmpty(Mono.defer(() -> {
-						SecurityContext securityContext = new SecurityContextImpl();
-						securityContext.setAuthentication(createAuthentication(exchange));
+						Authentication authentication = createAuthentication(exchange);
+						SecurityContext securityContext = new SecurityContextImpl(authentication);
+						if (logger.isDebugEnabled()) {
+							logger.debug("Populated SecurityContext with anonymous token: '" + authentication + "'");
+						}
 						return chain.filter(exchange)
 								.subscriberContext(ReactiveSecurityContextHolder.withSecurityContext(Mono.just(securityContext)))
 								.then(Mono.empty());
-				})).flatMap(securityContext -> chain.filter(exchange));
-
+				}))
+				.flatMap(securityContext -> {
+					if (logger.isDebugEnabled()) {
+						logger.debug("SecurityContext contains anonymous token: '" + securityContext.getAuthentication() + "'");
+					}
+					return chain.filter(exchange);
+				});
 	}
 
 	protected Authentication createAuthentication(ServerWebExchange exchange) {

+ 10 - 1
web/src/main/java/org/springframework/security/web/server/authentication/AuthenticationWebFilter.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2019 the original author or authors.
+ * 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.
@@ -17,6 +17,8 @@ package org.springframework.security.web.server.authentication;
 
 import java.util.function.Function;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import reactor.core.publisher.Mono;
 
 import org.springframework.security.authentication.ReactiveAuthenticationManager;
@@ -65,9 +67,11 @@ import org.springframework.web.server.WebFilterChain;
  *
  * @author Rob Winch
  * @author Rafiullah Hamedy
+ * @author Mathieu Ouellet
  * @since 5.0
  */
 public class AuthenticationWebFilter implements WebFilter {
+	private static final Log logger = LogFactory.getLog(AuthenticationWebFilter.class);
 	private final ReactiveAuthenticationManagerResolver<ServerWebExchange> authenticationManagerResolver;
 
 	private ServerAuthenticationSuccessHandler authenticationSuccessHandler = new WebFilterChainServerAuthenticationSuccessHandler();
@@ -116,6 +120,11 @@ public class AuthenticationWebFilter implements WebFilter {
 			.flatMap(authenticationManager -> authenticationManager.authenticate(token))
 			.switchIfEmpty(Mono.defer(() -> Mono.error(new IllegalStateException("No provider found for " + token.getClass()))))
 			.flatMap(authentication -> onAuthenticationSuccess(authentication, webFilterExchange))
+			.doOnError(AuthenticationException.class, e -> {
+				if (logger.isDebugEnabled()) {
+					logger.debug("Authentication failed: " + e.getMessage());
+				}
+			})
 			.onErrorResume(AuthenticationException.class, e -> this.authenticationFailureHandler
 				.onAuthenticationFailure(webFilterExchange, e));
 	}

+ 10 - 1
web/src/main/java/org/springframework/security/web/server/authentication/logout/LogoutWebFilter.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2017 the original author or authors.
+ * 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.
@@ -16,6 +16,8 @@
 
 package org.springframework.security.web.server.authentication.logout;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import reactor.core.publisher.Mono;
 
 import org.springframework.http.HttpMethod;
@@ -36,9 +38,12 @@ import org.springframework.web.server.WebFilterChain;
  * {@link ServerLogoutHandler}.
  *
  * @author Rob Winch
+ * @author Mathieu Ouellet
  * @since 5.0
  */
 public class LogoutWebFilter implements WebFilter {
+	private static final Log logger = LogFactory.getLog(LogoutWebFilter.class);
+
 	private AnonymousAuthenticationToken anonymousAuthenticationToken = new AnonymousAuthenticationToken("key", "anonymous",
 		AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"));
 
@@ -69,6 +74,10 @@ public class LogoutWebFilter implements WebFilter {
 	}
 
 	private Mono<Void> logout(WebFilterExchange webFilterExchange, Authentication authentication) {
+		if (logger.isDebugEnabled()) {
+			logger.debug("Logging out user '" + authentication
+					+ "' and transferring to logout destination");
+		}
 		return this.logoutHandler.logout(webFilterExchange, authentication)
 			.then(this.logoutSuccessHandler
 				.onLogoutSuccess(webFilterExchange, authentication))

+ 20 - 6
web/src/main/java/org/springframework/security/web/server/authorization/AuthorizationWebFilter.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2017 the original author or authors.
+ * 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.
@@ -15,7 +15,9 @@
  */
 package org.springframework.security.web.server.authorization;
 
-
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.security.access.AccessDeniedException;
 import org.springframework.security.authorization.ReactiveAuthorizationManager;
 import org.springframework.security.core.context.ReactiveSecurityContextHolder;
 import org.springframework.security.core.context.SecurityContext;
@@ -28,13 +30,15 @@ import reactor.core.publisher.Mono;
 /**
  *
  * @author Rob Winch
+ * @author Mathieu Ouellet
  * @since 5.0
  */
 public class AuthorizationWebFilter implements WebFilter {
-	private ReactiveAuthorizationManager<? super ServerWebExchange> accessDecisionManager;
+	private static final Log logger = LogFactory.getLog(AuthorizationWebFilter.class);
+	private ReactiveAuthorizationManager<? super ServerWebExchange> authorizationManager;
 
-	public AuthorizationWebFilter(ReactiveAuthorizationManager<? super ServerWebExchange> accessDecisionManager) {
-		this.accessDecisionManager = accessDecisionManager;
+	public AuthorizationWebFilter(ReactiveAuthorizationManager<? super ServerWebExchange> authorizationManager) {
+		this.authorizationManager = authorizationManager;
 	}
 
 	@Override
@@ -42,7 +46,17 @@ public class AuthorizationWebFilter implements WebFilter {
 		return ReactiveSecurityContextHolder.getContext()
 			.filter(c -> c.getAuthentication() != null)
 			.map(SecurityContext::getAuthentication)
-			.as(authentication -> this.accessDecisionManager.verify(authentication, exchange))
+			.as(authentication -> this.authorizationManager.verify(authentication, exchange))
+			.doOnSuccess(it -> {
+				if (logger.isDebugEnabled()) {
+					logger.debug("Authorization successful");
+				}
+			})
+			.doOnError(AccessDeniedException.class, e -> {
+				if (logger.isDebugEnabled()) {
+					logger.debug("Authorization failed: " + e.getMessage());
+				}
+			})
 			.switchIfEmpty(chain.filter(exchange));
 	}
 }

+ 17 - 7
web/src/main/java/org/springframework/security/web/server/authorization/DelegatingReactiveAuthorizationManager.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2017 the original author or authors.
+ * 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.
@@ -15,11 +15,13 @@
  */
 package org.springframework.security.web.server.authorization;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import org.springframework.security.authorization.AuthorizationDecision;
 import org.springframework.security.authorization.ReactiveAuthorizationManager;
 import org.springframework.security.core.Authentication;
+import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher.MatchResult;
 import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcherEntry;
-import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;
 import org.springframework.web.server.ServerWebExchange;
 
 import reactor.core.publisher.Flux;
@@ -30,9 +32,11 @@ import java.util.List;
 
 /**
  * @author Rob Winch
+ * @author Mathieu Ouellet
  * @since 5.0
  */
 public class DelegatingReactiveAuthorizationManager implements ReactiveAuthorizationManager<ServerWebExchange> {
+	private static final Log logger = LogFactory.getLog(DelegatingReactiveAuthorizationManager.class);
 	private final List<ServerWebExchangeMatcherEntry<ReactiveAuthorizationManager<AuthorizationContext>>> mappings;
 
 	private DelegatingReactiveAuthorizationManager(List<ServerWebExchangeMatcherEntry<ReactiveAuthorizationManager<AuthorizationContext>>> mappings) {
@@ -43,11 +47,17 @@ public class DelegatingReactiveAuthorizationManager implements ReactiveAuthoriza
 	public Mono<AuthorizationDecision> check(Mono<Authentication> authentication, ServerWebExchange exchange) {
 		return Flux.fromIterable(mappings)
 			.concatMap(mapping -> mapping.getMatcher().matches(exchange)
-				.filter(ServerWebExchangeMatcher.MatchResult::isMatch)
-				.map(r -> r.getVariables())
-				.flatMap(variables -> mapping.getEntry()
-					.check(authentication, new AuthorizationContext(exchange, variables))
-				)
+				.filter(MatchResult::isMatch)
+				.map(MatchResult::getVariables)
+				.flatMap(variables -> {
+					if (logger.isDebugEnabled()) {
+						logger.debug("Checking authorization on '"
+							+ exchange.getRequest().getPath().pathWithinApplication()
+							+ "' using " + mapping.getEntry());
+					}
+					return mapping.getEntry()
+						.check(authentication, new AuthorizationContext(exchange, variables));
+				})
 			)
 			.next()
 			.defaultIfEmpty(new AuthorizationDecision(false));

+ 22 - 5
web/src/main/java/org/springframework/security/web/server/context/WebSessionServerSecurityContextRepository.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2018 the original author or authors.
+ * 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.
@@ -15,11 +15,12 @@
  */
 package org.springframework.security.web.server.context;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import org.springframework.security.core.context.SecurityContext;
 import org.springframework.util.Assert;
 import org.springframework.web.server.ServerWebExchange;
 
-import org.springframework.web.server.WebSession;
 import reactor.core.publisher.Mono;
 
 /**
@@ -27,11 +28,14 @@ import reactor.core.publisher.Mono;
  * {@link org.springframework.web.server.WebSession}. When a {@link SecurityContext} is
  * saved, the session id is changed to prevent session fixation attacks.
  * @author Rob Winch
+ * @author Mathieu Ouellet
  * @since 5.0
  */
 public class WebSessionServerSecurityContextRepository
 	implements ServerSecurityContextRepository {
 
+	private static final Log logger = LogFactory.getLog(WebSessionServerSecurityContextRepository.class);
+
 	/**
 	 * The default session attribute name to save and load the {@link SecurityContext}
 	 */
@@ -54,8 +58,14 @@ public class WebSessionServerSecurityContextRepository
 			.doOnNext(session -> {
 				if (context == null) {
 					session.getAttributes().remove(this.springSecurityContextAttrName);
+					if (logger.isDebugEnabled()) {
+						logger.debug("Removed SecurityContext stored in WebSession: '" + session + "'");
+					}
 				} else {
 					session.getAttributes().put(this.springSecurityContextAttrName, context);
+					if (logger.isDebugEnabled()) {
+						logger.debug("Saved SecurityContext '" + context + "' in WebSession: '" + session + "'");
+					}
 				}
 			})
 			.flatMap(session -> session.changeSessionId());
@@ -63,9 +73,16 @@ public class WebSessionServerSecurityContextRepository
 
 	public Mono<SecurityContext> load(ServerWebExchange exchange) {
 		return exchange.getSession()
-			.map(WebSession::getAttributes)
-			.flatMap( attrs -> {
-				SecurityContext context = (SecurityContext) attrs.get(this.springSecurityContextAttrName);
+			.flatMap(session -> {
+				SecurityContext context = (SecurityContext) session.getAttribute(this.springSecurityContextAttrName);
+				if (logger.isDebugEnabled()) {
+					if (context == null) {
+						logger.debug("No SecurityContext found in WebSession: '" + session + "'");
+					}
+					else {
+						logger.debug("Found SecurityContext '" + context + "' in WebSession: '" + session + "'");
+					}
+				}
 				return Mono.justOrEmpty(context);
 			});
 	}

+ 12 - 1
web/src/main/java/org/springframework/security/web/server/savedrequest/CookieServerRequestCache.java

@@ -16,6 +16,8 @@
 
 package org.springframework.security.web.server.savedrequest;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import org.springframework.http.HttpCookie;
 import org.springframework.http.HttpMethod;
 import org.springframework.http.MediaType;
@@ -42,6 +44,7 @@ import java.util.Collections;
  * requested URI in a cookie.
  *
  * @author Eleftheria Stein
+ * @author Mathieu Ouellet
  * @since 5.4
  */
 public class CookieServerRequestCache implements ServerRequestCache {
@@ -49,6 +52,8 @@ public class CookieServerRequestCache implements ServerRequestCache {
 
 	private static final Duration COOKIE_MAX_AGE = Duration.ofSeconds(-1);
 
+	private static final Log logger = LogFactory.getLog(CookieServerRequestCache.class);
+
 	private ServerWebExchangeMatcher saveRequestMatcher = createDefaultRequestMatcher();
 
 	/**
@@ -69,7 +74,13 @@ public class CookieServerRequestCache implements ServerRequestCache {
 				.filter(m -> m.isMatch())
 				.map(m -> exchange.getResponse())
 				.map(ServerHttpResponse::getCookies)
-				.doOnNext(cookies -> cookies.add(REDIRECT_URI_COOKIE_NAME, createRedirectUriCookie(exchange.getRequest())))
+				.doOnNext(cookies -> {
+					ResponseCookie redirectUriCookie = createRedirectUriCookie(exchange.getRequest());
+					cookies.add(REDIRECT_URI_COOKIE_NAME, redirectUriCookie);
+					if (logger.isDebugEnabled()) {
+						logger.debug("Request added to Cookie: " + redirectUriCookie);
+					}
+				})
 				.then();
 	}
 

+ 22 - 5
web/src/main/java/org/springframework/security/web/server/savedrequest/WebSessionServerRequestCache.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2017 the original author or authors.
+ * 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.
@@ -25,6 +25,7 @@ import org.springframework.security.web.server.util.matcher.AndServerWebExchange
 import org.springframework.security.web.server.util.matcher.MediaTypeServerWebExchangeMatcher;
 import org.springframework.security.web.server.util.matcher.NegatedServerWebExchangeMatcher;
 import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;
+import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher.MatchResult;
 import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers;
 import org.springframework.util.Assert;
 import org.springframework.web.server.ServerWebExchange;
@@ -41,12 +42,13 @@ import java.util.Collections;
  * The current implementation only saves the URL that was requested.
  *
  * @author Rob Winch
+ * @author Mathieu Ouellet
  * @since 5.0
  */
 public class WebSessionServerRequestCache implements ServerRequestCache {
 	private static final String DEFAULT_SAVED_REQUEST_ATTR = "SPRING_SECURITY_SAVED_REQUEST";
 
-	protected final Log logger = LogFactory.getLog(this.getClass());
+	private static final Log logger = LogFactory.getLog(WebSessionServerRequestCache.class);
 
 	private String sessionAttrName = DEFAULT_SAVED_REQUEST_ATTR;
 
@@ -66,10 +68,16 @@ public class WebSessionServerRequestCache implements ServerRequestCache {
 	@Override
 	public Mono<Void> saveRequest(ServerWebExchange exchange) {
 		return this.saveRequestMatcher.matches(exchange)
-			.filter(m -> m.isMatch())
+			.filter(MatchResult::isMatch)
 			.flatMap(m -> exchange.getSession())
 			.map(WebSession::getAttributes)
-			.doOnNext(attrs -> attrs.put(this.sessionAttrName, pathInApplication(exchange.getRequest())))
+			.doOnNext(attrs -> {
+				String requestPath = pathInApplication(exchange.getRequest());
+				attrs.put(this.sessionAttrName, requestPath);
+				if (logger.isDebugEnabled()) {
+					logger.debug("Request added to WebSession: '" + requestPath + "'");
+				}
+			})
 			.then();
 	}
 
@@ -85,7 +93,16 @@ public class WebSessionServerRequestCache implements ServerRequestCache {
 		ServerWebExchange exchange) {
 		return exchange.getSession()
 			.map(WebSession::getAttributes)
-			.filter(attributes -> attributes.remove(this.sessionAttrName, pathInApplication(exchange.getRequest())))
+			.filter(attributes -> {
+				String requestPath = pathInApplication(exchange.getRequest());
+				boolean removed = attributes.remove(this.sessionAttrName, requestPath);
+				if (removed) {
+					if (logger.isDebugEnabled()) {
+						logger.debug("Request removed from WebSession: '" + requestPath + "'");
+					}
+				}
+				return removed;
+			})
 			.map(attributes -> exchange.getRequest());
 	}
 

+ 16 - 2
web/src/main/java/org/springframework/security/web/server/util/matcher/AndServerWebExchangeMatcher.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2017 the original author or authors.
+ * 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.
@@ -15,6 +15,8 @@
  */
 package org.springframework.security.web.server.util.matcher;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import org.springframework.util.Assert;
 import org.springframework.web.server.ServerWebExchange;
 import reactor.core.publisher.Flux;
@@ -28,10 +30,12 @@ import java.util.Map;
 /**
  * Matches if all the provided {@link ServerWebExchangeMatcher} match
  * @author Rob Winch
+ * @author Mathieu Ouellet
  * @since 5.0
  * @see OrServerWebExchangeMatcher
  */
 public class AndServerWebExchangeMatcher implements ServerWebExchangeMatcher {
+	private static final Log logger = LogFactory.getLog(AndServerWebExchangeMatcher.class);
 	private final List<ServerWebExchangeMatcher> matchers;
 
 	public AndServerWebExchangeMatcher(List<ServerWebExchangeMatcher> matchers) {
@@ -51,10 +55,20 @@ public class AndServerWebExchangeMatcher implements ServerWebExchangeMatcher {
 		return Mono.defer(() -> {
 			Map<String, Object> variables = new HashMap<>();
 			return Flux.fromIterable(matchers)
+				.doOnNext(it -> {
+					if (logger.isDebugEnabled()) {
+						logger.debug("Trying to match using " + it);
+					}
+				})
 				.flatMap(matcher -> matcher.matches(exchange))
 				.doOnNext(matchResult -> variables.putAll(matchResult.getVariables()))
 				.all(MatchResult::isMatch)
-				.flatMap(allMatch -> allMatch ? MatchResult.match(variables) : MatchResult.notMatch());
+				.flatMap(allMatch -> allMatch ? MatchResult.match(variables) : MatchResult.notMatch())
+				.doOnNext(it -> {
+					if (logger.isDebugEnabled()) {
+						logger.debug(it.isMatch() ? "All requestMatchers returned true" : "Did not match");
+					}
+				});
 		});
 	}
 

+ 11 - 2
web/src/main/java/org/springframework/security/web/server/util/matcher/NegatedServerWebExchangeMatcher.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2018 the original author or authors.
+ * 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.
@@ -15,6 +15,8 @@
  */
 package org.springframework.security.web.server.util.matcher;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import org.springframework.util.Assert;
 import org.springframework.web.server.ServerWebExchange;
 import reactor.core.publisher.Mono;
@@ -23,9 +25,11 @@ import reactor.core.publisher.Mono;
  * Negates the provided matcher. If the provided matcher returns true, then the result will be false. If the provided
  * matcher returns false, then the result will be true.
  * @author Tao Qian
+ * @author Mathieu Ouellet
  * @since 5.1
  */
 public class NegatedServerWebExchangeMatcher implements ServerWebExchangeMatcher {
+	private static final Log logger = LogFactory.getLog(NegatedServerWebExchangeMatcher.class);
 	private final ServerWebExchangeMatcher matcher;
 
 	public NegatedServerWebExchangeMatcher(ServerWebExchangeMatcher matcher) {
@@ -39,7 +43,12 @@ public class NegatedServerWebExchangeMatcher implements ServerWebExchangeMatcher
 	@Override
 	public Mono<MatchResult> matches(ServerWebExchange exchange) {
 		return matcher.matches(exchange)
-			.flatMap(m -> m.isMatch() ? MatchResult.notMatch() : MatchResult.match());
+			.flatMap(m -> m.isMatch() ? MatchResult.notMatch() : MatchResult.match())
+			.doOnNext(it -> {
+				if (logger.isDebugEnabled()) {
+					logger.debug("matches = " + it.isMatch());
+				}
+			});
 	}
 
 	@Override

+ 17 - 3
web/src/main/java/org/springframework/security/web/server/util/matcher/OrServerWebExchangeMatcher.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2017 the original author or authors.
+ * 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.
@@ -18,6 +18,8 @@ package org.springframework.security.web.server.util.matcher;
 import java.util.Arrays;
 import java.util.List;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import org.springframework.util.Assert;
 import org.springframework.web.server.ServerWebExchange;
 import reactor.core.publisher.Flux;
@@ -26,10 +28,12 @@ import reactor.core.publisher.Mono;
 /**
  * Matches if any of the provided {@link ServerWebExchangeMatcher} match
  * @author Rob Winch
+ * @author Mathieu Ouellet
  * @since 5.0
  * @see AndServerWebExchangeMatcher
  */
 public class OrServerWebExchangeMatcher implements ServerWebExchangeMatcher {
+	private static final Log logger = LogFactory.getLog(OrServerWebExchangeMatcher.class);
 	private final List<ServerWebExchangeMatcher> matchers;
 
 	public OrServerWebExchangeMatcher(List<ServerWebExchangeMatcher> matchers) {
@@ -48,10 +52,20 @@ public class OrServerWebExchangeMatcher implements ServerWebExchangeMatcher {
 	@Override
 	public Mono<MatchResult> matches(ServerWebExchange exchange) {
 		return Flux.fromIterable(matchers)
+			.doOnNext(it -> {
+				if (logger.isDebugEnabled()) {
+					logger.debug("Trying to match using " + it);
+				}
+			})
 			.flatMap(m -> m.matches(exchange))
-			.filter(m -> m.isMatch())
+			.filter(MatchResult::isMatch)
 			.next()
-			.switchIfEmpty(MatchResult.notMatch());
+			.switchIfEmpty(MatchResult.notMatch())
+			.doOnNext(it -> {
+				if (logger.isDebugEnabled()) {
+					logger.debug(it.isMatch() ? "matched" : "No matches found");
+				}
+			});
 	}
 
 	@Override

+ 26 - 4
web/src/main/java/org/springframework/security/web/server/util/matcher/PathPatternParserServerWebExchangeMatcher.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2018 the original author or authors.
+ * 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.
@@ -15,6 +15,8 @@
  */
 package org.springframework.security.web.server.util.matcher;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import org.springframework.http.HttpMethod;
 import org.springframework.http.server.PathContainer;
 import org.springframework.http.server.reactive.ServerHttpRequest;
@@ -30,9 +32,11 @@ import java.util.Map;
 /**
  * Matches if the {@link PathPattern} matches the path within the application.
  * @author Rob Winch
+ * @author Mathieu Ouellet
  * @since 5.0
  */
 public final class PathPatternParserServerWebExchangeMatcher implements ServerWebExchangeMatcher {
+	private static final Log logger = LogFactory.getLog(PathPatternParserServerWebExchangeMatcher.class);
 	private static final PathPatternParser DEFAULT_PATTERN_PARSER = new PathPatternParser();
 
 	private final PathPattern pattern;
@@ -61,16 +65,34 @@ public final class PathPatternParserServerWebExchangeMatcher implements ServerWe
 	@Override
 	public Mono<MatchResult> matches(ServerWebExchange exchange) {
 		ServerHttpRequest request = exchange.getRequest();
+		PathContainer path = request.getPath().pathWithinApplication();
 		if (this.method != null && !this.method.equals(request.getMethod())) {
-			return MatchResult.notMatch();
+			return MatchResult.notMatch()
+				.doOnNext(result -> {
+					if (logger.isDebugEnabled()) {
+						logger.debug("Request '" + request.getMethod() + " " + path
+								+ "' doesn't match '" + this.method + " "
+								+ this.pattern.getPatternString() + "'");
+					}
+			});
 		}
-		PathContainer path = request.getPath().pathWithinApplication();
 		boolean match = this.pattern.matches(path);
 		if (!match) {
-			return MatchResult.notMatch();
+			return MatchResult.notMatch()
+				.doOnNext(result -> {
+					if (logger.isDebugEnabled()) {
+						logger.debug("Request '" + request.getMethod() + " " + path
+								+ "' doesn't match '" + this.method + " "
+								+ this.pattern.getPatternString() + "'");
+					}
+				});
 		}
 		Map<String, String> pathVariables = this.pattern.matchAndExtract(path).getUriVariables();
 		Map<String, Object> variables = new HashMap<>(pathVariables);
+		if (logger.isDebugEnabled()) {
+			logger.debug("Checking match of request : '" + path + "'; against '"
+					+ this.pattern.getPatternString() + "'");
+		}
 		return MatchResult.match(variables);
 	}