Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 30aede82da | |||
| 2686452b99 | |||
| 7e470ff5bd | |||
| df6a6e5991 | |||
| 1500a22439 | |||
| 64cb256bb9 | |||
| e6d68acfd6 | |||
| bc2a7ac394 | |||
| 55019b2f0b | |||
| 151b545ed0 | |||
| c67ce144b9 | |||
| 6729c39905 | |||
| 197439f9e1 | |||
| 70a11267fa | |||
| 4e0c165d47 | |||
| 93844b1823 | |||
| a30b3321c4 | |||
| ffb8c1978a | |||
| 4722ab21bf | |||
| 335735e227 | |||
| 501269a51f | |||
| 6b58218256 | |||
| de1137fc5e | |||
| e68657216c | |||
| bfada59a1e | |||
| bc17860cb6 | |||
| 5e351a4f75 | |||
| 0458ca88e6 | |||
| a7a71d6cef | |||
| a7f180b2c7 | |||
| 87f266133b | |||
| 8f8deac0f4 | |||
| 9830d01acc | |||
| d8fea5c6f1 | |||
| 4d410b0b61 | |||
| 24a4fbfe56 | |||
| d5d1c30d1d |
@@ -30,3 +30,11 @@ gradle.taskGraph.whenReady { graph ->
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
subprojects {
|
||||||
|
plugins.withType(JavaPlugin) {
|
||||||
|
project.sourceCompatibility='1.8'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-1
@@ -137,7 +137,9 @@ public class CasAuthenticationTokenMixinTests {
|
|||||||
assertThat(token.getUserDetails()).isNotNull().isInstanceOf(User.class);
|
assertThat(token.getUserDetails()).isNotNull().isInstanceOf(User.class);
|
||||||
assertThat(token.getAssertion()).isNotNull().isInstanceOf(AssertionImpl.class);
|
assertThat(token.getAssertion()).isNotNull().isInstanceOf(AssertionImpl.class);
|
||||||
assertThat(token.getKeyHash()).isEqualTo(KEY.hashCode());
|
assertThat(token.getKeyHash()).isEqualTo(KEY.hashCode());
|
||||||
assertThat(token.getUserDetails().getAuthorities()).hasSize(1).contains(new SimpleGrantedAuthority("ROLE_USER"));
|
assertThat(token.getUserDetails().getAuthorities())
|
||||||
|
.extracting(GrantedAuthority::getAuthority)
|
||||||
|
.containsOnly("ROLE_USER");
|
||||||
assertThat(token.getAssertion().getAuthenticationDate()).isEqualTo(START_DATE);
|
assertThat(token.getAssertion().getAuthenticationDate()).isEqualTo(START_DATE);
|
||||||
assertThat(token.getAssertion().getValidFromDate()).isEqualTo(START_DATE);
|
assertThat(token.getAssertion().getValidFromDate()).isEqualTo(START_DATE);
|
||||||
assertThat(token.getAssertion().getValidUntilDate()).isEqualTo(END_DATE);
|
assertThat(token.getAssertion().getValidUntilDate()).isEqualTo(END_DATE);
|
||||||
|
|||||||
+6
-8
@@ -16,11 +16,6 @@
|
|||||||
|
|
||||||
package org.springframework.security.cas.web;
|
package org.springframework.security.cas.web;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.*;
|
|
||||||
import static org.mockito.Mockito.*;
|
|
||||||
|
|
||||||
import javax.servlet.FilterChain;
|
|
||||||
|
|
||||||
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage;
|
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -35,9 +30,13 @@ import org.springframework.security.core.Authentication;
|
|||||||
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.AuthenticationException;
|
||||||
import org.springframework.security.core.authority.AuthorityUtils;
|
import org.springframework.security.core.authority.AuthorityUtils;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
|
||||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
||||||
|
|
||||||
|
import javax.servlet.FilterChain;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link CasAuthenticationFilter}.
|
* Tests {@link CasAuthenticationFilter}.
|
||||||
*
|
*
|
||||||
@@ -146,8 +145,7 @@ public class CasAuthenticationFilterTests {
|
|||||||
.createAuthorityList("ROLE_ANONYMOUS")));
|
.createAuthorityList("ROLE_ANONYMOUS")));
|
||||||
assertThat(filter.requiresAuthentication(request, response)).isTrue();
|
assertThat(filter.requiresAuthentication(request, response)).isTrue();
|
||||||
SecurityContextHolder.getContext().setAuthentication(
|
SecurityContextHolder.getContext().setAuthentication(
|
||||||
new TestingAuthenticationToken("un", "principal", AuthorityUtils
|
new TestingAuthenticationToken("un", "principal"));
|
||||||
.createAuthorityList("ROLE_ANONYMOUS")));
|
|
||||||
assertThat(filter.requiresAuthentication(request, response)).isTrue();
|
assertThat(filter.requiresAuthentication(request, response)).isTrue();
|
||||||
SecurityContextHolder.getContext().setAuthentication(
|
SecurityContextHolder.getContext().setAuthentication(
|
||||||
new TestingAuthenticationToken("un", "principal", "ROLE_ANONYMOUS"));
|
new TestingAuthenticationToken("un", "principal", "ROLE_ANONYMOUS"));
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ dependencies {
|
|||||||
testCompile spockDependencies
|
testCompile spockDependencies
|
||||||
testCompile 'ch.qos.logback:logback-classic'
|
testCompile 'ch.qos.logback:logback-classic'
|
||||||
testCompile 'javax.annotation:jsr250-api:1.0'
|
testCompile 'javax.annotation:jsr250-api:1.0'
|
||||||
|
testCompile 'javax.xml.bind:jaxb-api'
|
||||||
testCompile 'ldapsdk:ldapsdk:4.1'
|
testCompile 'ldapsdk:ldapsdk:4.1'
|
||||||
testCompile('net.sourceforge.htmlunit:htmlunit') {
|
testCompile('net.sourceforge.htmlunit:htmlunit') {
|
||||||
exclude group: 'commons-logging', module: 'commons-logging'
|
exclude group: 'commons-logging', module: 'commons-logging'
|
||||||
|
|||||||
+1
-1
@@ -27,7 +27,7 @@ import org.springframework.security.config.annotation.authentication.configurati
|
|||||||
* {@link AuthenticationConfiguration} to configure the global
|
* {@link AuthenticationConfiguration} to configure the global
|
||||||
* {@link AuthenticationManagerBuilder}.
|
* {@link AuthenticationManagerBuilder}.
|
||||||
*
|
*
|
||||||
* @since 3.2.1
|
* @since 5.0
|
||||||
* @author Rob Winch
|
* @author Rob Winch
|
||||||
*/
|
*/
|
||||||
@Order(100)
|
@Order(100)
|
||||||
|
|||||||
+1
-1
@@ -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()
|
||||||
|
|||||||
+634
-21
File diff suppressed because it is too large
Load Diff
+2
-1
@@ -186,13 +186,14 @@ class LogoutConfigurerTests extends BaseSpringSpec {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def "LogoutConfigurer content negotiation default redirects"() {
|
def "LogoutConfigurer content negotiation text/html redirects"() {
|
||||||
setup:
|
setup:
|
||||||
loadConfig(LogoutHandlerContentNegotiation)
|
loadConfig(LogoutHandlerContentNegotiation)
|
||||||
when:
|
when:
|
||||||
login()
|
login()
|
||||||
request.method = 'POST'
|
request.method = 'POST'
|
||||||
request.servletPath = '/logout'
|
request.servletPath = '/logout'
|
||||||
|
request.addHeader('Accept', 'text/html')
|
||||||
springSecurityFilterChain.doFilter(request,response,chain)
|
springSecurityFilterChain.doFilter(request,response,chain)
|
||||||
then:
|
then:
|
||||||
response.status == 302
|
response.status == 302
|
||||||
|
|||||||
+42
-16
@@ -28,7 +28,7 @@ import org.springframework.test.web.reactive.server.FluxExchangeResult;
|
|||||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.Collections;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -45,7 +45,7 @@ public class HeaderSpecTests {
|
|||||||
|
|
||||||
HttpHeaders expectedHeaders = new HttpHeaders();
|
HttpHeaders expectedHeaders = new HttpHeaders();
|
||||||
|
|
||||||
Set<String> ignoredHeaderNames = Collections.singleton(HttpHeaders.CONTENT_TYPE);
|
Set<String> headerNamesNotPresent = new HashSet<>();
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setup() {
|
public void setup() {
|
||||||
@@ -60,6 +60,23 @@ public class HeaderSpecTests {
|
|||||||
.add(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "1 ; mode=block");
|
.add(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "1 ; mode=block");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void headersWhenDisableThenNoSecurityHeaders() {
|
||||||
|
new HashSet<>(this.expectedHeaders.keySet()).forEach(this::expectHeaderNamesNotPresent);
|
||||||
|
|
||||||
|
this.headers.disable();
|
||||||
|
|
||||||
|
assertHeaders();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void headersWhenDisableAndInvokedExplicitlyThenDefautsUsed() {
|
||||||
|
this.headers.disable()
|
||||||
|
.headers();
|
||||||
|
|
||||||
|
assertHeaders();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void headersWhenDefaultsThenAllDefaultsWritten() {
|
public void headersWhenDefaultsThenAllDefaultsWritten() {
|
||||||
assertHeaders();
|
assertHeaders();
|
||||||
@@ -67,9 +84,7 @@ public class HeaderSpecTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void headersWhenCacheDisableThenCacheNotWritten() {
|
public void headersWhenCacheDisableThenCacheNotWritten() {
|
||||||
this.expectedHeaders.remove(HttpHeaders.CACHE_CONTROL);
|
expectHeaderNamesNotPresent(HttpHeaders.CACHE_CONTROL, HttpHeaders.PRAGMA, HttpHeaders.EXPIRES);
|
||||||
this.expectedHeaders.remove(HttpHeaders.PRAGMA);
|
|
||||||
this.expectedHeaders.remove(HttpHeaders.EXPIRES);
|
|
||||||
this.headers.cache().disable();
|
this.headers.cache().disable();
|
||||||
|
|
||||||
assertHeaders();
|
assertHeaders();
|
||||||
@@ -77,7 +92,7 @@ public class HeaderSpecTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void headersWhenContentOptionsDisableThenContentTypeOptionsNotWritten() {
|
public void headersWhenContentOptionsDisableThenContentTypeOptionsNotWritten() {
|
||||||
this.expectedHeaders.remove(ContentTypeOptionsServerHttpHeadersWriter.X_CONTENT_OPTIONS);
|
expectHeaderNamesNotPresent(ContentTypeOptionsServerHttpHeadersWriter.X_CONTENT_OPTIONS);
|
||||||
this.headers.contentTypeOptions().disable();
|
this.headers.contentTypeOptions().disable();
|
||||||
|
|
||||||
assertHeaders();
|
assertHeaders();
|
||||||
@@ -85,7 +100,7 @@ public class HeaderSpecTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void headersWhenHstsDisableThenHstsNotWritten() {
|
public void headersWhenHstsDisableThenHstsNotWritten() {
|
||||||
this.expectedHeaders.remove(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY);
|
expectHeaderNamesNotPresent(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY);
|
||||||
this.headers.hsts().disable();
|
this.headers.hsts().disable();
|
||||||
|
|
||||||
assertHeaders();
|
assertHeaders();
|
||||||
@@ -103,7 +118,7 @@ public class HeaderSpecTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void headersWhenFrameOptionsDisableThenFrameOptionsNotWritten() {
|
public void headersWhenFrameOptionsDisableThenFrameOptionsNotWritten() {
|
||||||
this.expectedHeaders.remove(XFrameOptionsServerHttpHeadersWriter.X_FRAME_OPTIONS);
|
expectHeaderNamesNotPresent(XFrameOptionsServerHttpHeadersWriter.X_FRAME_OPTIONS);
|
||||||
this.headers.frameOptions().disable();
|
this.headers.frameOptions().disable();
|
||||||
|
|
||||||
assertHeaders();
|
assertHeaders();
|
||||||
@@ -111,22 +126,29 @@ public class HeaderSpecTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void headersWhenFrameOptionsModeThenFrameOptionsCustomMode() {
|
public void headersWhenFrameOptionsModeThenFrameOptionsCustomMode() {
|
||||||
this.expectedHeaders.remove(XFrameOptionsServerHttpHeadersWriter.X_FRAME_OPTIONS);
|
this.expectedHeaders.set(XFrameOptionsServerHttpHeadersWriter.X_FRAME_OPTIONS, "SAMEORIGIN");
|
||||||
this.expectedHeaders
|
this.headers
|
||||||
.add(XFrameOptionsServerHttpHeadersWriter.X_FRAME_OPTIONS, "SAMEORIGIN");
|
.frameOptions()
|
||||||
this.headers.frameOptions().mode(XFrameOptionsServerHttpHeadersWriter.Mode.SAMEORIGIN);
|
.mode(XFrameOptionsServerHttpHeadersWriter.Mode.SAMEORIGIN);
|
||||||
|
|
||||||
assertHeaders();
|
assertHeaders();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void headersWhenXssProtectionDisableThenXssProtectionNotWritten() {
|
public void headersWhenXssProtectionDisableThenXssProtectionNotWritten() {
|
||||||
this.expectedHeaders.remove("X-Xss-Protection");
|
expectHeaderNamesNotPresent("X-Xss-Protection");
|
||||||
this.headers.xssProtection().disable();
|
this.headers.xssProtection().disable();
|
||||||
|
|
||||||
assertHeaders();
|
assertHeaders();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void expectHeaderNamesNotPresent(String... headerNames) {
|
||||||
|
for(String headerName : headerNames) {
|
||||||
|
this.expectedHeaders.remove(headerName);
|
||||||
|
this.headerNamesNotPresent.add(headerName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void assertHeaders() {
|
private void assertHeaders() {
|
||||||
WebTestClient client = buildClient();
|
WebTestClient client = buildClient();
|
||||||
FluxExchangeResult<String> response = client.get()
|
FluxExchangeResult<String> response = client.get()
|
||||||
@@ -135,10 +157,14 @@ public class HeaderSpecTests {
|
|||||||
.returnResult(String.class);
|
.returnResult(String.class);
|
||||||
|
|
||||||
Map<String, List<String>> responseHeaders = response.getResponseHeaders();
|
Map<String, List<String>> responseHeaders = response.getResponseHeaders();
|
||||||
this.ignoredHeaderNames.stream().forEach(responseHeaders::remove);
|
|
||||||
|
|
||||||
assertThat(responseHeaders).describedAs(response.toString()).isEqualTo(
|
if (!this.expectedHeaders.isEmpty()) {
|
||||||
this.expectedHeaders);
|
assertThat(responseHeaders).describedAs(response.toString())
|
||||||
|
.containsAllEntriesOf(this.expectedHeaders);
|
||||||
|
}
|
||||||
|
if (!this.headerNamesNotPresent.isEmpty()) {
|
||||||
|
assertThat(responseHeaders.keySet()).doesNotContainAnyElementsOf(this.headerNamesNotPresent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private WebTestClient buildClient() {
|
private WebTestClient buildClient() {
|
||||||
|
|||||||
+9
@@ -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");
|
||||||
|
|||||||
+1
@@ -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 {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+3
-3
@@ -16,11 +16,11 @@
|
|||||||
|
|
||||||
package org.springframework.security.authentication;
|
package org.springframework.security.authentication;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
import org.springframework.security.core.authority.AuthorityUtils;
|
import org.springframework.security.core.authority.AuthorityUtils;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An {@link org.springframework.security.core.Authentication} implementation that is
|
* An {@link org.springframework.security.core.Authentication} implementation that is
|
||||||
* designed for use whilst unit testing.
|
* designed for use whilst unit testing.
|
||||||
@@ -49,7 +49,6 @@ public class TestingAuthenticationToken extends AbstractAuthenticationToken {
|
|||||||
public TestingAuthenticationToken(Object principal, Object credentials,
|
public TestingAuthenticationToken(Object principal, Object credentials,
|
||||||
String... authorities) {
|
String... authorities) {
|
||||||
this(principal, credentials, AuthorityUtils.createAuthorityList(authorities));
|
this(principal, credentials, AuthorityUtils.createAuthorityList(authorities));
|
||||||
setAuthenticated(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public TestingAuthenticationToken(Object principal, Object credentials,
|
public TestingAuthenticationToken(Object principal, Object credentials,
|
||||||
@@ -57,6 +56,7 @@ public class TestingAuthenticationToken extends AbstractAuthenticationToken {
|
|||||||
super(authorities);
|
super(authorities);
|
||||||
this.principal = principal;
|
this.principal = principal;
|
||||||
this.credentials = credentials;
|
this.credentials = credentials;
|
||||||
|
setAuthenticated(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ~ Methods
|
// ~ Methods
|
||||||
|
|||||||
+8
@@ -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;
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ public class SpringSecurityCoreVersion {
|
|||||||
*/
|
*/
|
||||||
public static final long SERIAL_VERSION_UID = 500L;
|
public static final long SERIAL_VERSION_UID = 500L;
|
||||||
|
|
||||||
static final String MIN_SPRING_VERSION = "5.0.4.RELEASE";
|
static final String MIN_SPRING_VERSION = "5.0.5.RELEASE";
|
||||||
|
|
||||||
static {
|
static {
|
||||||
performVersionChecks();
|
performVersionChecks();
|
||||||
|
|||||||
+13
@@ -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()));
|
||||||
|
|||||||
+11
-2
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -327,6 +327,7 @@ public class User implements UserDetails, CredentialsContainer {
|
|||||||
* acceptable for demos and getting started. For production purposes, ensure the
|
* acceptable for demos and getting started. For production purposes, ensure the
|
||||||
* password is encoded externally. See the method Javadoc for additional details.
|
* password is encoded externally. See the method Javadoc for additional details.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public static UserBuilder withDefaultPasswordEncoder() {
|
public static UserBuilder withDefaultPasswordEncoder() {
|
||||||
logger.warn("User.withDefaultPasswordEncoder() is considered unsafe for production and is only intended for sample applications.");
|
logger.warn("User.withDefaultPasswordEncoder() is considered unsafe for production and is only intended for sample applications.");
|
||||||
PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
|
PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
|
||||||
|
|||||||
+15
-3
@@ -18,16 +18,28 @@ package org.springframework.security.jackson2;
|
|||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JacksonAnnotation;
|
import com.fasterxml.jackson.annotation.JacksonAnnotation;
|
||||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||||
import com.fasterxml.jackson.databind.*;
|
import com.fasterxml.jackson.databind.DatabindContext;
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationConfig;
|
||||||
|
import com.fasterxml.jackson.databind.JavaType;
|
||||||
|
import com.fasterxml.jackson.databind.Module;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.fasterxml.jackson.databind.cfg.MapperConfig;
|
import com.fasterxml.jackson.databind.cfg.MapperConfig;
|
||||||
import com.fasterxml.jackson.databind.jsontype.*;
|
import com.fasterxml.jackson.databind.jsontype.NamedType;
|
||||||
|
import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
|
||||||
|
import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.springframework.core.annotation.AnnotationUtils;
|
import org.springframework.core.annotation.AnnotationUtils;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This utility class will find all the SecurityModules in classpath.
|
* This utility class will find all the SecurityModules in classpath.
|
||||||
|
|||||||
+1
-1
@@ -30,7 +30,7 @@ public class MethodInvocationFactory {
|
|||||||
public static MockMethodInvocation createSec2150MethodInvocation()
|
public static MockMethodInvocation createSec2150MethodInvocation()
|
||||||
throws NoSuchMethodException {
|
throws NoSuchMethodException {
|
||||||
ProxyFactory factory = new ProxyFactory(new Class[] { PersonRepository.class });
|
ProxyFactory factory = new ProxyFactory(new Class[] { PersonRepository.class });
|
||||||
factory.setTargetClass(CrudRepository.class);
|
factory.setTargetClass(PersonRepositoryImpl.class);
|
||||||
PersonRepository repository = (PersonRepository) factory.getProxy();
|
PersonRepository repository = (PersonRepository) factory.getProxy();
|
||||||
return new MockMethodInvocation(repository, PersonRepository.class, "findAll");
|
return new MockMethodInvocation(repository, PersonRepository.class, "findAll");
|
||||||
}
|
}
|
||||||
|
|||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2018 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.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.springframework.security.access.annotation.sec2150;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Joe Grandja
|
||||||
|
*/
|
||||||
|
public class PersonRepositoryImpl implements PersonRepository {
|
||||||
|
@Override
|
||||||
|
public Iterable<Object> findAll() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
+53
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2018 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.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.springframework.security.authentication;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Josh Cummings
|
||||||
|
*/
|
||||||
|
public class TestingAuthenticationTokenTests {
|
||||||
|
@Test
|
||||||
|
public void constructorWhenNoAuthoritiesThenUnauthenticated() {
|
||||||
|
TestingAuthenticationToken unauthenticated =
|
||||||
|
new TestingAuthenticationToken("principal", "credentials");
|
||||||
|
|
||||||
|
assertThat(unauthenticated.isAuthenticated()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void constructorWhenArityAuthoritiesThenAuthenticated() {
|
||||||
|
TestingAuthenticationToken authenticated =
|
||||||
|
new TestingAuthenticationToken("principal", "credentials", "authority");
|
||||||
|
|
||||||
|
assertThat(authenticated.isAuthenticated()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void constructorWhenCollectionAuthoritiesThenAuthenticated() {
|
||||||
|
TestingAuthenticationToken authenticated =
|
||||||
|
new TestingAuthenticationToken("principal", "credentials",
|
||||||
|
Arrays.asList(new SimpleGrantedAuthority("authority")));
|
||||||
|
|
||||||
|
assertThat(authenticated.isAuthenticated()).isTrue();
|
||||||
|
}
|
||||||
|
}
|
||||||
+10
-6
@@ -16,7 +16,12 @@
|
|||||||
|
|
||||||
package org.springframework.security.jackson2;
|
package org.springframework.security.jackson2;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.skyscreamer.jsonassert.JSONAssert;
|
import org.skyscreamer.jsonassert.JSONAssert;
|
||||||
@@ -25,10 +30,7 @@ import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
|||||||
import org.springframework.security.core.context.SecurityContext;
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
import org.springframework.security.core.context.SecurityContextImpl;
|
import org.springframework.security.core.context.SecurityContextImpl;
|
||||||
|
|
||||||
import java.io.IOException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
import java.util.Collections;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Jitendra Singh
|
* @author Jitendra Singh
|
||||||
@@ -59,6 +61,8 @@ public class SecurityContextMixinTests extends AbstractMixinTests {
|
|||||||
assertThat(context.getAuthentication().getPrincipal()).isEqualTo("admin");
|
assertThat(context.getAuthentication().getPrincipal()).isEqualTo("admin");
|
||||||
assertThat(context.getAuthentication().getCredentials()).isEqualTo("1234");
|
assertThat(context.getAuthentication().getCredentials()).isEqualTo("1234");
|
||||||
assertThat(context.getAuthentication().isAuthenticated()).isTrue();
|
assertThat(context.getAuthentication().isAuthenticated()).isTrue();
|
||||||
assertThat(context.getAuthentication().getAuthorities()).hasSize(1).contains(new SimpleGrantedAuthority("ROLE_USER"));
|
Collection authorities = context.getAuthentication().getAuthorities();
|
||||||
|
assertThat(authorities).hasSize(1);
|
||||||
|
assertThat(authorities).contains(new SimpleGrantedAuthority("ROLE_USER"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ package org.springframework.security.crypto.codec;
|
|||||||
* @since 3.0
|
* @since 3.0
|
||||||
* @deprecated Use java.util.Base64
|
* @deprecated Use java.util.Base64
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public final class Base64 {
|
public final class Base64 {
|
||||||
|
|
||||||
/** No options specified. Value is zero. */
|
/** No options specified. Value is zero. */
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ apply plugin: 'io.spring.convention.spring-module'
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile project(':spring-security-core')
|
compile project(':spring-security-core')
|
||||||
|
compile 'javax.xml.bind:jaxb-api'
|
||||||
compile 'org.springframework.data:spring-data-commons'
|
compile 'org.springframework.data:spring-data-commons'
|
||||||
compile 'org.springframework:spring-core'
|
compile 'org.springframework:spring-core'
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
gaeVersion=1.9.62
|
gaeVersion=1.9.62
|
||||||
springBootVersion=2.0.0.RC2
|
springBootVersion=2.0.0.RELEASE
|
||||||
version=5.0.4.BUILD-SNAPSHOT
|
version=5.0.4.RELEASE
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
dependencyManagement {
|
dependencyManagement {
|
||||||
imports {
|
imports {
|
||||||
mavenBom 'io.projectreactor:reactor-bom:Bismuth-SR7'
|
mavenBom 'io.projectreactor:reactor-bom:Bismuth-SR8'
|
||||||
mavenBom 'org.springframework:spring-framework-bom:5.0.4.RELEASE'
|
mavenBom 'org.springframework:spring-framework-bom:5.0.5.RELEASE'
|
||||||
mavenBom 'org.springframework.data:spring-data-releasetrain:Kay-SR5'
|
mavenBom 'org.springframework.data:spring-data-releasetrain:Kay-SR6'
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
dependency 'cglib:cglib-nodep:3.2.6'
|
dependency 'cglib:cglib-nodep:3.2.6'
|
||||||
dependency 'com.squareup.okhttp3:mockwebserver:3.9.1'
|
dependency 'com.squareup.okhttp3:mockwebserver:3.10.0'
|
||||||
dependency 'opensymphony:sitemesh:2.4.2'
|
dependency 'opensymphony:sitemesh:2.4.2'
|
||||||
dependency 'org.gebish:geb-spock:0.10.0'
|
dependency 'org.gebish:geb-spock:0.10.0'
|
||||||
dependency 'org.jasig.cas:cas-server-webapp:4.2.7'
|
dependency 'org.jasig.cas:cas-server-webapp:4.2.7'
|
||||||
@@ -28,25 +28,27 @@ dependencyManagement {
|
|||||||
dependency 'asm:asm:3.1'
|
dependency 'asm:asm:3.1'
|
||||||
dependency 'ch.qos.logback:logback-classic:1.2.3'
|
dependency 'ch.qos.logback:logback-classic:1.2.3'
|
||||||
dependency 'ch.qos.logback:logback-core:1.2.3'
|
dependency 'ch.qos.logback:logback-core:1.2.3'
|
||||||
dependency 'com.fasterxml.jackson.core:jackson-annotations:2.9.4'
|
dependency 'com.fasterxml.jackson.core:jackson-annotations:2.9.5'
|
||||||
dependency 'com.fasterxml.jackson.core:jackson-core:2.9.4'
|
dependency 'com.fasterxml.jackson.core:jackson-core:2.9.5'
|
||||||
dependency 'com.fasterxml.jackson.core:jackson-databind:2.9.4'
|
dependency 'com.fasterxml.jackson.core:jackson-databind:2.9.5'
|
||||||
dependency 'com.fasterxml:classmate:1.3.4'
|
dependency 'com.fasterxml:classmate:1.3.4'
|
||||||
dependency 'com.github.stephenc.jcip:jcip-annotations:1.0-1'
|
dependency 'com.github.stephenc.jcip:jcip-annotations:1.0-1'
|
||||||
dependency 'com.google.appengine:appengine-api-1.0-sdk:1.9.62'
|
dependency 'com.google.appengine:appengine-api-1.0-sdk:1.9.62'
|
||||||
dependency 'com.google.appengine:appengine-api-labs:1.9.62'
|
dependency 'com.google.appengine:appengine-api-labs:1.9.63'
|
||||||
dependency 'com.google.appengine:appengine-api-stubs:1.9.62'
|
dependency 'com.google.appengine:appengine-api-stubs:1.9.63'
|
||||||
dependency 'com.google.appengine:appengine-testing:1.9.62'
|
dependency 'com.google.appengine:appengine-testing:1.9.63'
|
||||||
dependency 'com.google.appengine:appengine:1.9.62'
|
dependency 'com.google.appengine:appengine:1.9.63'
|
||||||
dependency 'com.google.code.gson:gson:2.8.2'
|
dependency 'com.google.code.gson:gson:2.8.2'
|
||||||
dependency 'com.google.guava:guava:20.0'
|
dependency 'com.google.guava:guava:20.0'
|
||||||
dependency 'com.google.inject:guice:3.0'
|
dependency 'com.google.inject:guice:3.0'
|
||||||
dependency 'com.nimbusds:lang-tag:1.4.3'
|
dependency 'com.nimbusds:lang-tag:1.4.3'
|
||||||
dependency 'com.nimbusds:nimbus-jose-jwt:5.5'
|
dependency 'com.nimbusds:nimbus-jose-jwt:5.9'
|
||||||
dependency 'com.nimbusds:oauth2-oidc-sdk:5.54'
|
dependency 'com.nimbusds:oauth2-oidc-sdk:5.57'
|
||||||
dependency 'com.squareup.okhttp3:okhttp:3.9.0'
|
dependency 'com.squareup.okhttp3:okhttp:3.9.0'
|
||||||
dependency 'com.squareup.okio:okio:1.13.0'
|
dependency 'com.squareup.okio:okio:1.13.0'
|
||||||
dependency 'com.unboundid:unboundid-ldapsdk:4.0.4'
|
dependency 'com.sun.xml.bind:jaxb-core:2.3.0'
|
||||||
|
dependency 'com.sun.xml.bind:jaxb-impl:2.3.0'
|
||||||
|
dependency 'com.unboundid:unboundid-ldapsdk:4.0.5'
|
||||||
dependency 'com.vaadin.external.google:android-json:0.0.20131108.vaadin1'
|
dependency 'com.vaadin.external.google:android-json:0.0.20131108.vaadin1'
|
||||||
dependency 'commons-cli:commons-cli:1.4'
|
dependency 'commons-cli:commons-cli:1.4'
|
||||||
dependency 'commons-codec:commons-codec:1.11'
|
dependency 'commons-codec:commons-codec:1.11'
|
||||||
@@ -64,6 +66,7 @@ dependencyManagement {
|
|||||||
dependency 'javax.servlet.jsp:javax.servlet.jsp-api:2.3.2-b02'
|
dependency 'javax.servlet.jsp:javax.servlet.jsp-api:2.3.2-b02'
|
||||||
dependency 'javax.servlet:javax.servlet-api:4.0.0'
|
dependency 'javax.servlet:javax.servlet-api:4.0.0'
|
||||||
dependency 'javax.validation:validation-api:2.0.1.Final'
|
dependency 'javax.validation:validation-api:2.0.1.Final'
|
||||||
|
dependency 'javax.xml.bind:jaxb-api:2.3.0'
|
||||||
dependency 'junit:junit:4.12'
|
dependency 'junit:junit:4.12'
|
||||||
dependency 'ldapsdk:ldapsdk:4.1'
|
dependency 'ldapsdk:ldapsdk:4.1'
|
||||||
dependency 'net.bytebuddy:byte-buddy-agent:1.7.9'
|
dependency 'net.bytebuddy:byte-buddy-agent:1.7.9'
|
||||||
@@ -78,7 +81,7 @@ dependencyManagement {
|
|||||||
dependency 'net.sourceforge.htmlunit:neko-htmlunit:2.28'
|
dependency 'net.sourceforge.htmlunit:neko-htmlunit:2.28'
|
||||||
dependency 'net.sourceforge.nekohtml:nekohtml:1.9.22'
|
dependency 'net.sourceforge.nekohtml:nekohtml:1.9.22'
|
||||||
dependency 'nz.net.ultraq.thymeleaf:thymeleaf-expression-processor:1.1.3'
|
dependency 'nz.net.ultraq.thymeleaf:thymeleaf-expression-processor:1.1.3'
|
||||||
dependency 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:2.2.2'
|
dependency 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:2.3.0'
|
||||||
dependency 'ognl:ognl:3.1.12'
|
dependency 'ognl:ognl:3.1.12'
|
||||||
dependency 'org.akhikhl.gretty:gretty-runner-tomcat8:2.0.0'
|
dependency 'org.akhikhl.gretty:gretty-runner-tomcat8:2.0.0'
|
||||||
dependency 'org.akhikhl.gretty:gretty-runner-tomcat:2.0.0'
|
dependency 'org.akhikhl.gretty:gretty-runner-tomcat:2.0.0'
|
||||||
@@ -127,16 +130,16 @@ dependencyManagement {
|
|||||||
dependency 'org.apache.tomcat.embed:tomcat-embed-logging-log4j:8.0.44'
|
dependency 'org.apache.tomcat.embed:tomcat-embed-logging-log4j:8.0.44'
|
||||||
dependency 'org.apache.tomcat.embed:tomcat-embed-websocket:8.5.23'
|
dependency 'org.apache.tomcat.embed:tomcat-embed-websocket:8.5.23'
|
||||||
dependency 'org.apache.tomcat:tomcat-annotations-api:8.5.23'
|
dependency 'org.apache.tomcat:tomcat-annotations-api:8.5.23'
|
||||||
dependency 'org.aspectj:aspectjrt:1.8.13'
|
dependency 'org.aspectj:aspectjrt:1.9.0'
|
||||||
dependency 'org.aspectj:aspectjtools:1.8.13'
|
dependency 'org.aspectj:aspectjtools:1.9.0'
|
||||||
dependency 'org.aspectj:aspectjweaver:1.8.13'
|
dependency 'org.aspectj:aspectjweaver:1.8.13'
|
||||||
dependency 'org.assertj:assertj-core:3.9.0'
|
dependency 'org.assertj:assertj-core:3.9.1'
|
||||||
dependency 'org.attoparser:attoparser:2.0.4.RELEASE'
|
dependency 'org.attoparser:attoparser:2.0.4.RELEASE'
|
||||||
dependency 'org.bouncycastle:bcpkix-jdk15on:1.59'
|
dependency 'org.bouncycastle:bcpkix-jdk15on:1.59'
|
||||||
dependency 'org.bouncycastle:bcprov-jdk15on:1.58'
|
dependency 'org.bouncycastle:bcprov-jdk15on:1.58'
|
||||||
dependency 'org.codehaus.groovy:groovy-all:2.5.0-beta-1'
|
dependency 'org.codehaus.groovy:groovy-all:2.4.14'
|
||||||
dependency 'org.codehaus.groovy:groovy-json:2.5.0-beta-1'
|
dependency 'org.codehaus.groovy:groovy-json:2.4.14'
|
||||||
dependency 'org.codehaus.groovy:groovy:2.5.0-beta-1'
|
dependency 'org.codehaus.groovy:groovy:2.4.14'
|
||||||
dependency 'org.eclipse.jdt:ecj:3.12.3'
|
dependency 'org.eclipse.jdt:ecj:3.12.3'
|
||||||
dependency 'org.eclipse.jetty.websocket:websocket-api:9.4.7.v20170914'
|
dependency 'org.eclipse.jetty.websocket:websocket-api:9.4.7.v20170914'
|
||||||
dependency 'org.eclipse.jetty.websocket:websocket-client:9.4.7.v20170914'
|
dependency 'org.eclipse.jetty.websocket:websocket-client:9.4.7.v20170914'
|
||||||
@@ -158,18 +161,18 @@ dependencyManagement {
|
|||||||
dependency 'org.hibernate.common:hibernate-commons-annotations:5.0.1.Final'
|
dependency 'org.hibernate.common:hibernate-commons-annotations:5.0.1.Final'
|
||||||
dependency 'org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.0.Final'
|
dependency 'org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.0.Final'
|
||||||
dependency 'org.hibernate:hibernate-core:5.2.12.Final'
|
dependency 'org.hibernate:hibernate-core:5.2.12.Final'
|
||||||
dependency 'org.hibernate:hibernate-entitymanager:5.2.12.Final'
|
dependency 'org.hibernate:hibernate-entitymanager:5.2.16.Final'
|
||||||
dependency 'org.hibernate:hibernate-validator:6.0.5.Final'
|
dependency 'org.hibernate:hibernate-validator:6.0.9.Final'
|
||||||
dependency 'org.hsqldb:hsqldb:2.4.0'
|
dependency 'org.hsqldb:hsqldb:2.4.0'
|
||||||
dependency 'org.jasig.cas.client:cas-client-core:3.5.0'
|
dependency 'org.jasig.cas.client:cas-client-core:3.5.0'
|
||||||
dependency 'org.javassist:javassist:3.22.0-CR2'
|
dependency 'org.javassist:javassist:3.22.0-CR2'
|
||||||
dependency 'org.jboss.logging:jboss-logging:3.3.1.Final'
|
dependency 'org.jboss.logging:jboss-logging:3.3.1.Final'
|
||||||
dependency 'org.jboss.spec.javax.transaction:jboss-transaction-api_1.2_spec:1.0.1.Final'
|
dependency 'org.jboss.spec.javax.transaction:jboss-transaction-api_1.2_spec:1.0.1.Final'
|
||||||
dependency 'org.jboss:jandex:2.0.3.Final'
|
dependency 'org.jboss:jandex:2.0.3.Final'
|
||||||
dependency 'org.mockito:mockito-core:2.13.0'
|
dependency 'org.mockito:mockito-core:2.17.0'
|
||||||
dependency 'org.objenesis:objenesis:2.6'
|
dependency 'org.objenesis:objenesis:2.6'
|
||||||
dependency 'org.openid4java:openid4java-nodeps:0.9.6'
|
dependency 'org.openid4java:openid4java-nodeps:0.9.6'
|
||||||
dependency 'org.ow2.asm:asm:5.0.4'
|
dependency 'org.ow2.asm:asm:6.0'
|
||||||
dependency 'org.reactivestreams:reactive-streams:1.0.1'
|
dependency 'org.reactivestreams:reactive-streams:1.0.1'
|
||||||
dependency 'org.seleniumhq.selenium:htmlunit-driver:2.29.2'
|
dependency 'org.seleniumhq.selenium:htmlunit-driver:2.29.2'
|
||||||
dependency 'org.seleniumhq.selenium:selenium-api:3.8.1'
|
dependency 'org.seleniumhq.selenium:selenium-api:3.8.1'
|
||||||
|
|||||||
+1
-1
@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
|
|||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.5.1-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-bin.zip
|
||||||
|
|||||||
+12
-12
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2017 the original author or authors.
|
* Copyright 2002-2018 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -38,14 +38,14 @@ public abstract class AbstractOAuth2Token implements Serializable {
|
|||||||
* Sub-class constructor.
|
* Sub-class constructor.
|
||||||
*
|
*
|
||||||
* @param tokenValue the token value
|
* @param tokenValue the token value
|
||||||
* @param issuedAt the time at which the token was issued
|
* @param issuedAt the time at which the token was issued, may be null
|
||||||
* @param expiresAt the expiration time on or after which the token MUST NOT be accepted
|
* @param expiresAt the expiration time on or after which the token MUST NOT be accepted, may be null
|
||||||
*/
|
*/
|
||||||
protected AbstractOAuth2Token(String tokenValue, Instant issuedAt, Instant expiresAt) {
|
protected AbstractOAuth2Token(String tokenValue, Instant issuedAt, Instant expiresAt) {
|
||||||
Assert.hasText(tokenValue, "tokenValue cannot be empty");
|
Assert.hasText(tokenValue, "tokenValue cannot be empty");
|
||||||
Assert.notNull(issuedAt, "issuedAt cannot be null");
|
if (issuedAt != null && expiresAt != null) {
|
||||||
Assert.notNull(expiresAt, "expiresAt cannot be null");
|
Assert.isTrue(expiresAt.isAfter(issuedAt), "expiresAt must be after issuedAt");
|
||||||
Assert.isTrue(expiresAt.isAfter(issuedAt), "expiresAt must be after issuedAt");
|
}
|
||||||
this.tokenValue = tokenValue;
|
this.tokenValue = tokenValue;
|
||||||
this.issuedAt = issuedAt;
|
this.issuedAt = issuedAt;
|
||||||
this.expiresAt = expiresAt;
|
this.expiresAt = expiresAt;
|
||||||
@@ -63,7 +63,7 @@ public abstract class AbstractOAuth2Token implements Serializable {
|
|||||||
/**
|
/**
|
||||||
* Returns the time at which the token was issued.
|
* Returns the time at which the token was issued.
|
||||||
*
|
*
|
||||||
* @return the time the token was issued
|
* @return the time the token was issued or null
|
||||||
*/
|
*/
|
||||||
public Instant getIssuedAt() {
|
public Instant getIssuedAt() {
|
||||||
return this.issuedAt;
|
return this.issuedAt;
|
||||||
@@ -72,7 +72,7 @@ public abstract class AbstractOAuth2Token implements Serializable {
|
|||||||
/**
|
/**
|
||||||
* Returns the expiration time on or after which the token MUST NOT be accepted.
|
* Returns the expiration time on or after which the token MUST NOT be accepted.
|
||||||
*
|
*
|
||||||
* @return the expiration time of the token
|
* @return the expiration time of the token or null
|
||||||
*/
|
*/
|
||||||
public Instant getExpiresAt() {
|
public Instant getExpiresAt() {
|
||||||
return this.expiresAt;
|
return this.expiresAt;
|
||||||
@@ -92,17 +92,17 @@ public abstract class AbstractOAuth2Token implements Serializable {
|
|||||||
if (!this.getTokenValue().equals(that.getTokenValue())) {
|
if (!this.getTokenValue().equals(that.getTokenValue())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!this.getIssuedAt().equals(that.getIssuedAt())) {
|
if (this.getIssuedAt() != null ? !this.getIssuedAt().equals(that.getIssuedAt()) : that.getIssuedAt() != null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return this.getExpiresAt().equals(that.getExpiresAt());
|
return this.getExpiresAt() != null ? this.getExpiresAt().equals(that.getExpiresAt()) : that.getExpiresAt() == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
int result = this.getTokenValue().hashCode();
|
int result = this.getTokenValue().hashCode();
|
||||||
result = 31 * result + this.getIssuedAt().hashCode();
|
result = 31 * result + (this.getIssuedAt() != null ? this.getIssuedAt().hashCode() : 0);
|
||||||
result = 31 * result + this.getExpiresAt().hashCode();
|
result = 31 * result + (this.getExpiresAt() != null ? this.getExpiresAt().hashCode() : 0);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-4
@@ -21,6 +21,7 @@ import java.net.MalformedURLException;
|
|||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -81,11 +82,18 @@ public interface ClaimAccessor {
|
|||||||
if (!this.containsClaim(claim)) {
|
if (!this.containsClaim(claim)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
try {
|
Object claimValue = this.getClaims().get(claim);
|
||||||
return Instant.ofEpochMilli(Long.valueOf(this.getClaimAsString(claim)));
|
if (Long.class.isAssignableFrom(claimValue.getClass())) {
|
||||||
} catch (NumberFormatException ex) {
|
return Instant.ofEpochSecond((Long) claimValue);
|
||||||
throw new IllegalArgumentException("Unable to convert claim '" + claim + "' to Instant: " + ex.getMessage(), ex);
|
|
||||||
}
|
}
|
||||||
|
if (Date.class.isAssignableFrom(claimValue.getClass())) {
|
||||||
|
return ((Date) claimValue).toInstant();
|
||||||
|
}
|
||||||
|
if (Instant.class.isAssignableFrom(claimValue.getClass())) {
|
||||||
|
return (Instant) claimValue;
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Unable to convert claim '" + claim +
|
||||||
|
"' of type '" + claimValue.getClass() + "' to Instant.");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+73
@@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2018 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.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.springframework.security.oauth2.core;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link ClaimAccessor}.
|
||||||
|
*
|
||||||
|
* @author Joe Grandja
|
||||||
|
*/
|
||||||
|
public class ClaimAccessorTests {
|
||||||
|
private Map<String, Object> claims = new HashMap<>();
|
||||||
|
private ClaimAccessor claimAccessor = (() -> this.claims);
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
this.claims.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// gh-5192
|
||||||
|
@Test
|
||||||
|
public void getClaimAsInstantWhenDateTypeThenReturnInstant() {
|
||||||
|
Instant expectedClaimValue = Instant.now();
|
||||||
|
String claimName = "date";
|
||||||
|
this.claims.put(claimName, Date.from(expectedClaimValue));
|
||||||
|
|
||||||
|
assertThat(this.claimAccessor.getClaimAsInstant(claimName)).isBetween(
|
||||||
|
expectedClaimValue.minusSeconds(1), expectedClaimValue.plusSeconds(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// gh-5191
|
||||||
|
@Test
|
||||||
|
public void getClaimAsInstantWhenLongTypeSecondsThenReturnInstant() {
|
||||||
|
Instant expectedClaimValue = Instant.now();
|
||||||
|
String claimName = "longSeconds";
|
||||||
|
this.claims.put(claimName, expectedClaimValue.getEpochSecond());
|
||||||
|
|
||||||
|
assertThat(this.claimAccessor.getClaimAsInstant(claimName)).isBetween(
|
||||||
|
expectedClaimValue.minusSeconds(1), expectedClaimValue.plusSeconds(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getClaimAsInstantWhenInstantTypeThenReturnInstant() {
|
||||||
|
Instant expectedClaimValue = Instant.now();
|
||||||
|
String claimName = "instant";
|
||||||
|
this.claims.put(claimName, expectedClaimValue);
|
||||||
|
|
||||||
|
assertThat(this.claimAccessor.getClaimAsInstant(claimName)).isBetween(
|
||||||
|
expectedClaimValue.minusSeconds(1), expectedClaimValue.plusSeconds(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
-10
@@ -51,16 +51,6 @@ public class OAuth2AccessTokenTests {
|
|||||||
new OAuth2AccessToken(TOKEN_TYPE, null, ISSUED_AT, EXPIRES_AT);
|
new OAuth2AccessToken(TOKEN_TYPE, null, ISSUED_AT, EXPIRES_AT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
|
||||||
public void constructorWhenIssuedAtIsNullThenThrowIllegalArgumentException() {
|
|
||||||
new OAuth2AccessToken(TOKEN_TYPE, TOKEN_VALUE, null, EXPIRES_AT);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
|
||||||
public void constructorWhenExpiresAtIsNullThenThrowIllegalArgumentException() {
|
|
||||||
new OAuth2AccessToken(TOKEN_TYPE, TOKEN_VALUE, ISSUED_AT, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test(expected = IllegalArgumentException.class)
|
||||||
public void constructorWhenIssuedAtAfterExpiresAtThenThrowIllegalArgumentException() {
|
public void constructorWhenIssuedAtAfterExpiresAtThenThrowIllegalArgumentException() {
|
||||||
new OAuth2AccessToken(TOKEN_TYPE, TOKEN_VALUE, Instant.from(EXPIRES_AT).plusSeconds(1), EXPIRES_AT);
|
new OAuth2AccessToken(TOKEN_TYPE, TOKEN_VALUE, Instant.from(EXPIRES_AT).plusSeconds(1), EXPIRES_AT);
|
||||||
|
|||||||
+1
-11
@@ -82,16 +82,6 @@ public class OidcIdTokenTests {
|
|||||||
new OidcIdToken(null, Instant.ofEpochMilli(IAT_VALUE), Instant.ofEpochMilli(EXP_VALUE), CLAIMS);
|
new OidcIdToken(null, Instant.ofEpochMilli(IAT_VALUE), Instant.ofEpochMilli(EXP_VALUE), CLAIMS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
|
||||||
public void constructorWhenIssuedAtIsNullThenThrowIllegalArgumentException() {
|
|
||||||
new OidcIdToken(ID_TOKEN_VALUE, null, Instant.ofEpochMilli(EXP_VALUE), CLAIMS);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
|
||||||
public void constructorWhenExpiresAtIsNullThenThrowIllegalArgumentException() {
|
|
||||||
new OidcIdToken(ID_TOKEN_VALUE, Instant.ofEpochMilli(IAT_VALUE), null, CLAIMS);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test(expected = IllegalArgumentException.class)
|
||||||
public void constructorWhenClaimsIsEmptyThenThrowIllegalArgumentException() {
|
public void constructorWhenClaimsIsEmptyThenThrowIllegalArgumentException() {
|
||||||
new OidcIdToken(ID_TOKEN_VALUE, Instant.ofEpochMilli(IAT_VALUE),
|
new OidcIdToken(ID_TOKEN_VALUE, Instant.ofEpochMilli(IAT_VALUE),
|
||||||
@@ -110,7 +100,7 @@ public class OidcIdTokenTests {
|
|||||||
assertThat(idToken.getAudience()).isEqualTo(AUD_VALUE);
|
assertThat(idToken.getAudience()).isEqualTo(AUD_VALUE);
|
||||||
assertThat(idToken.getIssuedAt().toEpochMilli()).isEqualTo(IAT_VALUE);
|
assertThat(idToken.getIssuedAt().toEpochMilli()).isEqualTo(IAT_VALUE);
|
||||||
assertThat(idToken.getExpiresAt().toEpochMilli()).isEqualTo(EXP_VALUE);
|
assertThat(idToken.getExpiresAt().toEpochMilli()).isEqualTo(EXP_VALUE);
|
||||||
assertThat(idToken.getAuthenticatedAt().toEpochMilli()).isEqualTo(AUTH_TIME_VALUE);
|
assertThat(idToken.getAuthenticatedAt().getEpochSecond()).isEqualTo(AUTH_TIME_VALUE);
|
||||||
assertThat(idToken.getNonce()).isEqualTo(NONCE_VALUE);
|
assertThat(idToken.getNonce()).isEqualTo(NONCE_VALUE);
|
||||||
assertThat(idToken.getAuthenticationContextClass()).isEqualTo(ACR_VALUE);
|
assertThat(idToken.getAuthenticationContextClass()).isEqualTo(ACR_VALUE);
|
||||||
assertThat(idToken.getAuthenticationMethods()).isEqualTo(AMR_VALUE);
|
assertThat(idToken.getAuthenticationMethods()).isEqualTo(AMR_VALUE);
|
||||||
|
|||||||
+1
-1
@@ -137,6 +137,6 @@ public class OidcUserInfoTests {
|
|||||||
assertThat(userInfo.getPhoneNumber()).isEqualTo(PHONE_NUMBER_VALUE);
|
assertThat(userInfo.getPhoneNumber()).isEqualTo(PHONE_NUMBER_VALUE);
|
||||||
assertThat(userInfo.getPhoneNumberVerified()).isEqualTo(PHONE_NUMBER_VERIFIED_VALUE);
|
assertThat(userInfo.getPhoneNumberVerified()).isEqualTo(PHONE_NUMBER_VERIFIED_VALUE);
|
||||||
assertThat(userInfo.getAddress()).isEqualTo(new DefaultAddressStandardClaim.Builder(ADDRESS_VALUE).build());
|
assertThat(userInfo.getAddress()).isEqualTo(new DefaultAddressStandardClaim.Builder(ADDRESS_VALUE).build());
|
||||||
assertThat(userInfo.getUpdatedAt().toEpochMilli()).isEqualTo(UPDATED_AT_VALUE);
|
assertThat(userInfo.getUpdatedAt().getEpochSecond()).isEqualTo(UPDATED_AT_VALUE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,4 +5,6 @@ dependencies {
|
|||||||
compile project(':spring-security-oauth2-core')
|
compile project(':spring-security-oauth2-core')
|
||||||
compile springCoreDependency
|
compile springCoreDependency
|
||||||
compile 'com.nimbusds:nimbus-jose-jwt'
|
compile 'com.nimbusds:nimbus-jose-jwt'
|
||||||
|
|
||||||
|
testCompile powerMock2Dependencies
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-4
@@ -103,12 +103,15 @@ public final class NimbusJwtDecoderJwkSupport implements JwtDecoder {
|
|||||||
// Verify the signature
|
// Verify the signature
|
||||||
JWTClaimsSet jwtClaimsSet = this.jwtProcessor.process(parsedJwt, null);
|
JWTClaimsSet jwtClaimsSet = this.jwtProcessor.process(parsedJwt, null);
|
||||||
|
|
||||||
Instant expiresAt = jwtClaimsSet.getExpirationTime().toInstant();
|
Instant expiresAt = null;
|
||||||
Instant issuedAt;
|
if (jwtClaimsSet.getExpirationTime() != null) {
|
||||||
|
expiresAt = jwtClaimsSet.getExpirationTime().toInstant();
|
||||||
|
}
|
||||||
|
Instant issuedAt = null;
|
||||||
if (jwtClaimsSet.getIssueTime() != null) {
|
if (jwtClaimsSet.getIssueTime() != null) {
|
||||||
issuedAt = jwtClaimsSet.getIssueTime().toInstant();
|
issuedAt = jwtClaimsSet.getIssueTime().toInstant();
|
||||||
} else {
|
} else if (expiresAt != null) {
|
||||||
// issuedAt is required in AbstractOAuth2Token so let's default to expiresAt - 1 second
|
// Default to expiresAt - 1 second
|
||||||
issuedAt = Instant.from(expiresAt).minusSeconds(1);
|
issuedAt = Instant.from(expiresAt).minusSeconds(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-11
@@ -72,16 +72,6 @@ public class JwtTests {
|
|||||||
new Jwt(null, Instant.ofEpochMilli(IAT_VALUE), Instant.ofEpochMilli(EXP_VALUE), HEADERS, CLAIMS);
|
new Jwt(null, Instant.ofEpochMilli(IAT_VALUE), Instant.ofEpochMilli(EXP_VALUE), HEADERS, CLAIMS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
|
||||||
public void constructorWhenIssuedAtIsNullThenThrowIllegalArgumentException() {
|
|
||||||
new Jwt(JWT_TOKEN_VALUE, null, Instant.ofEpochMilli(EXP_VALUE), HEADERS, CLAIMS);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
|
||||||
public void constructorWhenExpiresAtIsNullThenThrowIllegalArgumentException() {
|
|
||||||
new Jwt(JWT_TOKEN_VALUE, Instant.ofEpochMilli(IAT_VALUE), null, HEADERS, CLAIMS);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test(expected = IllegalArgumentException.class)
|
||||||
public void constructorWhenHeadersIsEmptyThenThrowIllegalArgumentException() {
|
public void constructorWhenHeadersIsEmptyThenThrowIllegalArgumentException() {
|
||||||
new Jwt(JWT_TOKEN_VALUE, Instant.ofEpochMilli(IAT_VALUE),
|
new Jwt(JWT_TOKEN_VALUE, Instant.ofEpochMilli(IAT_VALUE),
|
||||||
@@ -106,7 +96,7 @@ public class JwtTests {
|
|||||||
assertThat(jwt.getSubject()).isEqualTo(SUB_VALUE);
|
assertThat(jwt.getSubject()).isEqualTo(SUB_VALUE);
|
||||||
assertThat(jwt.getAudience()).isEqualTo(AUD_VALUE);
|
assertThat(jwt.getAudience()).isEqualTo(AUD_VALUE);
|
||||||
assertThat(jwt.getExpiresAt().toEpochMilli()).isEqualTo(EXP_VALUE);
|
assertThat(jwt.getExpiresAt().toEpochMilli()).isEqualTo(EXP_VALUE);
|
||||||
assertThat(jwt.getNotBefore().toEpochMilli()).isEqualTo(NBF_VALUE);
|
assertThat(jwt.getNotBefore().getEpochSecond()).isEqualTo(NBF_VALUE);
|
||||||
assertThat(jwt.getIssuedAt().toEpochMilli()).isEqualTo(IAT_VALUE);
|
assertThat(jwt.getIssuedAt().toEpochMilli()).isEqualTo(IAT_VALUE);
|
||||||
assertThat(jwt.getId()).isEqualTo(JTI_VALUE);
|
assertThat(jwt.getId()).isEqualTo(JTI_VALUE);
|
||||||
}
|
}
|
||||||
|
|||||||
+49
-8
@@ -15,36 +15,77 @@
|
|||||||
*/
|
*/
|
||||||
package org.springframework.security.oauth2.jwt;
|
package org.springframework.security.oauth2.jwt;
|
||||||
|
|
||||||
|
import com.nimbusds.jose.JWSAlgorithm;
|
||||||
|
import com.nimbusds.jose.JWSHeader;
|
||||||
|
import com.nimbusds.jwt.JWT;
|
||||||
|
import com.nimbusds.jwt.JWTClaimsSet;
|
||||||
|
import com.nimbusds.jwt.JWTParser;
|
||||||
|
import com.nimbusds.jwt.proc.DefaultJWTProcessor;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.powermock.core.classloader.annotations.PrepareForTest;
|
||||||
|
import org.powermock.modules.junit4.PowerMockRunner;
|
||||||
import org.springframework.security.oauth2.jose.jws.JwsAlgorithms;
|
import org.springframework.security.oauth2.jose.jws.JwsAlgorithms;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.AssertionsForClassTypes.assertThatCode;
|
||||||
|
import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
|
||||||
|
import static org.mockito.ArgumentMatchers.*;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.powermock.api.mockito.PowerMockito.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link NimbusJwtDecoderJwkSupport}.
|
* Tests for {@link NimbusJwtDecoderJwkSupport}.
|
||||||
*
|
*
|
||||||
* @author Joe Grandja
|
* @author Joe Grandja
|
||||||
*/
|
*/
|
||||||
|
@RunWith(PowerMockRunner.class)
|
||||||
|
@PrepareForTest({NimbusJwtDecoderJwkSupport.class, JWTParser.class})
|
||||||
public class NimbusJwtDecoderJwkSupportTests {
|
public class NimbusJwtDecoderJwkSupportTests {
|
||||||
private static final String JWK_SET_URL = "https://provider.com/oauth2/keys";
|
private static final String JWK_SET_URL = "https://provider.com/oauth2/keys";
|
||||||
private static final String JWS_ALGORITHM = JwsAlgorithms.RS256;
|
private static final String JWS_ALGORITHM = JwsAlgorithms.RS256;
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test
|
||||||
public void constructorWhenJwkSetUrlIsNullThenThrowIllegalArgumentException() {
|
public void constructorWhenJwkSetUrlIsNullThenThrowIllegalArgumentException() {
|
||||||
new NimbusJwtDecoderJwkSupport(null);
|
assertThatThrownBy(() -> new NimbusJwtDecoderJwkSupport(null))
|
||||||
|
.isInstanceOf(IllegalArgumentException.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test
|
||||||
public void constructorWhenJwkSetUrlInvalidThenThrowIllegalArgumentException() {
|
public void constructorWhenJwkSetUrlInvalidThenThrowIllegalArgumentException() {
|
||||||
new NimbusJwtDecoderJwkSupport("invalid.com");
|
assertThatThrownBy(() -> new NimbusJwtDecoderJwkSupport("invalid.com"))
|
||||||
|
.isInstanceOf(IllegalArgumentException.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test
|
||||||
public void constructorWhenJwsAlgorithmIsNullThenThrowIllegalArgumentException() {
|
public void constructorWhenJwsAlgorithmIsNullThenThrowIllegalArgumentException() {
|
||||||
new NimbusJwtDecoderJwkSupport(JWK_SET_URL, null);
|
assertThatThrownBy(() -> new NimbusJwtDecoderJwkSupport(JWK_SET_URL, null))
|
||||||
|
.isInstanceOf(IllegalArgumentException.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = JwtException.class)
|
@Test
|
||||||
public void decodeWhenJwtInvalidThenThrowJwtException() {
|
public void decodeWhenJwtInvalidThenThrowJwtException() {
|
||||||
NimbusJwtDecoderJwkSupport jwtDecoder = new NimbusJwtDecoderJwkSupport(JWK_SET_URL, JWS_ALGORITHM);
|
NimbusJwtDecoderJwkSupport jwtDecoder = new NimbusJwtDecoderJwkSupport(JWK_SET_URL, JWS_ALGORITHM);
|
||||||
jwtDecoder.decode("invalid");
|
assertThatThrownBy(() -> jwtDecoder.decode("invalid"))
|
||||||
|
.isInstanceOf(JwtException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
// gh-5168
|
||||||
|
@Test
|
||||||
|
public void decodeWhenExpClaimNullThenDoesNotThrowException() throws Exception {
|
||||||
|
JWT jwt = mock(JWT.class);
|
||||||
|
JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.parse(JWS_ALGORITHM)).build();
|
||||||
|
when(jwt.getHeader()).thenReturn(header);
|
||||||
|
|
||||||
|
mockStatic(JWTParser.class);
|
||||||
|
when(JWTParser.parse(anyString())).thenReturn(jwt);
|
||||||
|
|
||||||
|
DefaultJWTProcessor jwtProcessor = mock(DefaultJWTProcessor.class);
|
||||||
|
whenNew(DefaultJWTProcessor.class).withAnyArguments().thenReturn(jwtProcessor);
|
||||||
|
|
||||||
|
JWTClaimsSet jwtClaimsSet = new JWTClaimsSet.Builder().audience("resource1").build();
|
||||||
|
when(jwtProcessor.process(any(JWT.class), eq(null))).thenReturn(jwtClaimsSet);
|
||||||
|
|
||||||
|
NimbusJwtDecoderJwkSupport jwtDecoder = new NimbusJwtDecoderJwkSupport(JWK_SET_URL, JWS_ALGORITHM);
|
||||||
|
assertThatCode(() -> jwtDecoder.decode("encoded-jwt")).doesNotThrowAnyException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ dependencies {
|
|||||||
compile project(':spring-security-web')
|
compile project(':spring-security-web')
|
||||||
compile 'javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api'
|
compile 'javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api'
|
||||||
compile 'javax.validation:validation-api'
|
compile 'javax.validation:validation-api'
|
||||||
|
compile 'javax.xml.bind:jaxb-api'
|
||||||
compile 'org.hibernate:hibernate-validator'
|
compile 'org.hibernate:hibernate-validator'
|
||||||
compile 'org.springframework:spring-jdbc'
|
compile 'org.springframework:spring-jdbc'
|
||||||
compile 'org.springframework:spring-webmvc'
|
compile 'org.springframework:spring-webmvc'
|
||||||
|
|||||||
+4
-17
@@ -18,25 +18,19 @@ package org.springframework.security.samples.config;
|
|||||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.*;
|
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.*;
|
||||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*;
|
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*;
|
||||||
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.*;
|
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.*;
|
||||||
|
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
||||||
|
|
||||||
import javax.servlet.Filter;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.security.samples.mvc.config.WebMvcConfiguration;
|
import org.springframework.security.samples.mvc.config.WebMvcConfiguration;
|
||||||
import org.springframework.security.test.context.support.WithMockUser;
|
import org.springframework.security.test.context.support.WithMockUser;
|
||||||
import org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener;
|
|
||||||
import org.springframework.test.context.ContextConfiguration;
|
import org.springframework.test.context.ContextConfiguration;
|
||||||
import org.springframework.test.context.TestExecutionListeners;
|
|
||||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||||
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
|
|
||||||
import org.springframework.test.context.support.DirtiesContextTestExecutionListener;
|
|
||||||
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
|
|
||||||
import org.springframework.test.context.web.ServletTestExecutionListener;
|
|
||||||
import org.springframework.test.context.web.WebAppConfiguration;
|
import org.springframework.test.context.web.WebAppConfiguration;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
|
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
|
||||||
@@ -50,25 +44,18 @@ import org.springframework.web.context.WebApplicationContext;
|
|||||||
@RunWith(SpringJUnit4ClassRunner.class)
|
@RunWith(SpringJUnit4ClassRunner.class)
|
||||||
@ContextConfiguration(classes = { RootConfiguration.class, WebMvcConfiguration.class })
|
@ContextConfiguration(classes = { RootConfiguration.class, WebMvcConfiguration.class })
|
||||||
@WebAppConfiguration
|
@WebAppConfiguration
|
||||||
@TestExecutionListeners(listeners = { ServletTestExecutionListener.class,
|
|
||||||
DependencyInjectionTestExecutionListener.class,
|
|
||||||
DirtiesContextTestExecutionListener.class,
|
|
||||||
TransactionalTestExecutionListener.class,
|
|
||||||
WithSecurityContextTestExecutionListener.class })
|
|
||||||
public class SecurityConfigTests {
|
public class SecurityConfigTests {
|
||||||
private MockMvc mvc;
|
private MockMvc mvc;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private WebApplicationContext context;
|
private WebApplicationContext context;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private Filter springSecurityFilterChain;
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setup() {
|
public void setup() {
|
||||||
mvc = MockMvcBuilders.webAppContextSetup(context)
|
mvc = MockMvcBuilders.webAppContextSetup(context)
|
||||||
.addFilters(springSecurityFilterChain)
|
.apply(springSecurity())
|
||||||
.defaultRequest(get("/").with(testSecurityContext())).build();
|
.defaultRequest(get("/").accept(MediaType.TEXT_HTML)).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ dependencies {
|
|||||||
compile project(':spring-security-web')
|
compile project(':spring-security-web')
|
||||||
compile 'javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api'
|
compile 'javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api'
|
||||||
compile 'javax.validation:validation-api'
|
compile 'javax.validation:validation-api'
|
||||||
|
compile 'javax.xml.bind:jaxb-api'
|
||||||
compile 'org.hibernate:hibernate-validator'
|
compile 'org.hibernate:hibernate-validator'
|
||||||
compile 'org.springframework:spring-jdbc'
|
compile 'org.springframework:spring-jdbc'
|
||||||
compile 'org.springframework:spring-webmvc'
|
compile 'org.springframework:spring-webmvc'
|
||||||
|
|||||||
+7
-10
@@ -16,17 +16,16 @@
|
|||||||
package org.springframework.security.samples.config;
|
package org.springframework.security.samples.config;
|
||||||
|
|
||||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.*;
|
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.*;
|
||||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*;
|
|
||||||
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.*;
|
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.*;
|
||||||
|
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
||||||
|
|
||||||
import javax.servlet.Filter;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.security.samples.mvc.config.WebMvcConfiguration;
|
import org.springframework.security.samples.mvc.config.WebMvcConfiguration;
|
||||||
import org.springframework.security.test.context.support.WithMockUser;
|
import org.springframework.security.test.context.support.WithMockUser;
|
||||||
import org.springframework.test.context.ContextConfiguration;
|
import org.springframework.test.context.ContextConfiguration;
|
||||||
@@ -49,14 +48,11 @@ public class SecurityConfigTests {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private WebApplicationContext context;
|
private WebApplicationContext context;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private Filter springSecurityFilterChain;
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setup() {
|
public void setup() {
|
||||||
mvc = MockMvcBuilders.webAppContextSetup(context)
|
mvc = MockMvcBuilders.webAppContextSetup(context)
|
||||||
.addFilters(springSecurityFilterChain)
|
.apply(springSecurity())
|
||||||
.defaultRequest(get("/").with(testSecurityContext())).build();
|
.defaultRequest(get("/").accept(MediaType.TEXT_HTML)).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -84,7 +80,8 @@ public class SecurityConfigTests {
|
|||||||
@Test
|
@Test
|
||||||
@WithMockUser
|
@WithMockUser
|
||||||
public void logoutSuccess() throws Exception {
|
public void logoutSuccess() throws Exception {
|
||||||
mvc.perform(logout()).andExpect(redirectedUrl("/login?logout"))
|
mvc.perform(logout())
|
||||||
|
.andExpect(redirectedUrl("/login?logout"))
|
||||||
.andExpect(unauthenticated());
|
.andExpect(unauthenticated());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ dependencies {
|
|||||||
compile project(':spring-security-web')
|
compile project(':spring-security-web')
|
||||||
compile 'javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api'
|
compile 'javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api'
|
||||||
compile 'javax.validation:validation-api'
|
compile 'javax.validation:validation-api'
|
||||||
|
compile 'javax.xml.bind:jaxb-api'
|
||||||
compile 'org.hibernate:hibernate-validator'
|
compile 'org.hibernate:hibernate-validator'
|
||||||
compile 'org.springframework:spring-webmvc'
|
compile 'org.springframework:spring-webmvc'
|
||||||
compile apachedsDependencies
|
compile apachedsDependencies
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ dependencies {
|
|||||||
compile project(':spring-security-web')
|
compile project(':spring-security-web')
|
||||||
compile 'javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api'
|
compile 'javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api'
|
||||||
compile 'javax.validation:validation-api'
|
compile 'javax.validation:validation-api'
|
||||||
|
compile 'javax.xml.bind:jaxb-api'
|
||||||
compile 'org.hibernate:hibernate-validator'
|
compile 'org.hibernate:hibernate-validator'
|
||||||
compile 'org.springframework:spring-jdbc'
|
compile 'org.springframework:spring-jdbc'
|
||||||
compile 'org.springframework:spring-webmvc'
|
compile 'org.springframework:spring-webmvc'
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ apply plugin: 'io.spring.convention.spring-sample-war'
|
|||||||
dependencies {
|
dependencies {
|
||||||
compile jstlDependencies
|
compile jstlDependencies
|
||||||
compile slf4jDependencies
|
compile slf4jDependencies
|
||||||
|
compile 'javax.xml.bind:jaxb-api'
|
||||||
|
|
||||||
providedCompile 'javax.servlet:javax.servlet-api'
|
providedCompile 'javax.servlet:javax.servlet-api'
|
||||||
providedCompile 'javax.servlet.jsp:javax.servlet.jsp-api'
|
providedCompile 'javax.servlet.jsp:javax.servlet.jsp-api'
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ dependencies {
|
|||||||
compile slf4jDependencies
|
compile slf4jDependencies
|
||||||
compile 'javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api'
|
compile 'javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api'
|
||||||
compile 'javax.validation:validation-api'
|
compile 'javax.validation:validation-api'
|
||||||
|
compile 'javax.xml.bind:jaxb-api'
|
||||||
compile 'org.hibernate:hibernate-validator'
|
compile 'org.hibernate:hibernate-validator'
|
||||||
compile 'org.springframework:spring-jdbc'
|
compile 'org.springframework:spring-jdbc'
|
||||||
compile 'org.springframework:spring-webmvc'
|
compile 'org.springframework:spring-webmvc'
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ dependencies {
|
|||||||
|
|
||||||
testCompile 'com.fasterxml.jackson.core:jackson-databind'
|
testCompile 'com.fasterxml.jackson.core:jackson-databind'
|
||||||
testCompile 'io.projectreactor:reactor-test'
|
testCompile 'io.projectreactor:reactor-test'
|
||||||
|
testCompile 'javax.xml.bind:jaxb-api'
|
||||||
testCompile 'org.skyscreamer:jsonassert'
|
testCompile 'org.skyscreamer:jsonassert'
|
||||||
testCompile 'org.springframework:spring-webmvc'
|
testCompile 'org.springframework:spring-webmvc'
|
||||||
testCompile 'org.springframework:spring-tx'
|
testCompile 'org.springframework:spring-tx'
|
||||||
|
|||||||
+10
-4
@@ -19,6 +19,7 @@ package org.springframework.security.test.context.support;
|
|||||||
import org.reactivestreams.Subscription;
|
import org.reactivestreams.Subscription;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
|
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
|
||||||
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
import org.springframework.security.test.context.TestSecurityContextHolder;
|
import org.springframework.security.test.context.TestSecurityContextHolder;
|
||||||
import org.springframework.test.context.TestContext;
|
import org.springframework.test.context.TestContext;
|
||||||
import org.springframework.test.context.TestExecutionListener;
|
import org.springframework.test.context.TestExecutionListener;
|
||||||
@@ -26,6 +27,7 @@ import org.springframework.test.context.support.AbstractTestExecutionListener;
|
|||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
import reactor.core.CoreSubscriber;
|
import reactor.core.CoreSubscriber;
|
||||||
import reactor.core.publisher.Hooks;
|
import reactor.core.publisher.Hooks;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
import reactor.core.publisher.Operators;
|
import reactor.core.publisher.Operators;
|
||||||
import reactor.util.context.Context;
|
import reactor.util.context.Context;
|
||||||
|
|
||||||
@@ -54,7 +56,8 @@ public class ReactorContextTestExecutionListener
|
|||||||
private static class DelegateTestExecutionListener extends AbstractTestExecutionListener {
|
private static class DelegateTestExecutionListener extends AbstractTestExecutionListener {
|
||||||
@Override
|
@Override
|
||||||
public void beforeTestMethod(TestContext testContext) throws Exception {
|
public void beforeTestMethod(TestContext testContext) throws Exception {
|
||||||
Hooks.onLastOperator(Operators.lift((s, sub) -> new SecuritySubContext<>(sub)));
|
SecurityContext securityContext = TestSecurityContextHolder.getContext();
|
||||||
|
Hooks.onLastOperator(Operators.lift((s, sub) -> new SecuritySubContext<>(sub, securityContext)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -66,9 +69,11 @@ public class ReactorContextTestExecutionListener
|
|||||||
private static String CONTEXT_DEFAULTED_ATTR_NAME = SecuritySubContext.class.getName().concat(".CONTEXT_DEFAULTED_ATTR_NAME");
|
private static String CONTEXT_DEFAULTED_ATTR_NAME = SecuritySubContext.class.getName().concat(".CONTEXT_DEFAULTED_ATTR_NAME");
|
||||||
|
|
||||||
private final CoreSubscriber<T> delegate;
|
private final CoreSubscriber<T> delegate;
|
||||||
|
private final SecurityContext securityContext;
|
||||||
|
|
||||||
SecuritySubContext(CoreSubscriber<T> delegate) {
|
SecuritySubContext(CoreSubscriber<T> delegate, SecurityContext securityContext) {
|
||||||
this.delegate = delegate;
|
this.delegate = delegate;
|
||||||
|
this.securityContext = securityContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -78,11 +83,12 @@ public class ReactorContextTestExecutionListener
|
|||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
context = context.put(CONTEXT_DEFAULTED_ATTR_NAME, Boolean.TRUE);
|
context = context.put(CONTEXT_DEFAULTED_ATTR_NAME, Boolean.TRUE);
|
||||||
Authentication authentication = TestSecurityContextHolder.getContext().getAuthentication();
|
Authentication authentication = securityContext.getAuthentication();
|
||||||
if (authentication == null) {
|
if (authentication == null) {
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
Context toMerge = ReactiveSecurityContextHolder.withAuthentication(authentication);
|
Context toMerge = ReactiveSecurityContextHolder.withSecurityContext(
|
||||||
|
Mono.just(this.securityContext));
|
||||||
return toMerge.putAll(context);
|
return toMerge.putAll(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
@@ -90,6 +90,7 @@ public final class SecurityMockMvcRequestBuilders {
|
|||||||
@Override
|
@Override
|
||||||
public MockHttpServletRequest buildRequest(ServletContext servletContext) {
|
public MockHttpServletRequest buildRequest(ServletContext servletContext) {
|
||||||
MockHttpServletRequest request = post(this.logoutUrl)
|
MockHttpServletRequest request = post(this.logoutUrl)
|
||||||
|
.accept(MediaType.TEXT_HTML, MediaType.ALL)
|
||||||
.buildRequest(servletContext);
|
.buildRequest(servletContext);
|
||||||
return this.postProcessor.postProcessRequest(request);
|
return this.postProcessor.postProcessRequest(request);
|
||||||
}
|
}
|
||||||
|
|||||||
+52
@@ -21,6 +21,8 @@ package org.springframework.security.test.context.support;
|
|||||||
* @since 5.0
|
* @since 5.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import java.util.concurrent.ForkJoinPool;
|
||||||
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
@@ -92,6 +94,35 @@ public class ReactorContextTestExecutionListenerTests {
|
|||||||
assertAuthentication(expectedAuthentication);
|
assertAuthentication(expectedAuthentication);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void beforeTestMethodWhenCustomContext() throws Exception {
|
||||||
|
TestingAuthenticationToken expectedAuthentication = new TestingAuthenticationToken("user", "password", "ROLE_USER");
|
||||||
|
SecurityContext context = new CustomContext(expectedAuthentication);
|
||||||
|
TestSecurityContextHolder.setContext(context);
|
||||||
|
|
||||||
|
this.listener.beforeTestMethod(this.testContext);
|
||||||
|
|
||||||
|
assertSecurityContext(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
static class CustomContext implements SecurityContext {
|
||||||
|
private Authentication authentication;
|
||||||
|
|
||||||
|
CustomContext(Authentication authentication) {
|
||||||
|
this.authentication = authentication;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Authentication getAuthentication() {
|
||||||
|
return this.authentication;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAuthentication(Authentication authentication) {
|
||||||
|
this.authentication = authentication;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void beforeTestMethodWhenExistingAuthenticationThenReactorContextHasOriginalAuthentication() throws Exception {
|
public void beforeTestMethodWhenExistingAuthenticationThenReactorContextHasOriginalAuthentication() throws Exception {
|
||||||
TestingAuthenticationToken expectedAuthentication = new TestingAuthenticationToken("user", "password", "ROLE_USER");
|
TestingAuthenticationToken expectedAuthentication = new TestingAuthenticationToken("user", "password", "ROLE_USER");
|
||||||
@@ -153,6 +184,18 @@ public class ReactorContextTestExecutionListenerTests {
|
|||||||
assertThat(comparator.compare(withSecurity, reactorContext)).isLessThan(0);
|
assertThat(comparator.compare(withSecurity, reactorContext)).isLessThan(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void checkSecurityContextResolutionWhenSubscribedContextCalledOnTheDifferentThreadThanWithSecurityContextTestExecutionListener() throws Exception {
|
||||||
|
TestingAuthenticationToken contextHolder = new TestingAuthenticationToken("contextHolder", "password", "ROLE_USER");
|
||||||
|
TestSecurityContextHolder.setContext(new SecurityContextImpl(contextHolder));
|
||||||
|
|
||||||
|
this.listener.beforeTestMethod(this.testContext);
|
||||||
|
|
||||||
|
ForkJoinPool.commonPool()
|
||||||
|
.submit(() -> assertAuthentication(contextHolder))
|
||||||
|
.join();
|
||||||
|
}
|
||||||
|
|
||||||
public void assertAuthentication(Authentication expected) {
|
public void assertAuthentication(Authentication expected) {
|
||||||
Mono<Authentication> authentication = ReactiveSecurityContextHolder.getContext()
|
Mono<Authentication> authentication = ReactiveSecurityContextHolder.getContext()
|
||||||
.map(SecurityContext::getAuthentication);
|
.map(SecurityContext::getAuthentication);
|
||||||
@@ -161,4 +204,13 @@ public class ReactorContextTestExecutionListenerTests {
|
|||||||
.expectNext(expected)
|
.expectNext(expected)
|
||||||
.verifyComplete();
|
.verifyComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void assertSecurityContext(SecurityContext expected) {
|
||||||
|
Mono<SecurityContext> securityContext = ReactiveSecurityContextHolder.getContext();
|
||||||
|
|
||||||
|
StepVerifier.create(securityContext)
|
||||||
|
.expectNext(expected)
|
||||||
|
.verifyComplete();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -101,7 +101,7 @@ public class WithSecurityContextTestExcecutionListenerTests {
|
|||||||
SqlScriptsTestExecutionListener sql = new SqlScriptsTestExecutionListener();
|
SqlScriptsTestExecutionListener sql = new SqlScriptsTestExecutionListener();
|
||||||
WithSecurityContextTestExecutionListener security = new WithSecurityContextTestExecutionListener();
|
WithSecurityContextTestExecutionListener security = new WithSecurityContextTestExecutionListener();
|
||||||
|
|
||||||
List<? extends TestExecutionListener> listeners = Arrays.asList(security, sql);
|
List<TestExecutionListener> listeners = Arrays.asList(security, sql);
|
||||||
|
|
||||||
AnnotationAwareOrderComparator.sort(listeners);
|
AnnotationAwareOrderComparator.sort(listeners);
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -52,7 +52,7 @@ abstract class AbstractMockServerConfigurersTests {
|
|||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
protected static class PrincipalController {
|
protected static class PrincipalController {
|
||||||
Principal principal;
|
volatile Principal principal;
|
||||||
|
|
||||||
@RequestMapping("/**")
|
@RequestMapping("/**")
|
||||||
public Principal get(Principal principal) {
|
public Principal get(Principal principal) {
|
||||||
|
|||||||
+52
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package org.springframework.security.test.web.reactive.server;
|
package org.springframework.security.test.web.reactive.server;
|
||||||
|
|
||||||
|
import java.util.concurrent.ForkJoinPool;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
@@ -114,4 +116,54 @@ public class SecurityMockServerConfigurersAnnotatedTests extends AbstractMockSer
|
|||||||
|
|
||||||
assertPrincipalCreatedFromUserDetails(controller.removePrincipal(), userBuilder.build());
|
assertPrincipalCreatedFromUserDetails(controller.removePrincipal(), userBuilder.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockUser
|
||||||
|
public void withMockUserWhenOnMethodAndRequestIsExecutedOnDifferentThreadThenSuccess() {
|
||||||
|
Authentication authentication = TestSecurityContextHolder.getContext().getAuthentication();
|
||||||
|
ForkJoinPool
|
||||||
|
.commonPool()
|
||||||
|
.submit(() ->
|
||||||
|
client
|
||||||
|
.get()
|
||||||
|
.exchange()
|
||||||
|
.expectStatus()
|
||||||
|
.isOk()
|
||||||
|
)
|
||||||
|
.join();
|
||||||
|
|
||||||
|
controller.assertPrincipalIsEqualTo(authentication);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockUser
|
||||||
|
public void withMockUserAndWithCallOnSeparateThreadWhenMutateWithMockPrincipalAndNoMutateThenOverridesAnnotationAndUsesAnnotation() {
|
||||||
|
TestingAuthenticationToken authentication = new TestingAuthenticationToken("authentication", "secret", "ROLE_USER");
|
||||||
|
|
||||||
|
ForkJoinPool
|
||||||
|
.commonPool()
|
||||||
|
.submit(() ->
|
||||||
|
client
|
||||||
|
.mutateWith(mockAuthentication(authentication))
|
||||||
|
.get()
|
||||||
|
.exchange()
|
||||||
|
.expectStatus().isOk()
|
||||||
|
)
|
||||||
|
.join();
|
||||||
|
|
||||||
|
controller.assertPrincipalIsEqualTo(authentication);
|
||||||
|
|
||||||
|
|
||||||
|
ForkJoinPool
|
||||||
|
.commonPool()
|
||||||
|
.submit(() ->
|
||||||
|
client
|
||||||
|
.get()
|
||||||
|
.exchange()
|
||||||
|
.expectStatus().isOk()
|
||||||
|
)
|
||||||
|
.join();
|
||||||
|
|
||||||
|
assertPrincipalCreatedFromUserDetails(controller.removePrincipal(), userBuilder.build());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-5
@@ -24,6 +24,7 @@ import static org.powermock.api.mockito.PowerMockito.when;
|
|||||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
|
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
@@ -114,8 +115,8 @@ public class SecurityMockMvcRequestPostProcessorsUserTests {
|
|||||||
verify(repository).saveContext(contextCaptor.capture(), eq(request),
|
verify(repository).saveContext(contextCaptor.capture(), eq(request),
|
||||||
any(HttpServletResponse.class));
|
any(HttpServletResponse.class));
|
||||||
SecurityContext context = contextCaptor.getValue();
|
SecurityContext context = contextCaptor.getValue();
|
||||||
assertThat(context.getAuthentication().getAuthorities()).containsOnly(authority1,
|
assertThat((List<GrantedAuthority>) context.getAuthentication().getAuthorities())
|
||||||
authority2);
|
.containsOnly(authority1, authority2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test(expected = IllegalArgumentException.class)
|
||||||
@@ -133,12 +134,12 @@ public class SecurityMockMvcRequestPostProcessorsUserTests {
|
|||||||
verify(repository).saveContext(contextCaptor.capture(), eq(request),
|
verify(repository).saveContext(contextCaptor.capture(), eq(request),
|
||||||
any(HttpServletResponse.class));
|
any(HttpServletResponse.class));
|
||||||
SecurityContext context = contextCaptor.getValue();
|
SecurityContext context = contextCaptor.getValue();
|
||||||
assertThat(context.getAuthentication().getAuthorities()).containsOnly(authority1,
|
assertThat((List<GrantedAuthority>) context.getAuthentication().getAuthorities())
|
||||||
authority2);
|
.containsOnly(authority1, authority2);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void mockWebTestUtils() {
|
private void mockWebTestUtils() {
|
||||||
spy(WebTestUtils.class);
|
spy(WebTestUtils.class);
|
||||||
when(WebTestUtils.getSecurityContextRepository(request)).thenReturn(repository);
|
when(WebTestUtils.getSecurityContextRepository(request)).thenReturn(repository);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-1
@@ -27,6 +27,7 @@ import org.junit.Test;
|
|||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
import org.springframework.security.core.userdetails.User;
|
import org.springframework.security.core.userdetails.User;
|
||||||
@@ -53,7 +54,10 @@ public class AuthenticationTests {
|
|||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setup() {
|
public void setup() {
|
||||||
mvc = MockMvcBuilders.webAppContextSetup(context).apply(springSecurity()).build();
|
mvc = MockMvcBuilders.webAppContextSetup(context)
|
||||||
|
.apply(springSecurity())
|
||||||
|
.defaultRequest(get("/").accept(MediaType.TEXT_HTML))
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -21,12 +21,12 @@ dependencies {
|
|||||||
testCompile project(path: ':spring-security-core', configuration: 'tests')
|
testCompile project(path: ':spring-security-core', configuration: 'tests')
|
||||||
testCompile 'commons-codec:commons-codec'
|
testCompile 'commons-codec:commons-codec'
|
||||||
testCompile 'io.projectreactor:reactor-test'
|
testCompile 'io.projectreactor:reactor-test'
|
||||||
|
testCompile 'javax.xml.bind:jaxb-api'
|
||||||
testCompile 'org.codehaus.groovy:groovy-all'
|
testCompile 'org.codehaus.groovy:groovy-all'
|
||||||
testCompile 'org.skyscreamer:jsonassert'
|
testCompile 'org.skyscreamer:jsonassert'
|
||||||
testCompile 'org.springframework:spring-webflux'
|
testCompile 'org.springframework:spring-webflux'
|
||||||
testCompile powerMock2Dependencies
|
testCompile powerMock2Dependencies
|
||||||
testCompile spockDependencies
|
testCompile spockDependencies
|
||||||
testCompile slf4jDependencies
|
|
||||||
|
|
||||||
testRuntime 'org.hsqldb:hsqldb'
|
testRuntime 'org.hsqldb:hsqldb'
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-5
@@ -22,7 +22,6 @@ import javax.servlet.http.HttpServletResponse;
|
|||||||
|
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A logout handler which clears a defined list of cookies, using the context path as the
|
* A logout handler which clears a defined list of cookies, using the context path as the
|
||||||
@@ -43,10 +42,7 @@ public final class CookieClearingLogoutHandler implements LogoutHandler {
|
|||||||
Authentication authentication) {
|
Authentication authentication) {
|
||||||
for (String cookieName : cookiesToClear) {
|
for (String cookieName : cookiesToClear) {
|
||||||
Cookie cookie = new Cookie(cookieName, null);
|
Cookie cookie = new Cookie(cookieName, null);
|
||||||
String cookiePath = request.getContextPath();
|
String cookiePath = request.getContextPath() + "/";
|
||||||
if (!StringUtils.hasLength(cookiePath)) {
|
|
||||||
cookiePath = "/";
|
|
||||||
}
|
|
||||||
cookie.setPath(cookiePath);
|
cookie.setPath(cookiePath);
|
||||||
cookie.setMaxAge(0);
|
cookie.setMaxAge(0);
|
||||||
response.addCookie(cookie);
|
response.addCookie(cookie);
|
||||||
|
|||||||
+14
-5
@@ -25,7 +25,6 @@ import java.util.List;
|
|||||||
import javax.naming.Context;
|
import javax.naming.Context;
|
||||||
import javax.naming.InitialContext;
|
import javax.naming.InitialContext;
|
||||||
import javax.naming.NamingException;
|
import javax.naming.NamingException;
|
||||||
import javax.rmi.PortableRemoteObject;
|
|
||||||
import javax.security.auth.Subject;
|
import javax.security.auth.Subject;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
@@ -44,6 +43,8 @@ final class DefaultWASUsernameAndGroupsExtractor implements WASUsernameAndGroups
|
|||||||
private static final Log logger = LogFactory
|
private static final Log logger = LogFactory
|
||||||
.getLog(DefaultWASUsernameAndGroupsExtractor.class);
|
.getLog(DefaultWASUsernameAndGroupsExtractor.class);
|
||||||
|
|
||||||
|
private static final String PORTABLE_REMOTE_OBJECT_CLASSNAME = "javax.rmi.PortableRemoteObject";
|
||||||
|
|
||||||
private static final String USER_REGISTRY = "UserRegistry";
|
private static final String USER_REGISTRY = "UserRegistry";
|
||||||
|
|
||||||
private static Method getRunAsSubject = null;
|
private static Method getRunAsSubject = null;
|
||||||
@@ -52,6 +53,8 @@ final class DefaultWASUsernameAndGroupsExtractor implements WASUsernameAndGroups
|
|||||||
|
|
||||||
private static Method getSecurityName = null;
|
private static Method getSecurityName = null;
|
||||||
|
|
||||||
|
private static Method narrow = null;
|
||||||
|
|
||||||
// SEC-803
|
// SEC-803
|
||||||
private static Class<?> wsCredentialClass = null;
|
private static Class<?> wsCredentialClass = null;
|
||||||
|
|
||||||
@@ -80,7 +83,7 @@ final class DefaultWASUsernameAndGroupsExtractor implements WASUsernameAndGroups
|
|||||||
.iterator().next();
|
.iterator().next();
|
||||||
if (credential != null) {
|
if (credential != null) {
|
||||||
userSecurityName = (String) invokeMethod(getSecurityNameMethod(),
|
userSecurityName = (String) invokeMethod(getSecurityNameMethod(),
|
||||||
credential, null);
|
credential);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
@@ -125,8 +128,7 @@ final class DefaultWASUsernameAndGroupsExtractor implements WASUsernameAndGroups
|
|||||||
// TODO: Cache UserRegistry object
|
// TODO: Cache UserRegistry object
|
||||||
ic = new InitialContext();
|
ic = new InitialContext();
|
||||||
Object objRef = ic.lookup(USER_REGISTRY);
|
Object objRef = ic.lookup(USER_REGISTRY);
|
||||||
Object userReg = PortableRemoteObject.narrow(objRef,
|
Object userReg = invokeMethod(getNarrowMethod(), null , objRef, Class.forName("com.ibm.websphere.security.UserRegistry"));
|
||||||
Class.forName("com.ibm.websphere.security.UserRegistry"));
|
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("Determining WebSphere groups for user " + securityName
|
logger.debug("Determining WebSphere groups for user " + securityName
|
||||||
+ " using WebSphere UserRegistry " + userReg);
|
+ " using WebSphere UserRegistry " + userReg);
|
||||||
@@ -156,7 +158,7 @@ final class DefaultWASUsernameAndGroupsExtractor implements WASUsernameAndGroups
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Object invokeMethod(Method method, Object instance, Object[] args) {
|
private static Object invokeMethod(Method method, Object instance, Object... args) {
|
||||||
try {
|
try {
|
||||||
return method.invoke(instance, args);
|
return method.invoke(instance, args);
|
||||||
}
|
}
|
||||||
@@ -230,6 +232,13 @@ final class DefaultWASUsernameAndGroupsExtractor implements WASUsernameAndGroups
|
|||||||
return getSecurityName;
|
return getSecurityName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Method getNarrowMethod() {
|
||||||
|
if (narrow == null) {
|
||||||
|
narrow = getMethod(PORTABLE_REMOTE_OBJECT_CLASSNAME, "narrow", new String[] { Object.class.getName() , Class.class.getName()});
|
||||||
|
}
|
||||||
|
return narrow;
|
||||||
|
}
|
||||||
|
|
||||||
// SEC-803
|
// SEC-803
|
||||||
private static Class<?> getWSCredentialClass() {
|
private static Class<?> getWSCredentialClass() {
|
||||||
if (wsCredentialClass == null) {
|
if (wsCredentialClass == null) {
|
||||||
|
|||||||
+1
@@ -37,6 +37,7 @@ import org.springframework.security.core.Authentication;
|
|||||||
@Target({ ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
|
@Target({ ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Documented
|
@Documented
|
||||||
|
@Deprecated
|
||||||
public @interface AuthenticationPrincipal {
|
public @interface AuthenticationPrincipal {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -340,7 +340,7 @@ public class StrictHttpFirewall implements HttpFirewall {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (path.indexOf("//") > 0) {
|
if (path.indexOf("//") > -1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+6
@@ -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;
|
||||||
|
|||||||
+3
@@ -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
|
||||||
*/
|
*/
|
||||||
|
|||||||
+3
@@ -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
|
||||||
*/
|
*/
|
||||||
|
|||||||
+13
-1
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
+9
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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
|
||||||
*/
|
*/
|
||||||
|
|||||||
+52
@@ -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");
|
||||||
|
|||||||
+1
@@ -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
|
||||||
|
|||||||
+8
@@ -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;
|
||||||
|
|||||||
+4
@@ -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);
|
||||||
|
|||||||
+14
@@ -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;
|
||||||
|
|||||||
+1
@@ -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
|
||||||
*/
|
*/
|
||||||
|
|||||||
+8
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
+7
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
+2
@@ -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
|
||||||
*/
|
*/
|
||||||
|
|||||||
+1
@@ -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
|
||||||
*/
|
*/
|
||||||
|
|||||||
+8
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
+8
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-2
@@ -30,13 +30,18 @@ import org.springframework.web.server.ServerWebExchange;
|
|||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets an HTTP Status that is provided when
|
* Sets the provided HTTP Status when access is denied.
|
||||||
|
*
|
||||||
* @author Rob Winch
|
* @author Rob Winch
|
||||||
* @since 5.0
|
* @since 5.0
|
||||||
*/
|
*/
|
||||||
public class HttpStatusServerAccessDeniedHandler implements ServerAccessDeniedHandler {
|
public class HttpStatusServerAccessDeniedHandler implements ServerAccessDeniedHandler {
|
||||||
private final HttpStatus httpStatus;
|
private final HttpStatus httpStatus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance with the provided status
|
||||||
|
* @param httpStatus the status to use
|
||||||
|
*/
|
||||||
public HttpStatusServerAccessDeniedHandler(HttpStatus httpStatus) {
|
public HttpStatusServerAccessDeniedHandler(HttpStatus httpStatus) {
|
||||||
Assert.notNull(httpStatus, "httpStatus cannot be null");
|
Assert.notNull(httpStatus, "httpStatus cannot be null");
|
||||||
this.httpStatus = httpStatus;
|
this.httpStatus = httpStatus;
|
||||||
@@ -46,7 +51,7 @@ public class HttpStatusServerAccessDeniedHandler implements ServerAccessDeniedHa
|
|||||||
public Mono<Void> handle(ServerWebExchange exchange, AccessDeniedException e) {
|
public Mono<Void> handle(ServerWebExchange exchange, AccessDeniedException e) {
|
||||||
return Mono.defer(() -> Mono.just(exchange.getResponse()))
|
return Mono.defer(() -> Mono.just(exchange.getResponse()))
|
||||||
.flatMap(response -> {
|
.flatMap(response -> {
|
||||||
response.setStatusCode(HttpStatus.FORBIDDEN);
|
response.setStatusCode(this.httpStatus);
|
||||||
response.getHeaders().setContentType(MediaType.TEXT_PLAIN);
|
response.getHeaders().setContentType(MediaType.TEXT_PLAIN);
|
||||||
DataBufferFactory dataBufferFactory = response.bufferFactory();
|
DataBufferFactory dataBufferFactory = response.bufferFactory();
|
||||||
DataBuffer buffer = dataBufferFactory.wrap(e.getMessage().getBytes(
|
DataBuffer buffer = dataBufferFactory.wrap(e.getMessage().getBytes(
|
||||||
|
|||||||
+1
@@ -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
|
||||||
*/
|
*/
|
||||||
|
|||||||
+3
@@ -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
|
||||||
*/
|
*/
|
||||||
|
|||||||
+2
@@ -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;
|
||||||
|
|||||||
+1
-5
@@ -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
|
||||||
|
|||||||
+17
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
+7
@@ -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
|
||||||
|
|||||||
+1
@@ -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
|
||||||
|
|||||||
+2
@@ -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
|
||||||
*/
|
*/
|
||||||
|
|||||||
+9
@@ -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();
|
||||||
|
|||||||
+1
@@ -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
|
||||||
*/
|
*/
|
||||||
|
|||||||
+3
-1
@@ -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;
|
||||||
|
|||||||
+1
@@ -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
|
||||||
*/
|
*/
|
||||||
|
|||||||
+2
-1
@@ -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) {
|
||||||
|
|||||||
+2
@@ -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
|
||||||
*/
|
*/
|
||||||
|
|||||||
+2
@@ -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;
|
||||||
|
|||||||
+10
@@ -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);
|
||||||
|
|||||||
+2
@@ -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;
|
||||||
|
|||||||
+1
@@ -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
|
||||||
*/
|
*/
|
||||||
|
|||||||
+27
-1
@@ -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()));
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user