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:
parent
8d447633f4
commit
cd08102b93
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue