Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e38bf6e554 | |||
| 624e0da669 | |||
| b28aa6c647 | |||
| 9e83b4be25 | |||
| e9854c91ef | |||
| 0345e29afb | |||
| 8bd5795f8e | |||
| 4fbc98dcd2 | |||
| 44cdbd6cf3 | |||
| a9af8c4128 | |||
| 28c81af788 | |||
| 3a956daf0c | |||
| 9c245865d7 | |||
| 86ed937a47 | |||
| 32ec8c3ae4 | |||
| c7ffd2513a | |||
| b3e0f167ff | |||
| 8c063f8ccb | |||
| f1ca42e501 | |||
| 719149f01d | |||
| ccd30607a9 | |||
| aa1ef46d84 | |||
| 16a21264d0 | |||
| 1c0f092176 | |||
| 32d79f3f4e | |||
| 088a24cf59 |
@@ -63,6 +63,7 @@ updateDependenciesSettings {
|
||||
})
|
||||
dependencyExcludes {
|
||||
majorVersionBump()
|
||||
minorVersionBump()
|
||||
alphaBetaVersions()
|
||||
releaseCandidatesVersions()
|
||||
milestoneVersions()
|
||||
|
||||
@@ -9,7 +9,6 @@ plugins {
|
||||
sourceCompatibility = 1.8
|
||||
|
||||
repositories {
|
||||
jcenter()
|
||||
gradlePluginPortal()
|
||||
mavenCentral()
|
||||
maven { url 'https://repo.spring.io/plugins-release/' }
|
||||
@@ -76,7 +75,7 @@ dependencies {
|
||||
implementation localGroovy()
|
||||
|
||||
implementation 'io.github.gradle-nexus:publish-plugin:1.1.0'
|
||||
implementation 'io.projectreactor:reactor-core:3.4.12'
|
||||
implementation 'io.projectreactor:reactor-core:3.4.13'
|
||||
implementation 'gradle.plugin.org.gretty:gretty:3.0.1'
|
||||
implementation 'com.apollographql.apollo:apollo-runtime:2.4.5'
|
||||
implementation 'com.github.ben-manes:gradle-versions-plugin:0.38.0'
|
||||
@@ -88,7 +87,7 @@ dependencies {
|
||||
implementation 'org.jfrog.buildinfo:build-info-extractor-gradle:4.24.20'
|
||||
implementation 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.7.1'
|
||||
|
||||
testImplementation platform('org.junit:junit-bom:5.8.1')
|
||||
testImplementation platform('org.junit:junit-bom:5.8.2')
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-api"
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-params"
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-engine"
|
||||
|
||||
@@ -25,7 +25,7 @@ public class DocsPlugin implements Plugin<Project> {
|
||||
group = 'Distribution'
|
||||
archiveBaseName = project.rootProject.name
|
||||
archiveClassifier = 'docs'
|
||||
description = "Builds -${classifier} archive containing all " +
|
||||
description = "Builds -${archiveClassifier.get()} archive containing all " +
|
||||
"Docs for deployment at docs.spring.io"
|
||||
|
||||
from(project.tasks.api.outputs) {
|
||||
|
||||
@@ -35,11 +35,6 @@ class RepositoryConventionPlugin implements Plugin<Project> {
|
||||
mavenLocal()
|
||||
}
|
||||
mavenCentral()
|
||||
jcenter() {
|
||||
content {
|
||||
includeGroup "org.gretty"
|
||||
}
|
||||
}
|
||||
if (isSnapshot) {
|
||||
maven {
|
||||
name = 'artifactory-snapshot'
|
||||
|
||||
+12
-18
@@ -107,7 +107,7 @@ public class RepositoryConventionPluginTests {
|
||||
this.project.getPluginManager().apply(RepositoryConventionPlugin.class);
|
||||
|
||||
RepositoryHandler repositories = this.project.getRepositories();
|
||||
assertThat(repositories).hasSize(5);
|
||||
assertThat(repositories).hasSize(4);
|
||||
assertThat((repositories.get(0)).getName()).isEqualTo("MavenLocal");
|
||||
}
|
||||
|
||||
@@ -119,39 +119,33 @@ public class RepositoryConventionPluginTests {
|
||||
this.project.getPluginManager().apply(RepositoryConventionPlugin.class);
|
||||
|
||||
RepositoryHandler repositories = this.project.getRepositories();
|
||||
assertThat(repositories).hasSize(6);
|
||||
assertThat(repositories).hasSize(5);
|
||||
assertThat((repositories.get(0)).getName()).isEqualTo("MavenLocal");
|
||||
}
|
||||
|
||||
private void assertSnapshotRepository(RepositoryHandler repositories) {
|
||||
assertThat(repositories).extracting(ArtifactRepository::getName).hasSize(6);
|
||||
assertThat(((MavenArtifactRepository) repositories.get(0)).getUrl().toString())
|
||||
.isEqualTo("https://repo.maven.apache.org/maven2/");
|
||||
assertThat(((MavenArtifactRepository) repositories.get(1)).getUrl().toString())
|
||||
.isEqualTo("https://jcenter.bintray.com/");
|
||||
assertThat(((MavenArtifactRepository) repositories.get(2)).getUrl().toString())
|
||||
.isEqualTo("https://repo.spring.io/snapshot/");
|
||||
assertThat(((MavenArtifactRepository) repositories.get(3)).getUrl().toString())
|
||||
.isEqualTo("https://repo.spring.io/milestone/");
|
||||
}
|
||||
|
||||
private void assertMilestoneRepository(RepositoryHandler repositories) {
|
||||
assertThat(repositories).extracting(ArtifactRepository::getName).hasSize(5);
|
||||
assertThat(((MavenArtifactRepository) repositories.get(0)).getUrl().toString())
|
||||
.isEqualTo("https://repo.maven.apache.org/maven2/");
|
||||
assertThat(((MavenArtifactRepository) repositories.get(1)).getUrl().toString())
|
||||
.isEqualTo("https://jcenter.bintray.com/");
|
||||
.isEqualTo("https://repo.spring.io/snapshot/");
|
||||
assertThat(((MavenArtifactRepository) repositories.get(2)).getUrl().toString())
|
||||
.isEqualTo("https://repo.spring.io/milestone/");
|
||||
}
|
||||
|
||||
private void assertReleaseRepository(RepositoryHandler repositories) {
|
||||
private void assertMilestoneRepository(RepositoryHandler repositories) {
|
||||
assertThat(repositories).extracting(ArtifactRepository::getName).hasSize(4);
|
||||
assertThat(((MavenArtifactRepository) repositories.get(0)).getUrl().toString())
|
||||
.isEqualTo("https://repo.maven.apache.org/maven2/");
|
||||
assertThat(((MavenArtifactRepository) repositories.get(1)).getUrl().toString())
|
||||
.isEqualTo("https://jcenter.bintray.com/");
|
||||
assertThat(((MavenArtifactRepository) repositories.get(2)).getUrl().toString())
|
||||
.isEqualTo("https://repo.spring.io/milestone/");
|
||||
}
|
||||
|
||||
private void assertReleaseRepository(RepositoryHandler repositories) {
|
||||
assertThat(repositories).extracting(ArtifactRepository::getName).hasSize(3);
|
||||
assertThat(((MavenArtifactRepository) repositories.get(0)).getUrl().toString())
|
||||
.isEqualTo("https://repo.maven.apache.org/maven2/");
|
||||
assertThat(((MavenArtifactRepository) repositories.get(1)).getUrl().toString())
|
||||
.isEqualTo("https://repo.spring.io/release/");
|
||||
}
|
||||
|
||||
|
||||
+4
-2
@@ -133,8 +133,10 @@ public final class AuthorityAuthorizationManager<T> implements AuthorizationMana
|
||||
|
||||
private boolean isAuthorized(Authentication authentication) {
|
||||
for (GrantedAuthority grantedAuthority : authentication.getAuthorities()) {
|
||||
if (this.authorities.contains(grantedAuthority)) {
|
||||
return true;
|
||||
for (GrantedAuthority authority : this.authorities) {
|
||||
if (authority.getAuthority().equals(grantedAuthority.getAuthority())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
+3
-2
@@ -45,9 +45,10 @@ public class AuthorityReactiveAuthorizationManager<T> implements ReactiveAuthori
|
||||
@Override
|
||||
public Mono<AuthorizationDecision> check(Mono<Authentication> authentication, T object) {
|
||||
// @formatter:off
|
||||
return authentication.filter((a) -> a.isAuthenticated())
|
||||
return authentication.filter(Authentication::isAuthenticated)
|
||||
.flatMapIterable(Authentication::getAuthorities)
|
||||
.any(this.authorities::contains)
|
||||
.map(GrantedAuthority::getAuthority)
|
||||
.any((grantedAuthority) -> this.authorities.stream().anyMatch((authority) -> authority.getAuthority().equals(grantedAuthority)))
|
||||
.map((granted) -> ((AuthorizationDecision) new AuthorityAuthorizationDecision(granted, this.authorities)))
|
||||
.defaultIfEmpty(new AuthorityAuthorizationDecision(false, this.authorities));
|
||||
// @formatter:on
|
||||
|
||||
+26
@@ -16,12 +16,14 @@
|
||||
|
||||
package org.springframework.security.authorization;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
@@ -133,6 +135,30 @@ public class AuthorityAuthorizationManagerTests {
|
||||
assertThat(manager.check(authentication, object).isGranted()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasAuthorityWhenUserHasCustomAuthorityThenGrantedDecision() {
|
||||
AuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasAuthority("ADMIN");
|
||||
GrantedAuthority customGrantedAuthority = () -> "ADMIN";
|
||||
|
||||
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password",
|
||||
Collections.singletonList(customGrantedAuthority));
|
||||
Object object = new Object();
|
||||
|
||||
assertThat(manager.check(authentication, object).isGranted()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasAuthorityWhenUserHasNotCustomAuthorityThenDeniedDecision() {
|
||||
AuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasAuthority("ADMIN");
|
||||
GrantedAuthority customGrantedAuthority = () -> "USER";
|
||||
|
||||
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password",
|
||||
Collections.singletonList(customGrantedAuthority));
|
||||
Object object = new Object();
|
||||
|
||||
assertThat(manager.check(authentication, object).isGranted()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasAnyRoleWhenUserHasAnyRoleThenGrantedDecision() {
|
||||
AuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasAnyRole("ADMIN", "USER");
|
||||
|
||||
+19
@@ -27,6 +27,7 @@ import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
@@ -88,6 +89,24 @@ public class AuthorityReactiveAuthorizationManagerTests {
|
||||
assertThat(granted).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkWhenHasCustomAuthorityAndAuthorizedThenReturnTrue() {
|
||||
GrantedAuthority customGrantedAuthority = () -> "ADMIN";
|
||||
this.authentication = new TestingAuthenticationToken("rob", "secret",
|
||||
Collections.singletonList(customGrantedAuthority));
|
||||
boolean granted = this.manager.check(Mono.just(this.authentication), null).block().isGranted();
|
||||
assertThat(granted).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkWhenHasCustomAuthorityAndAuthenticatedAndWrongAuthoritiesThenReturnFalse() {
|
||||
GrantedAuthority customGrantedAuthority = () -> "USER";
|
||||
this.authentication = new TestingAuthenticationToken("rob", "secret",
|
||||
Collections.singletonList(customGrantedAuthority));
|
||||
boolean granted = this.manager.check(Mono.just(this.authentication), null).block().isGranted();
|
||||
assertThat(granted).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkWhenHasRoleAndAuthorizedThenReturnTrue() {
|
||||
this.manager = AuthorityReactiveAuthorizationManager.hasRole("ADMIN");
|
||||
|
||||
+8
-8
@@ -8,15 +8,15 @@ javaPlatform {
|
||||
|
||||
dependencies {
|
||||
api platform("org.springframework:spring-framework-bom:$springFrameworkVersion")
|
||||
api platform("io.projectreactor:reactor-bom:2020.0.13")
|
||||
api platform("io.projectreactor:reactor-bom:2020.0.14")
|
||||
api platform("io.rsocket:rsocket-bom:1.1.1")
|
||||
api platform("org.junit:junit-bom:5.8.1")
|
||||
api platform("org.junit:junit-bom:5.8.2")
|
||||
api platform("org.springframework.data:spring-data-bom:2021.1.0")
|
||||
api platform("org.jetbrains.kotlin:kotlin-bom:$kotlinVersion")
|
||||
api platform("org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.5.2")
|
||||
api platform("com.fasterxml.jackson:jackson-bom:2.13.0")
|
||||
api platform("com.fasterxml.jackson:jackson-bom:2.13.1")
|
||||
constraints {
|
||||
api "ch.qos.logback:logback-classic:1.2.7"
|
||||
api "ch.qos.logback:logback-classic:1.2.9"
|
||||
api "com.google.inject:guice:3.0"
|
||||
api "com.nimbusds:nimbus-jose-jwt:9.14"
|
||||
api "com.nimbusds:oauth2-oidc-sdk:9.19"
|
||||
@@ -26,7 +26,7 @@ dependencies {
|
||||
api "commons-codec:commons-codec:1.15"
|
||||
api "commons-collections:commons-collections:3.2.2"
|
||||
api "commons-logging:commons-logging:1.2"
|
||||
api "io.mockk:mockk:1.12.0"
|
||||
api "io.mockk:mockk:1.12.1"
|
||||
api "io.projectreactor.tools:blockhound:1.0.6.RELEASE"
|
||||
api "javax.annotation:jsr250-api:1.0"
|
||||
api "javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api:1.2.2"
|
||||
@@ -53,9 +53,9 @@ dependencies {
|
||||
api "org.eclipse.jetty:jetty-servlet:9.4.44.v20210927"
|
||||
api "org.eclipse.persistence:javax.persistence:2.2.1"
|
||||
api "org.hamcrest:hamcrest:2.2"
|
||||
api "org.hibernate:hibernate-entitymanager:5.6.1.Final"
|
||||
api "org.hibernate:hibernate-entitymanager:5.6.3.Final"
|
||||
api "org.hsqldb:hsqldb:2.6.1"
|
||||
api "org.jasig.cas.client:cas-client-core:3.6.2"
|
||||
api "org.jasig.cas.client:cas-client-core:3.6.4"
|
||||
api "org.mockito:mockito-core:3.12.4"
|
||||
api "org.mockito:mockito-inline:3.12.4"
|
||||
api "org.mockito:mockito-junit-jupiter:3.12.4"
|
||||
@@ -71,7 +71,7 @@ dependencies {
|
||||
api "org.slf4j:jcl-over-slf4j:1.7.32"
|
||||
api "org.slf4j:log4j-over-slf4j:1.7.32"
|
||||
api "org.slf4j:slf4j-api:1.7.32"
|
||||
api "org.springframework.ldap:spring-ldap-core:2.3.4.RELEASE"
|
||||
api "org.springframework.ldap:spring-ldap-core:2.3.5.RELEASE"
|
||||
api "org.synchronoss.cloud:nio-multipart-parser:1.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -463,7 +463,7 @@ You should instead migrate to using `DelegatingPasswordEncoder` to support secur
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
@Bean
|
||||
public static NoOpPasswordEncoder passwordEncoder() {
|
||||
public static PasswordEncoder passwordEncoder() {
|
||||
return NoOpPasswordEncoder.getInstance();
|
||||
}
|
||||
----
|
||||
|
||||
@@ -162,7 +162,7 @@ class SecurityConfig {
|
||||
|
||||
@Bean
|
||||
fun securityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
|
||||
http {
|
||||
return http {
|
||||
authorizeExchange {
|
||||
authorize(anyExchange, authenticated)
|
||||
}
|
||||
@@ -170,8 +170,6 @@ class SecurityConfig {
|
||||
authorizationRequestResolver = authorizationRequestResolver(customClientRegistrationRepository)
|
||||
}
|
||||
}
|
||||
|
||||
return http.build()
|
||||
}
|
||||
|
||||
private fun authorizationRequestResolver(
|
||||
@@ -282,13 +280,11 @@ class OAuth2ClientSecurityConfig {
|
||||
|
||||
@Bean
|
||||
fun securityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
|
||||
http {
|
||||
return http {
|
||||
oauth2Client {
|
||||
authorizationRequestRepository = authorizationRequestRepository()
|
||||
}
|
||||
}
|
||||
|
||||
return http.build()
|
||||
}
|
||||
}
|
||||
----
|
||||
@@ -363,13 +359,11 @@ class OAuth2ClientSecurityConfig {
|
||||
|
||||
@Bean
|
||||
fun securityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
|
||||
http {
|
||||
return http {
|
||||
oauth2Client {
|
||||
authenticationManager = authorizationCodeAuthenticationManager()
|
||||
}
|
||||
}
|
||||
|
||||
return http.build()
|
||||
}
|
||||
|
||||
private fun authorizationCodeAuthenticationManager(): ReactiveAuthenticationManager {
|
||||
|
||||
@@ -55,7 +55,7 @@ class OAuth2ClientSecurityConfig {
|
||||
|
||||
@Bean
|
||||
fun securityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
|
||||
http {
|
||||
return http {
|
||||
oauth2Client {
|
||||
clientRegistrationRepository = clientRegistrationRepository()
|
||||
authorizedClientRepository = authorizedClientRepository()
|
||||
@@ -64,8 +64,6 @@ class OAuth2ClientSecurityConfig {
|
||||
authenticationManager = authenticationManager()
|
||||
}
|
||||
}
|
||||
|
||||
return http.build()
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
@@ -60,7 +60,7 @@ class OAuth2LoginSecurityConfig {
|
||||
|
||||
@Bean
|
||||
fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
|
||||
http {
|
||||
return http {
|
||||
oauth2Login {
|
||||
authenticationConverter = authenticationConverter()
|
||||
authenticationMatcher = authenticationMatcher()
|
||||
@@ -75,8 +75,6 @@ class OAuth2LoginSecurityConfig {
|
||||
securityContextRepository = securityContextRepository()
|
||||
}
|
||||
}
|
||||
|
||||
return http.build()
|
||||
}
|
||||
}
|
||||
----
|
||||
@@ -158,7 +156,7 @@ class OAuth2LoginSecurityConfig {
|
||||
|
||||
@Bean
|
||||
fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
|
||||
http {
|
||||
return http {
|
||||
exceptionHandling {
|
||||
authenticationEntryPoint = RedirectServerAuthenticationEntryPoint("/login/oauth2")
|
||||
}
|
||||
@@ -166,8 +164,6 @@ class OAuth2LoginSecurityConfig {
|
||||
authorizationRequestResolver = authorizationRequestResolver()
|
||||
}
|
||||
}
|
||||
|
||||
return http.build()
|
||||
}
|
||||
|
||||
private fun authorizationRequestResolver(): ServerOAuth2AuthorizationRequestResolver {
|
||||
@@ -243,13 +239,11 @@ class OAuth2LoginSecurityConfig {
|
||||
|
||||
@Bean
|
||||
fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
|
||||
http {
|
||||
return http {
|
||||
oauth2Login {
|
||||
authenticationMatcher = PathPatternParserServerWebExchangeMatcher("/login/oauth2/callback/{registrationId}")
|
||||
}
|
||||
}
|
||||
|
||||
return http.build()
|
||||
}
|
||||
}
|
||||
----
|
||||
@@ -369,11 +363,9 @@ class OAuth2LoginSecurityConfig {
|
||||
|
||||
@Bean
|
||||
fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
|
||||
http {
|
||||
return http {
|
||||
oauth2Login { }
|
||||
}
|
||||
|
||||
return http.build()
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -458,11 +450,9 @@ class OAuth2LoginSecurityConfig {
|
||||
|
||||
@Bean
|
||||
fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
|
||||
http {
|
||||
return http {
|
||||
oauth2Login { }
|
||||
}
|
||||
|
||||
return http.build()
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -536,11 +526,9 @@ class OAuth2LoginSecurityConfig {
|
||||
|
||||
@Bean
|
||||
fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
|
||||
http {
|
||||
return http {
|
||||
oauth2Login { }
|
||||
}
|
||||
|
||||
return http.build()
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -594,11 +582,9 @@ class OAuth2LoginSecurityConfig {
|
||||
|
||||
@Bean
|
||||
fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
|
||||
http {
|
||||
return http {
|
||||
oauth2Login { }
|
||||
}
|
||||
|
||||
return http.build()
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -730,7 +716,7 @@ class OAuth2LoginSecurityConfig {
|
||||
|
||||
@Bean
|
||||
fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
|
||||
http {
|
||||
return http {
|
||||
authorizeExchange {
|
||||
authorize(anyExchange, authenticated)
|
||||
}
|
||||
@@ -739,8 +725,6 @@ class OAuth2LoginSecurityConfig {
|
||||
logoutSuccessHandler = oidcLogoutSuccessHandler()
|
||||
}
|
||||
}
|
||||
|
||||
return http.build()
|
||||
}
|
||||
|
||||
private fun oidcLogoutSuccessHandler(): ServerLogoutSuccessHandler {
|
||||
|
||||
@@ -341,14 +341,12 @@ class OAuth2LoginSecurityConfig {
|
||||
|
||||
@Bean
|
||||
fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
|
||||
http {
|
||||
return http {
|
||||
authorizeExchange {
|
||||
authorize(anyExchange, authenticated)
|
||||
}
|
||||
oauth2Login { }
|
||||
}
|
||||
|
||||
return http.build()
|
||||
}
|
||||
}
|
||||
----
|
||||
@@ -411,14 +409,12 @@ class OAuth2LoginConfig {
|
||||
|
||||
@Bean
|
||||
fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
|
||||
http {
|
||||
return http {
|
||||
authorizeExchange {
|
||||
authorize(anyExchange, authenticated)
|
||||
}
|
||||
oauth2Login { }
|
||||
}
|
||||
|
||||
return http.build()
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -505,14 +501,12 @@ class OAuth2LoginConfig {
|
||||
|
||||
@Bean
|
||||
fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
|
||||
http {
|
||||
return http {
|
||||
authorizeExchange {
|
||||
authorize(anyExchange, authenticated)
|
||||
}
|
||||
oauth2Login { }
|
||||
}
|
||||
|
||||
return http.build()
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -323,7 +323,7 @@ Next, we can construct a `JWTProcessor`:
|
||||
JWTProcessor jwtProcessor(JWTClaimSetJWSKeySelector keySelector) {
|
||||
ConfigurableJWTProcessor<SecurityContext> jwtProcessor =
|
||||
new DefaultJWTProcessor();
|
||||
jwtProcessor.setJWTClaimSetJWSKeySelector(keySelector);
|
||||
jwtProcessor.setJWTClaimsSetAwareJWSKeySelector(keySelector);
|
||||
return jwtProcessor;
|
||||
}
|
||||
----
|
||||
@@ -416,9 +416,9 @@ Now that we have a tenant-aware processor and a tenant-aware validator, we can p
|
||||
----
|
||||
@Bean
|
||||
JwtDecoder jwtDecoder(JWTProcessor jwtProcessor, OAuth2TokenValidator<Jwt> jwtValidator) {
|
||||
NimbusJwtDecoder decoder = new NimbusJwtDecoder(processor);
|
||||
NimbusJwtDecoder decoder = new NimbusJwtDecoder(jwtProcessor);
|
||||
OAuth2TokenValidator<Jwt> validator = new DelegatingOAuth2TokenValidator<>
|
||||
(JwtValidators.createDefault(), this.jwtValidator);
|
||||
(JwtValidators.createDefault(), jwtValidator);
|
||||
decoder.setJwtValidator(validator);
|
||||
return decoder;
|
||||
}
|
||||
|
||||
@@ -187,7 +187,7 @@ But, if you do need something from the request, then you can use create a custom
|
||||
----
|
||||
@Component
|
||||
public class AuthnRequestConverter implements
|
||||
Converter<MySaml2AuthenticationRequestContext, AuthnRequest> {
|
||||
Converter<Saml2AuthenticationRequestContext, AuthnRequest> {
|
||||
|
||||
private final AuthnRequestBuilder authnRequestBuilder;
|
||||
private final IssuerBuilder issuerBuilder;
|
||||
@@ -216,12 +216,12 @@ public class AuthnRequestConverter implements
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
@Component
|
||||
class AuthnRequestConverter : Converter<MySaml2AuthenticationRequestContext, AuthnRequest> {
|
||||
class AuthnRequestConverter : Converter<Saml2AuthenticationRequestContext, AuthnRequest> {
|
||||
private val authnRequestBuilder: AuthnRequestBuilder? = null
|
||||
private val issuerBuilder: IssuerBuilder? = null
|
||||
|
||||
// ... constructor
|
||||
override fun convert(context: MySaml2AuthenticationRequestContext): AuthnRequest {
|
||||
override fun convert(context: Saml2AuthenticationRequestContext): AuthnRequest {
|
||||
val myContext: MySaml2AuthenticationRequestContext = context
|
||||
val issuer: Issuer = issuerBuilder.buildObject()
|
||||
issuer.value = myContext.getIssuer()
|
||||
|
||||
+4
-4
@@ -1,11 +1,11 @@
|
||||
aspectjVersion=1.9.7
|
||||
springJavaformatVersion=0.0.29
|
||||
springBootVersion=2.4.2
|
||||
springFrameworkVersion=5.3.13
|
||||
springFrameworkVersion=5.3.14
|
||||
openSamlVersion=3.4.6
|
||||
version=5.6.1-SNAPSHOT
|
||||
kotlinVersion=1.5.31
|
||||
samplesBranch=main
|
||||
version=5.6.1
|
||||
kotlinVersion=1.5.32
|
||||
samplesBranch=5.6.x
|
||||
org.gradle.jvmargs=-Xmx3g -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError
|
||||
org.gradle.parallel=true
|
||||
org.gradle.caching=true
|
||||
|
||||
+13
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -204,10 +204,12 @@ public final class OAuth2AuthorizedClientProviderBuilder {
|
||||
|
||||
/**
|
||||
* Sets the maximum acceptable clock skew, which is used when checking the access
|
||||
* token expiry. An access token is considered expired if it's before
|
||||
* {@code Instant.now(this.clock) - clockSkew}.
|
||||
* token expiry. An access token is considered expired if
|
||||
* {@code OAuth2Token#getExpiresAt() - clockSkew} is before the current time
|
||||
* {@code clock#instant()}.
|
||||
* @param clockSkew the maximum acceptable clock skew
|
||||
* @return the {@link PasswordGrantBuilder}
|
||||
* @see PasswordOAuth2AuthorizedClientProvider#setClockSkew(Duration)
|
||||
*/
|
||||
public PasswordGrantBuilder clockSkew(Duration clockSkew) {
|
||||
this.clockSkew = clockSkew;
|
||||
@@ -275,10 +277,12 @@ public final class OAuth2AuthorizedClientProviderBuilder {
|
||||
|
||||
/**
|
||||
* Sets the maximum acceptable clock skew, which is used when checking the access
|
||||
* token expiry. An access token is considered expired if it's before
|
||||
* {@code Instant.now(this.clock) - clockSkew}.
|
||||
* token expiry. An access token is considered expired if
|
||||
* {@code OAuth2Token#getExpiresAt() - clockSkew} is before the current time
|
||||
* {@code clock#instant()}.
|
||||
* @param clockSkew the maximum acceptable clock skew
|
||||
* @return the {@link ClientCredentialsGrantBuilder}
|
||||
* @see ClientCredentialsOAuth2AuthorizedClientProvider#setClockSkew(Duration)
|
||||
*/
|
||||
public ClientCredentialsGrantBuilder clockSkew(Duration clockSkew) {
|
||||
this.clockSkew = clockSkew;
|
||||
@@ -365,10 +369,12 @@ public final class OAuth2AuthorizedClientProviderBuilder {
|
||||
|
||||
/**
|
||||
* Sets the maximum acceptable clock skew, which is used when checking the access
|
||||
* token expiry. An access token is considered expired if it's before
|
||||
* {@code Instant.now(this.clock) - clockSkew}.
|
||||
* token expiry. An access token is considered expired if
|
||||
* {@code OAuth2Token#getExpiresAt() - clockSkew} is before the current time
|
||||
* {@code clock#instant()}.
|
||||
* @param clockSkew the maximum acceptable clock skew
|
||||
* @return the {@link RefreshTokenGrantBuilder}
|
||||
* @see RefreshTokenOAuth2AuthorizedClientProvider#setClockSkew(Duration)
|
||||
*/
|
||||
public RefreshTokenGrantBuilder clockSkew(Duration clockSkew) {
|
||||
this.clockSkew = clockSkew;
|
||||
|
||||
+13
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -225,10 +225,12 @@ public final class ReactiveOAuth2AuthorizedClientProviderBuilder {
|
||||
|
||||
/**
|
||||
* Sets the maximum acceptable clock skew, which is used when checking the access
|
||||
* token expiry. An access token is considered expired if it's before
|
||||
* {@code Instant.now(this.clock) - clockSkew}.
|
||||
* token expiry. An access token is considered expired if
|
||||
* {@code OAuth2Token#getExpiresAt() - clockSkew} is before the current time
|
||||
* {@code clock#instant()}.
|
||||
* @param clockSkew the maximum acceptable clock skew
|
||||
* @return the {@link ClientCredentialsGrantBuilder}
|
||||
* @see ClientCredentialsReactiveOAuth2AuthorizedClientProvider#setClockSkew(Duration)
|
||||
*/
|
||||
public ClientCredentialsGrantBuilder clockSkew(Duration clockSkew) {
|
||||
this.clockSkew = clockSkew;
|
||||
@@ -297,10 +299,12 @@ public final class ReactiveOAuth2AuthorizedClientProviderBuilder {
|
||||
|
||||
/**
|
||||
* Sets the maximum acceptable clock skew, which is used when checking the access
|
||||
* token expiry. An access token is considered expired if it's before
|
||||
* {@code Instant.now(this.clock) - clockSkew}.
|
||||
* token expiry. An access token is considered expired if
|
||||
* {@code OAuth2Token#getExpiresAt() - clockSkew} is before the current time
|
||||
* {@code clock#instant()}.
|
||||
* @param clockSkew the maximum acceptable clock skew
|
||||
* @return the {@link PasswordGrantBuilder}
|
||||
* @see PasswordReactiveOAuth2AuthorizedClientProvider#setClockSkew(Duration)
|
||||
*/
|
||||
public PasswordGrantBuilder clockSkew(Duration clockSkew) {
|
||||
this.clockSkew = clockSkew;
|
||||
@@ -368,10 +372,12 @@ public final class ReactiveOAuth2AuthorizedClientProviderBuilder {
|
||||
|
||||
/**
|
||||
* Sets the maximum acceptable clock skew, which is used when checking the access
|
||||
* token expiry. An access token is considered expired if it's before
|
||||
* {@code Instant.now(this.clock) - clockSkew}.
|
||||
* token expiry. An access token is considered expired if
|
||||
* {@code OAuth2Token#getExpiresAt() - clockSkew} is before the current time
|
||||
* {@code clock#instant()}.
|
||||
* @param clockSkew the maximum acceptable clock skew
|
||||
* @return the {@link RefreshTokenGrantBuilder}
|
||||
* @see RefreshTokenReactiveOAuth2AuthorizedClientProvider#setClockSkew(Duration)
|
||||
*/
|
||||
public RefreshTokenGrantBuilder clockSkew(Duration clockSkew) {
|
||||
this.clockSkew = clockSkew;
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -54,7 +54,7 @@ public final class JwtClaimValidator<T> implements OAuth2TokenValidator<Jwt> {
|
||||
Assert.notNull(test, "test can not be null");
|
||||
this.claim = claim;
|
||||
this.test = test;
|
||||
this.error = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST, "The " + this.claim + " claim is not valid",
|
||||
this.error = new OAuth2Error(OAuth2ErrorCodes.INVALID_TOKEN, "The " + this.claim + " claim is not valid",
|
||||
"https://tools.ietf.org/html/rfc6750#section-3.1");
|
||||
}
|
||||
|
||||
|
||||
+7
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,10 +16,14 @@
|
||||
|
||||
package org.springframework.security.oauth2.jwt;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
|
||||
import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -45,7 +49,9 @@ public class JwtClaimValidatorTests {
|
||||
@Test
|
||||
public void validateWhenClaimFailsTheTestThenReturnsFailure() {
|
||||
Jwt jwt = TestJwts.jwt().claim(JwtClaimNames.ISS, "http://abc").build();
|
||||
Collection<OAuth2Error> details = this.validator.validate(jwt).getErrors();
|
||||
assertThat(this.validator.validate(jwt).getErrors().isEmpty()).isFalse();
|
||||
assertThat(details).allMatch((error) -> Objects.equals(error.getErrorCode(), OAuth2ErrorCodes.INVALID_TOKEN));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+4
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -23,6 +23,7 @@ import java.time.ZoneId;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -64,6 +65,7 @@ public class JwtTimestampValidatorTests {
|
||||
.collect(Collectors.toList());
|
||||
// @formatter:on
|
||||
assertThat(messages).contains("Jwt expired at " + oneHourAgo);
|
||||
assertThat(details).allMatch((error) -> Objects.equals(error.getErrorCode(), OAuth2ErrorCodes.INVALID_TOKEN));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -78,6 +80,7 @@ public class JwtTimestampValidatorTests {
|
||||
.collect(Collectors.toList());
|
||||
// @formatter:on
|
||||
assertThat(messages).contains("Jwt used before " + oneHourFromNow);
|
||||
assertThat(details).allMatch((error) -> Objects.equals(error.getErrorCode(), OAuth2ErrorCodes.INVALID_TOKEN));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -10,8 +10,6 @@ plugins {
|
||||
id "io.spring.ge.conventions" version "0.0.7"
|
||||
}
|
||||
|
||||
enableFeaturePreview("VERSION_ORDERING_V2")
|
||||
|
||||
dependencyResolutionManagement {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
|
||||
+11
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,7 +17,6 @@
|
||||
package org.springframework.security.web.server.header;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@@ -41,8 +40,16 @@ public class StaticServerHttpHeadersWriter implements ServerHttpHeadersWriter {
|
||||
@Override
|
||||
public Mono<Void> writeHttpHeaders(ServerWebExchange exchange) {
|
||||
HttpHeaders headers = exchange.getResponse().getHeaders();
|
||||
boolean containsOneHeaderToAdd = Collections.disjoint(headers.keySet(), this.headersToAdd.keySet());
|
||||
if (containsOneHeaderToAdd) {
|
||||
// Note: We need to ensure that the following algorithm compares headers
|
||||
// case insensitively, which should be true of headers.containsKey().
|
||||
boolean containsNoHeadersToAdd = true;
|
||||
for (String headerName : this.headersToAdd.keySet()) {
|
||||
if (headers.containsKey(headerName)) {
|
||||
containsNoHeadersToAdd = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (containsNoHeadersToAdd) {
|
||||
this.headersToAdd.forEach(headers::put);
|
||||
}
|
||||
return Mono.empty();
|
||||
|
||||
+22
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,11 +16,14 @@
|
||||
|
||||
package org.springframework.security.web.server.header;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.mock.web.server.MockServerWebExchange;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -56,6 +59,24 @@ public class StaticServerHttpHeadersWriterTests {
|
||||
.containsOnly(headerValue);
|
||||
}
|
||||
|
||||
// gh-10557
|
||||
@Test
|
||||
public void writeHeadersWhenHeaderWrittenWithDifferentCaseThenDoesNotWriteHeaders() {
|
||||
String headerName = HttpHeaders.CACHE_CONTROL.toLowerCase(Locale.ROOT);
|
||||
String headerValue = "max-age=120";
|
||||
this.headers.set(headerName, headerValue);
|
||||
// Note: This test inverts which collection uses case sensitive headers,
|
||||
// due to the fact that gh-10557 reports NettyHeadersAdapter as the
|
||||
// response headers implementation, which is not accessible here.
|
||||
HttpHeaders caseSensitiveHeaders = new HttpHeaders(new LinkedMultiValueMap<>());
|
||||
caseSensitiveHeaders.set(HttpHeaders.CACHE_CONTROL, CacheControlServerHttpHeadersWriter.CACHE_CONTRTOL_VALUE);
|
||||
caseSensitiveHeaders.set(HttpHeaders.PRAGMA, CacheControlServerHttpHeadersWriter.PRAGMA_VALUE);
|
||||
caseSensitiveHeaders.set(HttpHeaders.EXPIRES, CacheControlServerHttpHeadersWriter.EXPIRES_VALUE);
|
||||
this.writer = new StaticServerHttpHeadersWriter(caseSensitiveHeaders);
|
||||
this.writer.writeHttpHeaders(this.exchange);
|
||||
assertThat(this.headers.get(headerName)).containsOnly(headerValue);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeHeadersWhenMultiHeaderThenWritesAllHeaders() {
|
||||
this.writer = StaticServerHttpHeadersWriter.builder()
|
||||
|
||||
Reference in New Issue
Block a user