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
This commit is contained in:
Mathieu Ouellet 2020-05-01 08:55:22 -04:00 committed by Rob Winch
parent 8d447633f4
commit cd08102b93
14 changed files with 232 additions and 51 deletions

View File

@ -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);
});
}

View File

@ -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());
}

View File

@ -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) {

View File

@ -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));
}

View File

@ -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))

View File

@ -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));
}
}

View File

@ -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));

View File

@ -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);
});
}

View File

@ -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();
}

View File

@ -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());
}

View File

@ -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");
}
});
});
}

View File

@ -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

View File

@ -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

View File

@ -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();
if (this.method != null && !this.method.equals(request.getMethod())) {
return MatchResult.notMatch();
}
PathContainer path = request.getPath().pathWithinApplication();
if (this.method != null && !this.method.equals(request.getMethod())) {
return MatchResult.notMatch()
.doOnNext(result -> {
if (logger.isDebugEnabled()) {
logger.debug("Request '" + request.getMethod() + " " + path
+ "' doesn't match '" + this.method + " "
+ this.pattern.getPatternString() + "'");
}
});
}
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);
}