Polish Javadoc

Fixes: gh-5186
This commit is contained in:
Rob Winch 2018-03-27 21:05:29 -05:00
parent c67ce144b9
commit 151b545ed0
49 changed files with 931 additions and 27 deletions

View File

@ -55,7 +55,7 @@ import java.lang.annotation.Target;
* @EnableWebFluxSecurity * @EnableWebFluxSecurity
* public class MyExplicitSecurityConfiguration { * public class MyExplicitSecurityConfiguration {
* @Bean * @Bean
* SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) { * public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
* http * http
* .authorizeExchange() * .authorizeExchange()
* .anyExchange().authenticated() * .anyExchange().authenticated()

View File

@ -36,6 +36,9 @@ import java.lang.reflect.Method;
import java.util.Collection; import java.util.Collection;
/** /**
* A {@link MethodInterceptor} that supports {@link PreAuthorize} and {@link PostAuthorize} for methods that return
* {@link Mono} or {@link Flux}
*
* @author Rob Winch * @author Rob Winch
* @since 5.0 * @since 5.0
*/ */
@ -49,6 +52,12 @@ public class PrePostAdviceReactiveMethodInterceptor implements MethodInterceptor
private final PostInvocationAuthorizationAdvice postAdvice; private final PostInvocationAuthorizationAdvice postAdvice;
/**
* Creates a new instance
* @param attributeSource the {@link MethodSecurityMetadataSource} to use
* @param preInvocationAdvice the {@link PreInvocationAuthorizationAdvice} to use
* @param postInvocationAdvice the {@link PostInvocationAuthorizationAdvice} to use
*/
public PrePostAdviceReactiveMethodInterceptor(MethodSecurityMetadataSource attributeSource, PreInvocationAuthorizationAdvice preInvocationAdvice, PostInvocationAuthorizationAdvice postInvocationAdvice) { public PrePostAdviceReactiveMethodInterceptor(MethodSecurityMetadataSource attributeSource, PreInvocationAuthorizationAdvice preInvocationAdvice, PostInvocationAuthorizationAdvice postInvocationAdvice) {
Assert.notNull(attributeSource, "attributeSource cannot be null"); Assert.notNull(attributeSource, "attributeSource cannot be null");
Assert.notNull(preInvocationAdvice, "preInvocationAdvice cannot be null"); Assert.notNull(preInvocationAdvice, "preInvocationAdvice cannot be null");

View File

@ -25,6 +25,7 @@ import reactor.core.publisher.Mono;
* @author Rob Winch * @author Rob Winch
* @since 5.0 * @since 5.0
*/ */
@FunctionalInterface
public interface ReactiveAuthenticationManager { public interface ReactiveAuthenticationManager {
/** /**

View File

@ -26,6 +26,9 @@ import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers; import reactor.core.scheduler.Schedulers;
/** /**
* A {@link ReactiveAuthenticationManager} that uses a {@link ReactiveUserDetailsService} to validate the provided
* username and password.
*
* @author Rob Winch * @author Rob Winch
* @since 5.0 * @since 5.0
*/ */
@ -49,6 +52,11 @@ public class UserDetailsRepositoryReactiveAuthenticationManager implements React
.map( u -> new UsernamePasswordAuthenticationToken(u, u.getPassword(), u.getAuthorities()) ); .map( u -> new UsernamePasswordAuthenticationToken(u, u.getPassword(), u.getAuthorities()) );
} }
/**
* The {@link PasswordEncoder} that is used for validating the password. The default is
* {@link PasswordEncoderFactories#createDelegatingPasswordEncoder()}
* @param passwordEncoder the {@link PasswordEncoder} to use. Cannot be null
*/
public void setPasswordEncoder(PasswordEncoder passwordEncoder) { public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
Assert.notNull(passwordEncoder, "passwordEncoder cannot be null"); Assert.notNull(passwordEncoder, "passwordEncoder cannot be null");
this.passwordEncoder = passwordEncoder; this.passwordEncoder = passwordEncoder;

View File

@ -26,6 +26,7 @@ import org.springframework.util.Assert;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
/** /**
* A {@link Map} based implementation of {@link ReactiveUserDetailsService}
* *
* @author Rob Winch * @author Rob Winch
* @since 5.0 * @since 5.0
@ -33,14 +34,26 @@ import reactor.core.publisher.Mono;
public class MapReactiveUserDetailsService implements ReactiveUserDetailsService { public class MapReactiveUserDetailsService implements ReactiveUserDetailsService {
private final Map<String, UserDetails> users; private final Map<String, UserDetails> users;
/**
* Creates a new instance using a {@link Map} that must be non blocking.
* @param users a {@link Map} of users to use.
*/
public MapReactiveUserDetailsService(Map<String, UserDetails> users) { public MapReactiveUserDetailsService(Map<String, UserDetails> users) {
this.users = users; this.users = users;
} }
/**
* Creates a new instance
* @param users the {@link UserDetails} to use
*/
public MapReactiveUserDetailsService(UserDetails... users) { public MapReactiveUserDetailsService(UserDetails... users) {
this(Arrays.asList(users)); this(Arrays.asList(users));
} }
/**
* Creates a new instance
* @param users the {@link UserDetails} to use
*/
public MapReactiveUserDetailsService(Collection<UserDetails> users) { public MapReactiveUserDetailsService(Collection<UserDetails> users) {
Assert.notEmpty(users, "users cannot be null or empty"); Assert.notEmpty(users, "users cannot be null or empty");
this.users = users.stream().collect(Collectors.toConcurrentMap( u -> getKey(u.getUsername()), Function.identity())); this.users = users.stream().collect(Collectors.toConcurrentMap( u -> getKey(u.getUsername()), Function.identity()));

View File

@ -16,11 +16,20 @@
package org.springframework.security.core.userdetails; package org.springframework.security.core.userdetails;
import org.springframework.security.core.userdetails.UserDetails;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
/**
* An API for finding the {@link UserDetails} by username.
*
* @author Rob Winch
* @since 5.0
*/
public interface ReactiveUserDetailsService { public interface ReactiveUserDetailsService {
/**
* Find the {@link UserDetails} by username.
* @param username the username to look up
* @return the {@link UserDetails}. Cannot be null
*/
Mono<UserDetails> findByUsername(String username); Mono<UserDetails> findByUsername(String username);
} }

View File

@ -25,6 +25,8 @@ import reactor.core.publisher.Mono;
import java.net.URI; import java.net.URI;
/** /**
* The default {@link ServerRedirectStrategy} to use.
*
* @author Rob Winch * @author Rob Winch
* @since 5.0 * @since 5.0
*/ */
@ -55,6 +57,10 @@ public class DefaultServerRedirectStrategy implements ServerRedirectStrategy {
return location; return location;
} }
/**
* The {@link HttpStatus} to use for the redirect.
* @param httpStatus the status to use. Cannot be null
*/
public void setHttpStatus(HttpStatus httpStatus) { public void setHttpStatus(HttpStatus httpStatus) {
Assert.notNull(httpStatus, "httpStatus cannot be null"); Assert.notNull(httpStatus, "httpStatus cannot be null");
this.httpStatus = httpStatus; this.httpStatus = httpStatus;

View File

@ -29,6 +29,9 @@ import java.util.Arrays;
import java.util.List; import java.util.List;
/** /**
* A {@link ServerAuthenticationEntryPoint} which delegates to multiple {@link ServerAuthenticationEntryPoint} based
* on a {@link ServerWebExchangeMatcher}
*
* @author Rob Winch * @author Rob Winch
* @since 5.0 * @since 5.0
*/ */

View File

@ -26,6 +26,9 @@ import reactor.core.publisher.Mono;
import java.util.List; import java.util.List;
/** /**
* A {@link SecurityWebFilterChain} that leverages a {@link ServerWebExchangeMatcher} to determine which
* {@link WebFilter} to execute.
*
* @author Rob Winch * @author Rob Winch
* @since 5.0 * @since 5.0
*/ */

View File

@ -21,13 +21,25 @@ import org.springframework.web.server.WebFilter;
import reactor.core.publisher.Flux; import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
/** /**
* Defines a filter chain which is capable of being matched against a {@link ServerWebExchange} in order to decide
* whether it applies to that request.
*
* @author Rob Winch * @author Rob Winch
* @since 5.0 * @since 5.0
*/ */
public interface SecurityWebFilterChain { public interface SecurityWebFilterChain {
/**
* Determines if this {@link SecurityWebFilterChain} matches the provided {@link ServerWebExchange}
* @param exchange the {@link ServerWebExchange}
* @return true if it matches, else false
*/
Mono<Boolean> matches(ServerWebExchange exchange); Mono<Boolean> matches(ServerWebExchange exchange);
/**
* The {@link WebFilter} to use
* @return
*/
Flux<WebFilter> getWebFilters(); Flux<WebFilter> getWebFilters();
} }

View File

@ -21,11 +21,20 @@ import org.springframework.security.core.AuthenticationException;
import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.ServerWebExchange;
/** /**
* Used to request authentication
* *
* @author Rob Winch * @author Rob Winch
* @since 5.0 * @since 5.0
*/ */
@FunctionalInterface
public interface ServerAuthenticationEntryPoint { public interface ServerAuthenticationEntryPoint {
/**
* Initiates the authentication flow
*
* @param exchange
* @param e
* @return {@code Mono<Void>} to indicate when the request for authentication is complete
*/
Mono<Void> commence(ServerWebExchange exchange, AuthenticationException e); Mono<Void> commence(ServerWebExchange exchange, AuthenticationException e);
} }

View File

@ -27,6 +27,7 @@ import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
/** /**
* Converts from a {@link ServerWebExchange} to an {@link Authentication} that can be authenticated.
* *
* @author Rob Winch * @author Rob Winch
* @since 5.0 * @since 5.0

View File

@ -23,10 +23,19 @@ import reactor.core.publisher.Mono;
import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.ServerWebExchange;
/** /**
* A strategy for performing redirects.
*
* @author Rob Winch * @author Rob Winch
* @since 5.0 * @since 5.0
*/ */
@FunctionalInterface
public interface ServerRedirectStrategy { public interface ServerRedirectStrategy {
/**
* Performs a redirect based upon the provided {@link ServerWebExchange} and {@link URI}
* @param exchange the {@link ServerWebExchange} to use
* @param location the location to redirect to
* @return {@code Mono<Void>} to indicate when redirect is complete
*/
Mono<Void> sendRedirect(ServerWebExchange exchange, URI location); Mono<Void> sendRedirect(ServerWebExchange exchange, URI location);
} }

View File

@ -28,6 +28,8 @@ import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
/** /**
* Used to delegate to a List of {@link SecurityWebFilterChain} instances.
*
* @author Rob Winch * @author Rob Winch
* @since 5.0 * @since 5.0
*/ */

View File

@ -21,6 +21,8 @@ import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilterChain; import org.springframework.web.server.WebFilterChain;
/** /**
* A composite of the {@link ServerWebExchange} and the {@link WebFilterChain}. This is typically used as a value object
* for handling success and failures.
* @author Rob Winch * @author Rob Winch
* @since 5.0 * @since 5.0
*/ */

View File

@ -36,6 +36,27 @@ import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain; import org.springframework.web.server.WebFilterChain;
/** /**
* A {@link WebFilter} that performs authentication of a particular request. An outline of the logic:
*
* <ul>
* <li>
* A request comes in and if it does not match {@link #setRequiresAuthenticationMatcher(ServerWebExchangeMatcher)},
* then this filter does nothing and the {@link WebFilterChain} is continued. If it does match then...
* </li>
* <li>
* An attempt to convert the {@link ServerWebExchange} into an {@link Authentication} is made. If the result is
* empty, then the filter does nothing more and the {@link WebFilterChain} is continued. If it does create an
* {@link Authentication}...
* </li>
* <li>
* The {@link ReactiveAuthenticationManager} specified in
* {@link #AuthenticationWebFilter(ReactiveAuthenticationManager)} is used to perform authentication.
* </li>
* <li>
* If authentication is successful, {@link ServerAuthenticationSuccessHandler} is invoked and the authentication
* is set on {@link ReactiveSecurityContextHolder}, else {@link ServerAuthenticationFailureHandler} is invoked
* </li>
* </ul>
* *
* @author Rob Winch * @author Rob Winch
* @since 5.0 * @since 5.0
@ -54,6 +75,10 @@ public class AuthenticationWebFilter implements WebFilter {
private ServerWebExchangeMatcher requiresAuthenticationMatcher = ServerWebExchangeMatchers.anyExchange(); private ServerWebExchangeMatcher requiresAuthenticationMatcher = ServerWebExchangeMatchers.anyExchange();
/**
* Creates an instance
* @param authenticationManager the authentication manager to use
*/
public AuthenticationWebFilter(ReactiveAuthenticationManager authenticationManager) { public AuthenticationWebFilter(ReactiveAuthenticationManager authenticationManager) {
Assert.notNull(authenticationManager, "authenticationManager cannot be null"); Assert.notNull(authenticationManager, "authenticationManager cannot be null");
this.authenticationManager = authenticationManager; this.authenticationManager = authenticationManager;
@ -87,26 +112,53 @@ public class AuthenticationWebFilter implements WebFilter {
.subscriberContext(ReactiveSecurityContextHolder.withSecurityContext(Mono.just(securityContext))); .subscriberContext(ReactiveSecurityContextHolder.withSecurityContext(Mono.just(securityContext)));
} }
/**
* Sets the repository for persisting the SecurityContext. Default is {@link NoOpServerSecurityContextRepository}
* @param securityContextRepository the repository to use
*/
public void setSecurityContextRepository( public void setSecurityContextRepository(
ServerSecurityContextRepository securityContextRepository) { ServerSecurityContextRepository securityContextRepository) {
Assert.notNull(securityContextRepository, "securityContextRepository cannot be null"); Assert.notNull(securityContextRepository, "securityContextRepository cannot be null");
this.securityContextRepository = securityContextRepository; this.securityContextRepository = securityContextRepository;
} }
/**
* Sets the authentication success handler. Default is {@link WebFilterChainServerAuthenticationSuccessHandler}
* @param authenticationSuccessHandler the success handler to use
*/
public void setAuthenticationSuccessHandler(ServerAuthenticationSuccessHandler authenticationSuccessHandler) { public void setAuthenticationSuccessHandler(ServerAuthenticationSuccessHandler authenticationSuccessHandler) {
Assert.notNull(authenticationSuccessHandler, "authenticationSuccessHandler cannot be null");
this.authenticationSuccessHandler = authenticationSuccessHandler; this.authenticationSuccessHandler = authenticationSuccessHandler;
} }
/**
* Sets the strategy used for converting from a {@link ServerWebExchange} to an {@link Authentication} used for
* authenticating with the provided {@link ReactiveAuthenticationManager}. If the result is empty, then it signals
* that no authentication attempt should be made. The default converter is
* {@link ServerHttpBasicAuthenticationConverter}
* @param authenticationConverter the converter to use
*/
public void setAuthenticationConverter(Function<ServerWebExchange, Mono<Authentication>> authenticationConverter) { public void setAuthenticationConverter(Function<ServerWebExchange, Mono<Authentication>> authenticationConverter) {
Assert.notNull(authenticationConverter, "authenticationConverter cannot be null");
this.authenticationConverter = authenticationConverter; this.authenticationConverter = authenticationConverter;
} }
/**
* Sets the failure handler used when authentication fails. The default is to prompt for basic authentication.
* @param authenticationFailureHandler the handler to use. Cannot be null.
*/
public void setAuthenticationFailureHandler( public void setAuthenticationFailureHandler(
ServerAuthenticationFailureHandler authenticationFailureHandler) { ServerAuthenticationFailureHandler authenticationFailureHandler) {
Assert.notNull(authenticationFailureHandler, "authenticationFailureHandler cannot be null"); Assert.notNull(authenticationFailureHandler, "authenticationFailureHandler cannot be null");
this.authenticationFailureHandler = authenticationFailureHandler; this.authenticationFailureHandler = authenticationFailureHandler;
} }
/**
* Sets the matcher used to determine when creating an {@link Authentication} from
* {@link #setAuthenticationConverter(Function)} to be authentication. If the converter returns an empty
* result, then no authentication is attempted. The default is any request
* @param requiresAuthenticationMatcher the matcher to use. Cannot be null.
*/
public void setRequiresAuthenticationMatcher( public void setRequiresAuthenticationMatcher(
ServerWebExchangeMatcher requiresAuthenticationMatcher) { ServerWebExchangeMatcher requiresAuthenticationMatcher) {
Assert.notNull(requiresAuthenticationMatcher, "requiresAuthenticationMatcher cannot be null"); Assert.notNull(requiresAuthenticationMatcher, "requiresAuthenticationMatcher cannot be null");

View File

@ -25,6 +25,7 @@ import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
/** /**
* Prompts a user for HTTP Basic authentication.
* *
* @author Rob Winch * @author Rob Winch
* @since 5.0 * @since 5.0

View File

@ -43,11 +43,19 @@ public class RedirectServerAuthenticationEntryPoint
private ServerRequestCache requestCache = new WebSessionServerRequestCache(); private ServerRequestCache requestCache = new WebSessionServerRequestCache();
/**
* Creates an instance
* @param location the location to redirect to (i.e. "/logout-success")
*/
public RedirectServerAuthenticationEntryPoint(String location) { public RedirectServerAuthenticationEntryPoint(String location) {
Assert.notNull(location, "location cannot be null"); Assert.notNull(location, "location cannot be null");
this.location = URI.create(location); this.location = URI.create(location);
} }
/**
* The request cache to use to save the request before sending a redirect.
* @param requestCache the cache to redirect to.
*/
public void setRequestCache(ServerRequestCache requestCache) { public void setRequestCache(ServerRequestCache requestCache) {
Assert.notNull(requestCache, "requestCache cannot be null"); Assert.notNull(requestCache, "requestCache cannot be null");
this.requestCache = requestCache; this.requestCache = requestCache;

View File

@ -37,6 +37,10 @@ public class RedirectServerAuthenticationFailureHandler
private ServerRedirectStrategy redirectStrategy = new DefaultServerRedirectStrategy(); private ServerRedirectStrategy redirectStrategy = new DefaultServerRedirectStrategy();
/**
* Creates an instance
* @param location the location to redirect to (i.e. "/login?failed")
*/
public RedirectServerAuthenticationFailureHandler(String location) { public RedirectServerAuthenticationFailureHandler(String location) {
Assert.notNull(location, "location cannot be null"); Assert.notNull(location, "location cannot be null");
this.location = URI.create(location); this.location = URI.create(location);

View File

@ -29,6 +29,8 @@ import reactor.core.publisher.Mono;
import java.net.URI; import java.net.URI;
/** /**
* Performs a redirect on authentication success. The default is to redirect to a saved request if present and
* otherwise "/".
* @author Rob Winch * @author Rob Winch
* @since 5.0 * @since 5.0
*/ */
@ -40,12 +42,24 @@ public class RedirectServerAuthenticationSuccessHandler
private ServerRequestCache requestCache = new WebSessionServerRequestCache(); private ServerRequestCache requestCache = new WebSessionServerRequestCache();
/**
* Creates a new instance with location of "/"
*/
public RedirectServerAuthenticationSuccessHandler() {} public RedirectServerAuthenticationSuccessHandler() {}
/**
* Creates a new instance with the specified location
* @param location the location to redirect if the no request is cached in
* {@link #setRequestCache(ServerRequestCache)}
*/
public RedirectServerAuthenticationSuccessHandler(String location) { public RedirectServerAuthenticationSuccessHandler(String location) {
this.location = URI.create(location); this.location = URI.create(location);
} }
/**
* Sets the {@link ServerRequestCache} used to redirect to. Default is {@link WebSessionServerRequestCache}.
* @param requestCache the cache to use
*/
public void setRequestCache(ServerRequestCache requestCache) { public void setRequestCache(ServerRequestCache requestCache) {
Assert.notNull(requestCache, "requestCache cannot be null"); Assert.notNull(requestCache, "requestCache cannot be null");
this.requestCache = requestCache; this.requestCache = requestCache;

View File

@ -23,6 +23,7 @@ import org.springframework.util.Assert;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
/** /**
* Adapts a {@link ServerAuthenticationEntryPoint} into a {@link ServerAuthenticationFailureHandler}
* @author Rob Winch * @author Rob Winch
* @since 5.0 * @since 5.0
*/ */

View File

@ -22,9 +22,17 @@ import reactor.core.publisher.Mono;
import org.springframework.security.web.server.WebFilterExchange; import org.springframework.security.web.server.WebFilterExchange;
/** /**
* Handles authentication failure
*
* @author Rob Winch * @author Rob Winch
* @since 5.0 * @since 5.0
*/ */
public interface ServerAuthenticationFailureHandler { public interface ServerAuthenticationFailureHandler {
/**
* Invoked when authentication attempt fails
* @param webFilterExchange the exchange
* @param exception the reason authentication failed
* @return a completion notification (success or error)
*/
Mono<Void> onAuthenticationFailure(WebFilterExchange webFilterExchange, AuthenticationException exception); Mono<Void> onAuthenticationFailure(WebFilterExchange webFilterExchange, AuthenticationException exception);
} }

View File

@ -21,10 +21,17 @@ import org.springframework.security.web.server.WebFilterExchange;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
/** /**
* Handles authentication success
* @author Rob Winch * @author Rob Winch
* @since 5.0 * @since 5.0
*/ */
public interface ServerAuthenticationSuccessHandler { public interface ServerAuthenticationSuccessHandler {
/**
* Invoked when the application authenticates successfully
* @param webFilterExchange the exchange
* @param authentication the {@link Authentication}
* @return a completion notification (success or error)
*/
Mono<Void> onAuthenticationSuccess(WebFilterExchange webFilterExchange, Mono<Void> onAuthenticationSuccess(WebFilterExchange webFilterExchange,
Authentication authentication); Authentication authentication);
} }

View File

@ -22,6 +22,8 @@ import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
/** /**
* Success handler that continues the filter chain after authentication success.
*
* @author Rob Winch * @author Rob Winch
* @since 5.0 * @since 5.0
*/ */

View File

@ -26,6 +26,7 @@ import reactor.core.publisher.Mono;
import java.net.URI; import java.net.URI;
/** /**
* Performs a redirect on log out success.
* @author Rob Winch * @author Rob Winch
* @since 5.0 * @since 5.0
*/ */

View File

@ -21,9 +21,17 @@ import org.springframework.security.web.server.WebFilterExchange;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
/** /**
* Handles log out
* @author Rob Winch * @author Rob Winch
* @since 5.0 * @since 5.0
* @see ServerLogoutSuccessHandler
*/ */
public interface ServerLogoutHandler { public interface ServerLogoutHandler {
/**
* Invoked when log out is requested
* @param exchange the exchange
* @param authentication the {@link Authentication}
* @return a completion notification (success or error)
*/
Mono<Void> logout(WebFilterExchange exchange, Authentication authentication); Mono<Void> logout(WebFilterExchange exchange, Authentication authentication);
} }

View File

@ -21,10 +21,18 @@ import org.springframework.security.web.server.WebFilterExchange;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
/** /**
* Strategy for when log out was successfully performed (typically after {@link ServerLogoutHandler} is invoked).
* @author Rob Winch * @author Rob Winch
* @since 5.0 * @since 5.0
* @see ServerLogoutHandler
*/ */
public interface ServerLogoutSuccessHandler { public interface ServerLogoutSuccessHandler {
/**
* Invoked after log out was successful
* @param exchange the exchange
* @param authentication the {@link Authentication}
* @return a completion notification (success or error)
*/
Mono<Void> onLogoutSuccess(WebFilterExchange exchange, Authentication authentication); Mono<Void> onLogoutSuccess(WebFilterExchange exchange, Authentication authentication);
} }

View File

@ -22,6 +22,7 @@ import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
/** /**
* A do nothing implementation of {@link ServerSecurityContextRepository}. Used in stateless applications.
* @author Rob Winch * @author Rob Winch
* @since 5.0 * @since 5.0
*/ */

View File

@ -25,6 +25,9 @@ import reactor.core.publisher.Mono;
import reactor.util.context.Context; import reactor.util.context.Context;
/** /**
* Uses a {@link ServerSecurityContextRepository} to provide the {@link SecurityContext} to initialize the
* {@link ReactiveSecurityContextHolder}.
*
* @author Rob Winch * @author Rob Winch
* @since 5.0 * @since 5.0
*/ */

View File

@ -24,8 +24,10 @@ import org.springframework.web.server.ServerWebExchangeDecorator;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
/** /**
* Overrides the {@link ServerWebExchange#getPrincipal()} with the provided SecurityContext
* @author Rob Winch * @author Rob Winch
* @since 5.0 * @since 5.0
* @see SecurityContextServerWebExchangeWebFilter
*/ */
public class SecurityContextServerWebExchange extends ServerWebExchangeDecorator { public class SecurityContextServerWebExchange extends ServerWebExchangeDecorator {
private final Mono<SecurityContext> context; private final Mono<SecurityContext> context;

View File

@ -21,13 +21,9 @@ import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter; import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain; import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import reactor.util.context.Context;
import java.security.Principal;
/** /**
* Populate the {@link Principal} from {@link ServerWebExchange#getPrincipal()} into the * Override the {@link ServerWebExchange#getPrincipal()} to be looked up using {@link ReactiveSecurityContextHolder}.
* Reactor {@link Context}.
* *
* @author Rob Winch * @author Rob Winch
* @since 5.0 * @since 5.0

View File

@ -20,9 +20,26 @@ import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
/**
* Strategy used for persisting a {@link SecurityContext} between requests.
* @author Rob Winch
* @since 5.0
* @see ReactorContextWebFilter
*/
public interface ServerSecurityContextRepository { public interface ServerSecurityContextRepository {
/**
* Saves the SecurityContext
* @param exchange the exchange to associate to the SecurityContext
* @param context the SecurityContext to save
* @return a completion notification (success or error)
*/
Mono<Void> save(ServerWebExchange exchange, SecurityContext context); Mono<Void> save(ServerWebExchange exchange, SecurityContext context);
/**
* Loads the SecurityContext associated with the {@link ServerWebExchange}
* @param exchange the exchange to look up the {@link SecurityContext}
* @return the {@link SecurityContext} to lookup or empty if not found. Never null
*/
Mono<SecurityContext> load(ServerWebExchange exchange); Mono<SecurityContext> load(ServerWebExchange exchange);
} }

View File

@ -21,6 +21,13 @@ import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
/** /**
* Writes cache control related headers.
*
* <pre>
* Cache-Control: no-cache, no-store, max-age=0, must-revalidate
* Pragma: no-cache
* Expires: 0
* </pre>
* *
* @author Rob Winch * @author Rob Winch
* @since 5.0 * @since 5.0

View File

@ -25,6 +25,7 @@ import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
/** /**
* Combines multiple {@link ServerHttpHeadersWriter} instances into a single instance.
* *
* @author Rob Winch * @author Rob Winch
* @since 5.0 * @since 5.0

View File

@ -24,6 +24,8 @@ import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
/** /**
* Allows specifying {@link HttpHeaders} that should be written to the response.
*
* @author Rob Winch * @author Rob Winch
* @since 5.0 * @since 5.0
*/ */

View File

@ -22,6 +22,7 @@ import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
/** /**
* Writes the Strict-Transport-Security if the request is secure.
* @author Rob Winch * @author Rob Winch
* @since 5.0 * @since 5.0
*/ */
@ -52,11 +53,19 @@ public final class StrictTransportSecurityServerHttpHeadersWriter
return isSecure(exchange) ? delegate.writeHttpHeaders(exchange) : Mono.empty(); return isSecure(exchange) ? delegate.writeHttpHeaders(exchange) : Mono.empty();
} }
/**
* Sets if subdomains should be included. Default is true
* @param includeSubDomains if subdomains should be included
*/
public void setIncludeSubDomains(boolean includeSubDomains) { public void setIncludeSubDomains(boolean includeSubDomains) {
subdomain = includeSubDomains ? " ; includeSubDomains" : ""; subdomain = includeSubDomains ? " ; includeSubDomains" : "";
updateDelegate(); updateDelegate();
} }
/**
* Sets the max age of the header. Default is a year.
* @param maxAge the max age of the header
*/
public void setMaxAge(Duration maxAge) { public void setMaxAge(Duration maxAge) {
this.maxAge = "max-age=" + maxAge.getSeconds(); this.maxAge = "max-age=" + maxAge.getSeconds();
updateDelegate(); updateDelegate();

View File

@ -20,6 +20,7 @@ import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
/** /**
* {@code ServerHttpHeadersWriter} implementation for the X-Frame-Options headers.
* @author Rob Winch * @author Rob Winch
* @since 5.0 * @since 5.0
*/ */

View File

@ -20,6 +20,8 @@ import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
/** /**
* Add the x-xss-protection header.
*
* @author Rob Winch * @author Rob Winch
* @since 5.0 * @since 5.0
*/ */
@ -33,7 +35,7 @@ public class XXssProtectionServerHttpHeadersWriter implements ServerHttpHeadersW
private ServerHttpHeadersWriter delegate; private ServerHttpHeadersWriter delegate;
/** /**
* * Creates a new instance
*/ */
public XXssProtectionServerHttpHeadersWriter() { public XXssProtectionServerHttpHeadersWriter() {
this.enabled = true; this.enabled = true;

View File

@ -23,6 +23,7 @@ import reactor.core.publisher.Mono;
import java.net.URI; import java.net.URI;
/** /**
* An implementation of {@link ServerRequestCache} that does nothing. This is used in stateless applications
* @author Rob Winch * @author Rob Winch
* @since 5.0 * @since 5.0
*/ */

View File

@ -34,6 +34,8 @@ import reactor.core.publisher.Mono;
import java.nio.charset.Charset; import java.nio.charset.Charset;
/** /**
* Generates a default log in page used for authenticating users.
*
* @author Rob Winch * @author Rob Winch
* @since 5.0 * @since 5.0
*/ */
@ -54,7 +56,6 @@ public class LoginPageGeneratingWebFilter implements WebFilter {
result.setStatusCode(HttpStatus.OK); result.setStatusCode(HttpStatus.OK);
result.getHeaders().setContentType(MediaType.TEXT_HTML); result.getHeaders().setContentType(MediaType.TEXT_HTML);
return result.writeWith(createBuffer(exchange)); return result.writeWith(createBuffer(exchange));
// .doOnError( error -> DataBufferUtils.release(buffer));
} }
private Mono<DataBuffer> createBuffer(ServerWebExchange exchange) { private Mono<DataBuffer> createBuffer(ServerWebExchange exchange) {

View File

@ -33,6 +33,8 @@ import reactor.core.publisher.Mono;
import java.nio.charset.Charset; import java.nio.charset.Charset;
/** /**
* Generates a default log out page.
*
* @author Rob Winch * @author Rob Winch
* @since 5.0 * @since 5.0
*/ */

View File

@ -26,8 +26,10 @@ import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
* Matches if all the provided {@link ServerWebExchangeMatcher} match
* @author Rob Winch * @author Rob Winch
* @since 5.0 * @since 5.0
* @see OrServerWebExchangeMatcher
*/ */
public class AndServerWebExchangeMatcher implements ServerWebExchangeMatcher { public class AndServerWebExchangeMatcher implements ServerWebExchangeMatcher {
private final List<ServerWebExchangeMatcher> matchers; private final List<ServerWebExchangeMatcher> matchers;

View File

@ -34,6 +34,8 @@ import org.springframework.web.server.NotAcceptableStatusException;
import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.ServerWebExchange;
/** /**
* Matches based upon the accept headers.
*
* @author Rob Winch * @author Rob Winch
* @since 5.0 * @since 5.0
*/ */
@ -44,12 +46,20 @@ public class MediaTypeServerWebExchangeMatcher implements ServerWebExchangeMatch
private boolean useEquals; private boolean useEquals;
private Set<MediaType> ignoredMediaTypes = Collections.emptySet(); private Set<MediaType> ignoredMediaTypes = Collections.emptySet();
/**
* Creates a new instance
* @param matchingMediaTypes the types to match on
*/
public MediaTypeServerWebExchangeMatcher(MediaType... matchingMediaTypes) { public MediaTypeServerWebExchangeMatcher(MediaType... matchingMediaTypes) {
Assert.notEmpty(matchingMediaTypes, "matchingMediaTypes cannot be null"); Assert.notEmpty(matchingMediaTypes, "matchingMediaTypes cannot be null");
Assert.noNullElements(matchingMediaTypes, "matchingMediaTypes cannot contain null"); Assert.noNullElements(matchingMediaTypes, "matchingMediaTypes cannot contain null");
this.matchingMediaTypes = Arrays.asList(matchingMediaTypes); this.matchingMediaTypes = Arrays.asList(matchingMediaTypes);
} }
/**
* Creates a new instance
* @param matchingMediaTypes the types to match on
*/
public MediaTypeServerWebExchangeMatcher(Collection<MediaType> matchingMediaTypes) { public MediaTypeServerWebExchangeMatcher(Collection<MediaType> matchingMediaTypes) {
Assert.notEmpty(matchingMediaTypes, "matchingMediaTypes cannot be null"); Assert.notEmpty(matchingMediaTypes, "matchingMediaTypes cannot be null");
Assert.isTrue(!matchingMediaTypes.contains(null), () -> "matchingMediaTypes cannot contain null. Got " + matchingMediaTypes); Assert.isTrue(!matchingMediaTypes.contains(null), () -> "matchingMediaTypes cannot contain null. Got " + matchingMediaTypes);

View File

@ -24,8 +24,10 @@ import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
/** /**
* Matches if any of the provided {@link ServerWebExchangeMatcher} match
* @author Rob Winch * @author Rob Winch
* @since 5.0 * @since 5.0
* @see AndServerWebExchangeMatcher
*/ */
public class OrServerWebExchangeMatcher implements ServerWebExchangeMatcher { public class OrServerWebExchangeMatcher implements ServerWebExchangeMatcher {
private final List<ServerWebExchangeMatcher> matchers; private final List<ServerWebExchangeMatcher> matchers;

View File

@ -28,6 +28,7 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
/** /**
* Matches if the {@link PathPattern} matches the path within the application.
* @author Rob Winch * @author Rob Winch
* @since 5.0 * @since 5.0
*/ */

View File

@ -22,14 +22,22 @@ import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
/** /**
* * An interface for determining if a {@link ServerWebExchangeMatcher} matches.
* @author Rob Winch * @author Rob Winch
* @since 5.0 * @since 5.0
*/ */
public interface ServerWebExchangeMatcher { public interface ServerWebExchangeMatcher {
/**
* Determines if a request matches or not
* @param exchange
* @return
*/
Mono<MatchResult> matches(ServerWebExchange exchange); Mono<MatchResult> matches(ServerWebExchange exchange);
/**
* The result of matching
*/
class MatchResult { class MatchResult {
private final boolean match; private final boolean match;
private final Map<String, Object> variables; private final Map<String, Object> variables;
@ -43,18 +51,36 @@ public interface ServerWebExchangeMatcher {
return match; return match;
} }
/**
* Gets potential variables and their values
* @return
*/
public Map<String, Object> getVariables() { public Map<String, Object> getVariables() {
return variables; return variables;
} }
/**
* Creates an instance of {@link MatchResult} that is a match with no variables
* @return
*/
public static Mono<MatchResult> match() { public static Mono<MatchResult> match() {
return match(Collections.emptyMap()); return match(Collections.emptyMap());
} }
/**
*
* Creates an instance of {@link MatchResult} that is a match with the specified variables
* @param variables
* @return
*/
public static Mono<MatchResult> match(Map<String, Object> variables) { public static Mono<MatchResult> match(Map<String, Object> variables) {
return Mono.just(new MatchResult(true, variables)); return Mono.just(new MatchResult(true, variables));
} }
/**
* Creates an instance of {@link MatchResult} that is not a match.
* @return
*/
public static Mono<MatchResult> notMatch() { public static Mono<MatchResult> notMatch() {
return Mono.just(new MatchResult(false, Collections.emptyMap())); return Mono.just(new MatchResult(false, Collections.emptyMap()));
} }

View File

@ -17,6 +17,7 @@
package org.springframework.security.web.server.util.matcher; package org.springframework.security.web.server.util.matcher;
/** /**
* A rich object for associating a {@link ServerWebExchangeMatcher} to another object.
* @author Rob Winch * @author Rob Winch
* @since 5.0 * @since 5.0
*/ */

View File

@ -23,11 +23,18 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
* Provides factory methods for creating common {@link ServerWebExchangeMatcher}
* @author Rob Winch * @author Rob Winch
* @since 5.0 * @since 5.0
*/ */
public abstract class ServerWebExchangeMatchers { public abstract class ServerWebExchangeMatchers {
/**
* Creates a matcher that matches on the specific method and any of the provided patterns.
* @param method the method to match on. If null, any method will be matched
* @param patterns the patterns to match on
* @return the matcher to use
*/
public static ServerWebExchangeMatcher pathMatchers(HttpMethod method, String... patterns) { public static ServerWebExchangeMatcher pathMatchers(HttpMethod method, String... patterns) {
List<ServerWebExchangeMatcher> matchers = new ArrayList<>(patterns.length); List<ServerWebExchangeMatcher> matchers = new ArrayList<>(patterns.length);
for (String pattern : patterns) { for (String pattern : patterns) {
@ -36,15 +43,31 @@ public abstract class ServerWebExchangeMatchers {
return new OrServerWebExchangeMatcher(matchers); return new OrServerWebExchangeMatcher(matchers);
} }
/**
* Creates a matcher that matches on any of the provided patterns.
* @param patterns the patterns to match on
* @return the matcher to use
*/
public static ServerWebExchangeMatcher pathMatchers(String... patterns) { public static ServerWebExchangeMatcher pathMatchers(String... patterns) {
return pathMatchers(null, patterns); return pathMatchers(null, patterns);
} }
/**
* Creates a matcher that will match on any of the provided matchers
* @param matchers the matchers to match on
* @return the matcher to use
*/
public static ServerWebExchangeMatcher matchers(ServerWebExchangeMatcher... matchers) { public static ServerWebExchangeMatcher matchers(ServerWebExchangeMatcher... matchers) {
return new OrServerWebExchangeMatcher(matchers); return new OrServerWebExchangeMatcher(matchers);
} }
/**
* Matches any exchange
* @return the matcher to use
*/
public static ServerWebExchangeMatcher anyExchange() { public static ServerWebExchangeMatcher anyExchange() {
// we don't use a lambda to ensure a unique equals and hashcode
// which otherwise can cause problems with adding multiple entries to an ordered LinkedHashMap
return new ServerWebExchangeMatcher() { return new ServerWebExchangeMatcher() {
@Override @Override
public Mono<MatchResult> matches(ServerWebExchange exchange) { public Mono<MatchResult> matches(ServerWebExchange exchange) {