Merge branch 'spring-projects:main' into fix-ror-link
This commit is contained in:
commit
18d8a3a56e
|
@ -4,6 +4,32 @@ registries:
|
||||||
type: maven-repository
|
type: maven-repository
|
||||||
url: https://repo.spring.io/milestone
|
url: https://repo.spring.io/milestone
|
||||||
updates:
|
updates:
|
||||||
|
- package-ecosystem: gradle
|
||||||
|
target-branch: 6.5.x
|
||||||
|
directory: /
|
||||||
|
schedule:
|
||||||
|
interval: daily
|
||||||
|
time: '03:00'
|
||||||
|
timezone: Etc/UTC
|
||||||
|
labels:
|
||||||
|
- 'type: dependency-upgrade'
|
||||||
|
registries:
|
||||||
|
- spring-milestones
|
||||||
|
ignore:
|
||||||
|
- dependency-name: com.nimbusds:nimbus-jose-jwt
|
||||||
|
- dependency-name: org.python:jython
|
||||||
|
- dependency-name: org.apache.directory.server:*
|
||||||
|
- dependency-name: org.apache.directory.shared:*
|
||||||
|
- dependency-name: org.junit:junit-bom
|
||||||
|
update-types:
|
||||||
|
- version-update:semver-major
|
||||||
|
- dependency-name: org.mockito:mockito-bom
|
||||||
|
update-types:
|
||||||
|
- version-update:semver-major
|
||||||
|
- dependency-name: '*'
|
||||||
|
update-types:
|
||||||
|
- version-update:semver-major
|
||||||
|
- version-update:semver-minor
|
||||||
- package-ecosystem: gradle
|
- package-ecosystem: gradle
|
||||||
target-branch: 6.4.x
|
target-branch: 6.4.x
|
||||||
directory: /
|
directory: /
|
||||||
|
|
|
@ -39,48 +39,25 @@ jobs:
|
||||||
toolchain: 17
|
toolchain: 17
|
||||||
with:
|
with:
|
||||||
java-version: ${{ matrix.java-version }}
|
java-version: ${{ matrix.java-version }}
|
||||||
test-args: --refresh-dependencies -PforceMavenRepositories=snapshot -PisOverrideVersionCatalog -PtestToolchain=${{ matrix.toolchain }} -PspringFrameworkVersion=6.2.+ -PreactorVersion=2023.0.+ -PspringDataVersion=2024.0.+ --stacktrace
|
test-args: --refresh-dependencies -PforceMavenRepositories=snapshot,https://oss.sonatype.org/content/repositories/snapshots -PisOverrideVersionCatalog -PtestToolchain=${{ matrix.toolchain }} -PspringFrameworkVersion=7.+ -PreactorVersion=2025.+ -PspringDataVersion=2025.+ --stacktrace
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
check-samples:
|
|
||||||
name: Check Samples
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: ${{ github.repository_owner == 'spring-projects' }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: Set up gradle
|
|
||||||
uses: spring-io/spring-gradle-build-action@v2
|
|
||||||
with:
|
|
||||||
java-version: 17
|
|
||||||
distribution: temurin
|
|
||||||
- name: Check samples project
|
|
||||||
env:
|
|
||||||
LOCAL_REPOSITORY_PATH: ${{ github.workspace }}/build/publications/repos
|
|
||||||
SAMPLES_DIR: ../spring-security-samples
|
|
||||||
run: |
|
|
||||||
# Extract version from gradle.properties
|
|
||||||
version=$(cat gradle.properties | grep "version=" | awk -F'=' '{print $2}')
|
|
||||||
# Extract samplesBranch from gradle.properties
|
|
||||||
samples_branch=$(cat gradle.properties | grep "samplesBranch=" | awk -F'=' '{print $2}')
|
|
||||||
./gradlew publishMavenJavaPublicationToLocalRepository
|
|
||||||
./gradlew cloneRepository -PrepositoryName="spring-projects/spring-security-samples" -Pref="$samples_branch" -PcloneOutputDirectory="$SAMPLES_DIR"
|
|
||||||
./gradlew --refresh-dependencies --project-dir "$SAMPLES_DIR" --init-script spring-security-ci.gradle -PlocalRepositoryPath="$LOCAL_REPOSITORY_PATH" -PspringSecurityVersion="$version" test integrationTest
|
|
||||||
deploy-artifacts:
|
deploy-artifacts:
|
||||||
name: Deploy Artifacts
|
name: Deploy Artifacts
|
||||||
needs: [ build, test, check-samples ]
|
needs: [ build, test]
|
||||||
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-artifacts.yml@v1
|
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-artifacts.yml@v1
|
||||||
with:
|
with:
|
||||||
should-deploy-artifacts: ${{ needs.build.outputs.should-deploy-artifacts }}
|
should-deploy-artifacts: ${{ needs.build.outputs.should-deploy-artifacts }}
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
deploy-docs:
|
deploy-docs:
|
||||||
name: Deploy Docs
|
name: Deploy Docs
|
||||||
needs: [ build, test, check-samples ]
|
needs: [ build, test ]
|
||||||
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-docs.yml@v1
|
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-docs.yml@v1
|
||||||
with:
|
with:
|
||||||
should-deploy-docs: ${{ needs.build.outputs.should-deploy-artifacts }}
|
should-deploy-docs: ${{ needs.build.outputs.should-deploy-artifacts }}
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
deploy-schema:
|
deploy-schema:
|
||||||
name: Deploy Schema
|
name: Deploy Schema
|
||||||
needs: [ build, test, check-samples ]
|
needs: [ build, test ]
|
||||||
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-schema.yml@v1
|
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-schema.yml@v1
|
||||||
with:
|
with:
|
||||||
should-deploy-schema: ${{ needs.build.outputs.should-deploy-artifacts }}
|
should-deploy-schema: ${{ needs.build.outputs.should-deploy-artifacts }}
|
||||||
|
|
|
@ -11,7 +11,7 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
# List of active maintenance branches.
|
# List of active maintenance branches.
|
||||||
branch: [ main, 6.4.x, 6.3.x ]
|
branch: [ main, 6.5.x, 6.4.x, 6.3.x ]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
|
|
|
@ -80,6 +80,11 @@ class RepositoryConventionPlugin implements Plugin<Project> {
|
||||||
}
|
}
|
||||||
url = 'https://repo.spring.io/release/'
|
url = 'https://repo.spring.io/release/'
|
||||||
}
|
}
|
||||||
|
forceMavenRepositories.findAll { it.startsWith('https://') || it.startsWith('file://') }.each { mavenUrl ->
|
||||||
|
maven {
|
||||||
|
url mavenUrl
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,10 +32,13 @@ public class SchemaZipPlugin implements Plugin<Project> {
|
||||||
for (def key : schemas.keySet()) {
|
for (def key : schemas.keySet()) {
|
||||||
def shortName = key.replaceAll(/http.*schema.(.*).spring-.*/, '$1')
|
def shortName = key.replaceAll(/http.*schema.(.*).spring-.*/, '$1')
|
||||||
assert shortName != key
|
assert shortName != key
|
||||||
|
def schemaResourceName = schemas.get(key)
|
||||||
File xsdFile = module.sourceSets.main.resources.find {
|
File xsdFile = module.sourceSets.main.resources.find {
|
||||||
it.path.endsWith(schemas.get(key))
|
it.path.endsWith(schemaResourceName)
|
||||||
|
}
|
||||||
|
if (xsdFile == null) {
|
||||||
|
throw new IllegalStateException("Could not find schema file for resource name " + schemaResourceName + " in src/main/resources")
|
||||||
}
|
}
|
||||||
assert xsdFile != null
|
|
||||||
schemaZip.into (shortName) {
|
schemaZip.into (shortName) {
|
||||||
duplicatesStrategy 'exclude'
|
duplicatesStrategy 'exclude'
|
||||||
from xsdFile.path
|
from xsdFile.path
|
||||||
|
|
|
@ -81,9 +81,6 @@ public class CheckClasspathForProhibitedDependencies extends DefaultTask {
|
||||||
if (group.startsWith("javax")) {
|
if (group.startsWith("javax")) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (group.equals("commons-logging")) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (group.equals("org.slf4j") && id.getName().equals("jcl-over-slf4j")) {
|
if (group.equals("org.slf4j") && id.getName().equals("jcl-over-slf4j")) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,12 +78,6 @@ dependencies {
|
||||||
exclude group: 'commons-logging', module: 'commons-logging'
|
exclude group: 'commons-logging', module: 'commons-logging'
|
||||||
exclude group: 'xml-apis', module: 'xml-apis'
|
exclude group: 'xml-apis', module: 'xml-apis'
|
||||||
}
|
}
|
||||||
testImplementation "org.apache.directory.server:apacheds-core"
|
|
||||||
testImplementation "org.apache.directory.server:apacheds-core-entry"
|
|
||||||
testImplementation "org.apache.directory.server:apacheds-protocol-shared"
|
|
||||||
testImplementation "org.apache.directory.server:apacheds-protocol-ldap"
|
|
||||||
testImplementation "org.apache.directory.server:apacheds-server-jndi"
|
|
||||||
testImplementation 'org.apache.directory.shared:shared-ldap'
|
|
||||||
testImplementation "com.unboundid:unboundid-ldapsdk"
|
testImplementation "com.unboundid:unboundid-ldapsdk"
|
||||||
testImplementation 'jakarta.persistence:jakarta.persistence-api'
|
testImplementation 'jakarta.persistence:jakarta.persistence-api'
|
||||||
testImplementation "org.hibernate.orm:hibernate-core"
|
testImplementation "org.hibernate.orm:hibernate-core"
|
||||||
|
@ -127,6 +121,7 @@ dependencies {
|
||||||
|
|
||||||
testRuntimeOnly 'org.hsqldb:hsqldb'
|
testRuntimeOnly 'org.hsqldb:hsqldb'
|
||||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
||||||
|
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
|
||||||
}
|
}
|
||||||
|
|
||||||
def rncToXsd = tasks.named('rncToXsd', RncToXsd)
|
def rncToXsd = tasks.named('rncToXsd', RncToXsd)
|
||||||
|
|
|
@ -44,7 +44,7 @@ import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMap
|
||||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
|
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
|
||||||
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
|
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
|
||||||
import org.springframework.security.ldap.server.ApacheDSContainer;
|
import org.springframework.security.ldap.server.UnboundIdContainer;
|
||||||
import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;
|
import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;
|
||||||
import org.springframework.test.util.ReflectionTestUtils;
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
@ -326,11 +326,11 @@ public class LdapAuthenticationProviderBuilderSecurityBuilderTests {
|
||||||
abstract static class BaseLdapServerConfig extends BaseLdapProviderConfig {
|
abstract static class BaseLdapServerConfig extends BaseLdapProviderConfig {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
ApacheDSContainer ldapServer() throws Exception {
|
UnboundIdContainer ldapServer() throws Exception {
|
||||||
ApacheDSContainer apacheDSContainer = new ApacheDSContainer("dc=springframework,dc=org",
|
UnboundIdContainer unboundIdContainer = new UnboundIdContainer("dc=springframework,dc=org",
|
||||||
"classpath:/test-server.ldif");
|
"classpath:/test-server.ldif");
|
||||||
apacheDSContainer.setPort(getPort());
|
unboundIdContainer.setPort(getPort());
|
||||||
return apacheDSContainer;
|
return unboundIdContainer;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMap
|
||||||
import org.springframework.security.core.userdetails.User;
|
import org.springframework.security.core.userdetails.User;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
|
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
|
||||||
import org.springframework.security.ldap.server.ApacheDSContainer;
|
import org.springframework.security.ldap.server.UnboundIdContainer;
|
||||||
import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator;
|
import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator;
|
||||||
import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;
|
import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;
|
||||||
import org.springframework.security.ldap.userdetails.UserDetailsContextMapper;
|
import org.springframework.security.ldap.userdetails.UserDetailsContextMapper;
|
||||||
|
@ -226,18 +226,18 @@ public class LdapBindAuthenticationManagerFactoryITests {
|
||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
abstract static class BaseLdapServerConfig implements DisposableBean {
|
abstract static class BaseLdapServerConfig implements DisposableBean {
|
||||||
|
|
||||||
private ApacheDSContainer container;
|
private UnboundIdContainer container;
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
ApacheDSContainer ldapServer() throws Exception {
|
UnboundIdContainer ldapServer() {
|
||||||
this.container = new ApacheDSContainer("dc=springframework,dc=org", "classpath:/test-server.ldif");
|
this.container = new UnboundIdContainer("dc=springframework,dc=org", "classpath:/test-server.ldif");
|
||||||
this.container.setPort(0);
|
this.container.setPort(0);
|
||||||
return this.container;
|
return this.container;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
BaseLdapPathContextSource contextSource(ApacheDSContainer container) {
|
BaseLdapPathContextSource contextSource(UnboundIdContainer container) {
|
||||||
int port = container.getLocalPort();
|
int port = container.getPort();
|
||||||
return new DefaultSpringSecurityContextSource("ldap://localhost:" + port + "/dc=springframework,dc=org");
|
return new DefaultSpringSecurityContextSource("ldap://localhost:" + port + "/dc=springframework,dc=org");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ import org.springframework.security.config.test.SpringTestContextExtension;
|
||||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
|
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
|
||||||
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
|
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
|
||||||
import org.springframework.security.ldap.server.ApacheDSContainer;
|
import org.springframework.security.ldap.server.UnboundIdContainer;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
|
||||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;
|
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;
|
||||||
|
@ -93,18 +93,18 @@ public class LdapPasswordComparisonAuthenticationManagerFactoryITests {
|
||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
abstract static class BaseLdapServerConfig implements DisposableBean {
|
abstract static class BaseLdapServerConfig implements DisposableBean {
|
||||||
|
|
||||||
private ApacheDSContainer container;
|
private UnboundIdContainer container;
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
ApacheDSContainer ldapServer() throws Exception {
|
UnboundIdContainer ldapServer() {
|
||||||
this.container = new ApacheDSContainer("dc=springframework,dc=org", "classpath:/test-server.ldif");
|
this.container = new UnboundIdContainer("dc=springframework,dc=org", "classpath:/test-server.ldif");
|
||||||
this.container.setPort(0);
|
this.container.setPort(0);
|
||||||
return this.container;
|
return this.container;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
BaseLdapPathContextSource contextSource(ApacheDSContainer container) {
|
BaseLdapPathContextSource contextSource(UnboundIdContainer container) {
|
||||||
int port = container.getLocalPort();
|
int port = container.getPort();
|
||||||
return new DefaultSpringSecurityContextSource("ldap://localhost:" + port + "/dc=springframework,dc=org");
|
return new DefaultSpringSecurityContextSource("ldap://localhost:" + port + "/dc=springframework,dc=org");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,7 @@ public class LdapProviderBeanDefinitionParserTests {
|
||||||
AuthenticationManager authenticationManager = this.appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER,
|
AuthenticationManager authenticationManager = this.appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER,
|
||||||
AuthenticationManager.class);
|
AuthenticationManager.class);
|
||||||
Authentication auth = authenticationManager
|
Authentication auth = authenticationManager
|
||||||
.authenticate(UsernamePasswordAuthenticationToken.unauthenticated("ben", "benspassword"));
|
.authenticate(UsernamePasswordAuthenticationToken.unauthenticated("otherben", "otherbenspassword"));
|
||||||
UserDetails ben = (UserDetails) auth.getPrincipal();
|
UserDetails ben = (UserDetails) auth.getPrincipal();
|
||||||
assertThat(ben.getAuthorities()).hasSize(3);
|
assertThat(ben.getAuthorities()).hasSize(3);
|
||||||
}
|
}
|
||||||
|
@ -127,6 +127,27 @@ public class LdapProviderBeanDefinitionParserTests {
|
||||||
assertThat(auth).isNotNull();
|
assertThat(auth).isNotNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void supportsShaPasswordEncoder() {
|
||||||
|
this.appCtx = new InMemoryXmlApplicationContext("""
|
||||||
|
<ldap-server ldif='classpath:test-server.ldif' port='0'/>
|
||||||
|
<authentication-manager>
|
||||||
|
<ldap-authentication-provider user-dn-pattern='uid={0},ou=people'>
|
||||||
|
<password-compare>
|
||||||
|
<password-encoder ref='pe' />
|
||||||
|
</password-compare>
|
||||||
|
</ldap-authentication-provider>
|
||||||
|
</authentication-manager>
|
||||||
|
<b:bean id='pe' class='org.springframework.security.crypto.password.LdapShaPasswordEncoder' />
|
||||||
|
""");
|
||||||
|
AuthenticationManager authenticationManager = this.appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER,
|
||||||
|
AuthenticationManager.class);
|
||||||
|
Authentication auth = authenticationManager
|
||||||
|
.authenticate(UsernamePasswordAuthenticationToken.unauthenticated("ben", "benspassword"));
|
||||||
|
|
||||||
|
assertThat(auth).isNotNull();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void inetOrgContextMapperIsSupported() {
|
public void inetOrgContextMapperIsSupported() {
|
||||||
this.appCtx = new InMemoryXmlApplicationContext(
|
this.appCtx = new InMemoryXmlApplicationContext(
|
||||||
|
|
|
@ -26,7 +26,7 @@ import org.springframework.ldap.core.LdapTemplate;
|
||||||
import org.springframework.security.config.BeanIds;
|
import org.springframework.security.config.BeanIds;
|
||||||
import org.springframework.security.config.util.InMemoryXmlApplicationContext;
|
import org.springframework.security.config.util.InMemoryXmlApplicationContext;
|
||||||
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
|
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
|
||||||
import org.springframework.security.ldap.server.ApacheDSContainer;
|
import org.springframework.security.ldap.server.UnboundIdContainer;
|
||||||
import org.springframework.test.util.ReflectionTestUtils;
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
@ -92,9 +92,9 @@ public class LdapServerBeanDefinitionParserTests {
|
||||||
@Test
|
@Test
|
||||||
public void defaultLdifFileIsSuccessful() {
|
public void defaultLdifFileIsSuccessful() {
|
||||||
this.appCtx = new InMemoryXmlApplicationContext("<ldap-server/>");
|
this.appCtx = new InMemoryXmlApplicationContext("<ldap-server/>");
|
||||||
ApacheDSContainer dsContainer = this.appCtx.getBean(ApacheDSContainer.class);
|
UnboundIdContainer dsContainer = this.appCtx.getBean(UnboundIdContainer.class);
|
||||||
|
|
||||||
assertThat(ReflectionTestUtils.getField(dsContainer, "ldifResources")).isEqualTo("classpath*:*.ldif");
|
assertThat(ReflectionTestUtils.getField(dsContainer, "ldif")).isEqualTo("classpath*:*.ldif");
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getDefaultPort() throws IOException {
|
private int getDefaultPort() throws IOException {
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
|
|
||||||
<logger name="org.springframework.security" level="${sec.log.level:-WARN}"/>
|
<logger name="org.springframework.security" level="${sec.log.level:-WARN}"/>
|
||||||
|
|
||||||
<logger name="org.apache.directory" level="ERROR"/>
|
|
||||||
<logger name="JdbmTable" level="INFO"/>
|
<logger name="JdbmTable" level="INFO"/>
|
||||||
<logger name="JdbmIndex" level="INFO"/>
|
<logger name="JdbmIndex" level="INFO"/>
|
||||||
<logger name="org.apache.mina" level="WARN"/>
|
<logger name="org.apache.mina" level="WARN"/>
|
||||||
|
|
|
@ -54,8 +54,6 @@ public abstract class BeanIds {
|
||||||
|
|
||||||
public static final String METHOD_SECURITY_METADATA_SOURCE_ADVISOR = PREFIX + "methodSecurityMetadataSourceAdvisor";
|
public static final String METHOD_SECURITY_METADATA_SOURCE_ADVISOR = PREFIX + "methodSecurityMetadataSourceAdvisor";
|
||||||
|
|
||||||
public static final String EMBEDDED_APACHE_DS = PREFIX + "apacheDirectoryServerContainer";
|
|
||||||
|
|
||||||
public static final String EMBEDDED_UNBOUNDID = PREFIX + "unboundidServerContainer";
|
public static final String EMBEDDED_UNBOUNDID = PREFIX + "unboundidServerContainer";
|
||||||
|
|
||||||
public static final String CONTEXT_SOURCE = PREFIX + "securityContextSource";
|
public static final String CONTEXT_SOURCE = PREFIX + "securityContextSource";
|
||||||
|
|
|
@ -96,7 +96,7 @@ public final class SecurityNamespaceHandler implements NamespaceHandler {
|
||||||
pc.getReaderContext()
|
pc.getReaderContext()
|
||||||
.fatal("You cannot use a spring-security-2.0.xsd or spring-security-3.0.xsd or "
|
.fatal("You cannot use a spring-security-2.0.xsd or spring-security-3.0.xsd or "
|
||||||
+ "spring-security-3.1.xsd schema or spring-security-3.2.xsd schema or spring-security-4.0.xsd schema "
|
+ "spring-security-3.1.xsd schema or spring-security-3.2.xsd schema or spring-security-4.0.xsd schema "
|
||||||
+ "with Spring Security 6.5. Please update your schema declarations to the 6.5 schema.",
|
+ "with Spring Security 7.0. Please update your schema declarations to the 7.0 schema.",
|
||||||
element);
|
element);
|
||||||
}
|
}
|
||||||
String name = pc.getDelegate().getLocalName(element);
|
String name = pc.getDelegate().getLocalName(element);
|
||||||
|
@ -221,7 +221,7 @@ public final class SecurityNamespaceHandler implements NamespaceHandler {
|
||||||
|
|
||||||
private boolean matchesVersionInternal(Element element) {
|
private boolean matchesVersionInternal(Element element) {
|
||||||
String schemaLocation = element.getAttributeNS("http://www.w3.org/2001/XMLSchema-instance", "schemaLocation");
|
String schemaLocation = element.getAttributeNS("http://www.w3.org/2001/XMLSchema-instance", "schemaLocation");
|
||||||
return schemaLocation.matches("(?m).*spring-security-6\\.5.*.xsd.*")
|
return schemaLocation.matches("(?m).*spring-security-7\\.0.*.xsd.*")
|
||||||
|| schemaLocation.matches("(?m).*spring-security.xsd.*")
|
|| schemaLocation.matches("(?m).*spring-security.xsd.*")
|
||||||
|| !schemaLocation.matches("(?m).*spring-security.*");
|
|| !schemaLocation.matches("(?m).*spring-security.*");
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,6 @@ import org.springframework.security.ldap.authentication.LdapAuthenticator;
|
||||||
import org.springframework.security.ldap.authentication.PasswordComparisonAuthenticator;
|
import org.springframework.security.ldap.authentication.PasswordComparisonAuthenticator;
|
||||||
import org.springframework.security.ldap.search.FilterBasedLdapUserSearch;
|
import org.springframework.security.ldap.search.FilterBasedLdapUserSearch;
|
||||||
import org.springframework.security.ldap.search.LdapUserSearch;
|
import org.springframework.security.ldap.search.LdapUserSearch;
|
||||||
import org.springframework.security.ldap.server.ApacheDSContainer;
|
|
||||||
import org.springframework.security.ldap.server.UnboundIdContainer;
|
import org.springframework.security.ldap.server.UnboundIdContainer;
|
||||||
import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator;
|
import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator;
|
||||||
import org.springframework.security.ldap.userdetails.InetOrgPersonContextMapper;
|
import org.springframework.security.ldap.userdetails.InetOrgPersonContextMapper;
|
||||||
|
@ -60,12 +59,8 @@ import org.springframework.util.ClassUtils;
|
||||||
public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuilder<B>>
|
public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuilder<B>>
|
||||||
extends SecurityConfigurerAdapter<AuthenticationManager, B> {
|
extends SecurityConfigurerAdapter<AuthenticationManager, B> {
|
||||||
|
|
||||||
private static final String APACHEDS_CLASSNAME = "org.apache.directory.server.core.DefaultDirectoryService";
|
|
||||||
|
|
||||||
private static final String UNBOUNDID_CLASSNAME = "com.unboundid.ldap.listener.InMemoryDirectoryServer";
|
private static final String UNBOUNDID_CLASSNAME = "com.unboundid.ldap.listener.InMemoryDirectoryServer";
|
||||||
|
|
||||||
private static final boolean apacheDsPresent;
|
|
||||||
|
|
||||||
private static final boolean unboundIdPresent;
|
private static final boolean unboundIdPresent;
|
||||||
|
|
||||||
private String groupRoleAttribute = "cn";
|
private String groupRoleAttribute = "cn";
|
||||||
|
@ -100,7 +95,6 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
|
||||||
|
|
||||||
static {
|
static {
|
||||||
ClassLoader classLoader = LdapAuthenticationProviderConfigurer.class.getClassLoader();
|
ClassLoader classLoader = LdapAuthenticationProviderConfigurer.class.getClassLoader();
|
||||||
apacheDsPresent = ClassUtils.isPresent(APACHEDS_CLASSNAME, classLoader);
|
|
||||||
unboundIdPresent = ClassUtils.isPresent(UNBOUNDID_CLASSNAME, classLoader);
|
unboundIdPresent = ClassUtils.isPresent(UNBOUNDID_CLASSNAME, classLoader);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -467,8 +461,6 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
|
||||||
*/
|
*/
|
||||||
public final class ContextSourceBuilder {
|
public final class ContextSourceBuilder {
|
||||||
|
|
||||||
private static final String APACHEDS_CLASSNAME = "org.apache.directory.server.core.DefaultDirectoryService";
|
|
||||||
|
|
||||||
private static final String UNBOUNDID_CLASSNAME = "com.unboundid.ldap.listener.InMemoryDirectoryServer";
|
private static final String UNBOUNDID_CLASSNAME = "com.unboundid.ldap.listener.InMemoryDirectoryServer";
|
||||||
|
|
||||||
private static final int DEFAULT_PORT = 33389;
|
private static final int DEFAULT_PORT = 33389;
|
||||||
|
@ -584,14 +576,8 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
|
||||||
return contextSource;
|
return contextSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startEmbeddedLdapServer() throws Exception {
|
private void startEmbeddedLdapServer() {
|
||||||
if (apacheDsPresent) {
|
if (unboundIdPresent) {
|
||||||
ApacheDSContainer apacheDsContainer = new ApacheDSContainer(this.root, this.ldif);
|
|
||||||
apacheDsContainer.setPort(getPort());
|
|
||||||
postProcess(apacheDsContainer);
|
|
||||||
this.port = apacheDsContainer.getLocalPort();
|
|
||||||
}
|
|
||||||
else if (unboundIdPresent) {
|
|
||||||
UnboundIdContainer unboundIdContainer = new UnboundIdContainer(this.root, this.ldif);
|
UnboundIdContainer unboundIdContainer = new UnboundIdContainer(this.root, this.ldif);
|
||||||
unboundIdContainer.setPort(getPort());
|
unboundIdContainer.setPort(getPort());
|
||||||
postProcess(unboundIdContainer);
|
postProcess(unboundIdContainer);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2024 the original author or authors.
|
* Copyright 2002-2025 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.
|
||||||
|
@ -16,13 +16,22 @@
|
||||||
|
|
||||||
package org.springframework.security.config.annotation.method.configuration;
|
package org.springframework.security.config.annotation.method.configuration;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.springframework.aop.framework.AopInfrastructureBean;
|
import org.springframework.aop.framework.AopInfrastructureBean;
|
||||||
import org.springframework.beans.factory.config.BeanDefinition;
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Role;
|
import org.springframework.context.annotation.Role;
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
|
import org.springframework.data.domain.PageImpl;
|
||||||
|
import org.springframework.data.domain.SliceImpl;
|
||||||
|
import org.springframework.data.geo.GeoPage;
|
||||||
|
import org.springframework.data.geo.GeoResult;
|
||||||
|
import org.springframework.data.geo.GeoResults;
|
||||||
import org.springframework.security.aot.hint.SecurityHintsRegistrar;
|
import org.springframework.security.aot.hint.SecurityHintsRegistrar;
|
||||||
import org.springframework.security.authorization.AuthorizationProxyFactory;
|
import org.springframework.security.authorization.AuthorizationProxyFactory;
|
||||||
|
import org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory;
|
||||||
import org.springframework.security.data.aot.hint.AuthorizeReturnObjectDataHintsRegistrar;
|
import org.springframework.security.data.aot.hint.AuthorizeReturnObjectDataHintsRegistrar;
|
||||||
|
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
@ -34,4 +43,45 @@ final class AuthorizationProxyDataConfiguration implements AopInfrastructureBean
|
||||||
return new AuthorizeReturnObjectDataHintsRegistrar(proxyFactory);
|
return new AuthorizeReturnObjectDataHintsRegistrar(proxyFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||||
|
DataTargetVisitor dataTargetVisitor() {
|
||||||
|
return new DataTargetVisitor();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class DataTargetVisitor implements AuthorizationAdvisorProxyFactory.TargetVisitor, Ordered {
|
||||||
|
|
||||||
|
private static final int DEFAULT_ORDER = 200;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visit(AuthorizationAdvisorProxyFactory proxyFactory, Object target) {
|
||||||
|
if (target instanceof GeoResults<?> geoResults) {
|
||||||
|
return new GeoResults<>(proxyFactory.proxy(geoResults.getContent()), geoResults.getAverageDistance());
|
||||||
|
}
|
||||||
|
if (target instanceof GeoResult<?> geoResult) {
|
||||||
|
return new GeoResult<>(proxyFactory.proxy(geoResult.getContent()), geoResult.getDistance());
|
||||||
|
}
|
||||||
|
if (target instanceof GeoPage<?> geoPage) {
|
||||||
|
GeoResults<?> results = new GeoResults<>(proxyFactory.proxy(geoPage.getContent()),
|
||||||
|
geoPage.getAverageDistance());
|
||||||
|
return new GeoPage<>(results, geoPage.getPageable(), geoPage.getTotalElements());
|
||||||
|
}
|
||||||
|
if (target instanceof PageImpl<?> page) {
|
||||||
|
List<?> content = proxyFactory.proxy(page.getContent());
|
||||||
|
return new PageImpl<>(content, page.getPageable(), page.getTotalElements());
|
||||||
|
}
|
||||||
|
if (target instanceof SliceImpl<?> slice) {
|
||||||
|
List<?> content = proxyFactory.proxy(slice.getContent());
|
||||||
|
return new SliceImpl<>(content, slice.getPageable(), slice.hasNext());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOrder() {
|
||||||
|
return DEFAULT_ORDER;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,8 +16,12 @@
|
||||||
|
|
||||||
package org.springframework.security.config.annotation.method.configuration;
|
package org.springframework.security.config.annotation.method.configuration;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import org.springframework.beans.factory.config.BeanDefinition;
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
@ -25,12 +29,17 @@ import org.springframework.context.annotation.Role;
|
||||||
import org.springframework.core.Ordered;
|
import org.springframework.core.Ordered;
|
||||||
import org.springframework.http.HttpEntity;
|
import org.springframework.http.HttpEntity;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.security.access.AccessDeniedException;
|
||||||
import org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory;
|
import org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory;
|
||||||
|
import org.springframework.security.web.util.ThrowableAnalyzer;
|
||||||
|
import org.springframework.web.servlet.HandlerExceptionResolver;
|
||||||
import org.springframework.web.servlet.ModelAndView;
|
import org.springframework.web.servlet.ModelAndView;
|
||||||
import org.springframework.web.servlet.View;
|
import org.springframework.web.servlet.View;
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
class AuthorizationProxyWebConfiguration {
|
class AuthorizationProxyWebConfiguration implements WebMvcConfigurer {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||||
|
@ -38,6 +47,18 @@ class AuthorizationProxyWebConfiguration {
|
||||||
return new WebTargetVisitor();
|
return new WebTargetVisitor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
|
||||||
|
for (int i = 0; i < resolvers.size(); i++) {
|
||||||
|
HandlerExceptionResolver resolver = resolvers.get(i);
|
||||||
|
if (resolver instanceof DefaultHandlerExceptionResolver) {
|
||||||
|
resolvers.add(i, new AccessDeniedExceptionResolver());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resolvers.add(new AccessDeniedExceptionResolver());
|
||||||
|
}
|
||||||
|
|
||||||
static class WebTargetVisitor implements AuthorizationAdvisorProxyFactory.TargetVisitor, Ordered {
|
static class WebTargetVisitor implements AuthorizationAdvisorProxyFactory.TargetVisitor, Ordered {
|
||||||
|
|
||||||
private static final int DEFAULT_ORDER = 100;
|
private static final int DEFAULT_ORDER = 100;
|
||||||
|
@ -54,7 +75,7 @@ class AuthorizationProxyWebConfiguration {
|
||||||
if (target instanceof ModelAndView mav) {
|
if (target instanceof ModelAndView mav) {
|
||||||
View view = mav.getView();
|
View view = mav.getView();
|
||||||
String viewName = mav.getViewName();
|
String viewName = mav.getViewName();
|
||||||
Map<String, Object> model = (Map<String, Object>) proxyFactory.proxy(mav.getModel());
|
Map<String, Object> model = proxyFactory.proxy(mav.getModel());
|
||||||
ModelAndView proxied = (view != null) ? new ModelAndView(view, model)
|
ModelAndView proxied = (view != null) ? new ModelAndView(view, model)
|
||||||
: new ModelAndView(viewName, model);
|
: new ModelAndView(viewName, model);
|
||||||
proxied.setStatus(mav.getStatus());
|
proxied.setStatus(mav.getStatus());
|
||||||
|
@ -70,4 +91,24 @@ class AuthorizationProxyWebConfiguration {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class AccessDeniedExceptionResolver implements HandlerExceptionResolver {
|
||||||
|
|
||||||
|
final ThrowableAnalyzer throwableAnalyzer = new ThrowableAnalyzer();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
|
||||||
|
Exception ex) {
|
||||||
|
Throwable[] causeChain = this.throwableAnalyzer.determineCauseChain(ex);
|
||||||
|
Throwable accessDeniedException = this.throwableAnalyzer
|
||||||
|
.getFirstThrowableOfType(AccessDeniedException.class, causeChain);
|
||||||
|
if (accessDeniedException != null) {
|
||||||
|
return new ModelAndView((model, req, res) -> {
|
||||||
|
throw ex;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,7 +107,7 @@ import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
|
||||||
* A {@link HttpSecurity} is similar to Spring Security's XML <http> element in the
|
* A {@link HttpSecurity} is similar to Spring Security's XML <http> element in the
|
||||||
* namespace configuration. It allows configuring web based security for specific http
|
* namespace configuration. It allows configuring web based security for specific http
|
||||||
* requests. By default it will be applied to all requests, but can be restricted using
|
* requests. By default it will be applied to all requests, but can be restricted using
|
||||||
* {@link #requestMatcher(RequestMatcher)} or other similar methods.
|
* {@link #authorizeHttpRequests(Customizer)} or other similar methods.
|
||||||
*
|
*
|
||||||
* <h2>Example Usage</h2>
|
* <h2>Example Usage</h2>
|
||||||
*
|
*
|
||||||
|
@ -124,7 +124,12 @@ import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
|
||||||
*
|
*
|
||||||
* @Bean
|
* @Bean
|
||||||
* public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
* public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||||
* http.authorizeHttpRequests().requestMatchers("/**").hasRole("USER").and().formLogin();
|
* http
|
||||||
|
* .authorizeHttpRequests((authorizeHttpRequests) ->
|
||||||
|
* authorizeHttpRequests
|
||||||
|
* .requestMatchers("/**").hasRole("USER")
|
||||||
|
* )
|
||||||
|
* .formLogin(withDefaults());
|
||||||
* return http.build();
|
* return http.build();
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2023 the original author or authors.
|
* Copyright 2002-2025 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.
|
||||||
|
@ -19,9 +19,11 @@ package org.springframework.security.config.annotation.web.configurers;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import io.micrometer.observation.ObservationRegistry;
|
import io.micrometer.observation.ObservationRegistry;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.security.access.AccessDeniedException;
|
import org.springframework.security.access.AccessDeniedException;
|
||||||
|
@ -34,13 +36,17 @@ import org.springframework.security.web.access.CompositeAccessDeniedHandler;
|
||||||
import org.springframework.security.web.access.DelegatingAccessDeniedHandler;
|
import org.springframework.security.web.access.DelegatingAccessDeniedHandler;
|
||||||
import org.springframework.security.web.access.ObservationMarkingAccessDeniedHandler;
|
import org.springframework.security.web.access.ObservationMarkingAccessDeniedHandler;
|
||||||
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
|
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
|
||||||
|
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
|
||||||
import org.springframework.security.web.csrf.CsrfAuthenticationStrategy;
|
import org.springframework.security.web.csrf.CsrfAuthenticationStrategy;
|
||||||
import org.springframework.security.web.csrf.CsrfFilter;
|
import org.springframework.security.web.csrf.CsrfFilter;
|
||||||
import org.springframework.security.web.csrf.CsrfLogoutHandler;
|
import org.springframework.security.web.csrf.CsrfLogoutHandler;
|
||||||
|
import org.springframework.security.web.csrf.CsrfToken;
|
||||||
import org.springframework.security.web.csrf.CsrfTokenRepository;
|
import org.springframework.security.web.csrf.CsrfTokenRepository;
|
||||||
|
import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler;
|
||||||
import org.springframework.security.web.csrf.CsrfTokenRequestHandler;
|
import org.springframework.security.web.csrf.CsrfTokenRequestHandler;
|
||||||
import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;
|
import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;
|
||||||
import org.springframework.security.web.csrf.MissingCsrfTokenException;
|
import org.springframework.security.web.csrf.MissingCsrfTokenException;
|
||||||
|
import org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler;
|
||||||
import org.springframework.security.web.session.InvalidSessionAccessDeniedHandler;
|
import org.springframework.security.web.session.InvalidSessionAccessDeniedHandler;
|
||||||
import org.springframework.security.web.session.InvalidSessionStrategy;
|
import org.springframework.security.web.session.InvalidSessionStrategy;
|
||||||
import org.springframework.security.web.util.matcher.AndRequestMatcher;
|
import org.springframework.security.web.util.matcher.AndRequestMatcher;
|
||||||
|
@ -48,6 +54,7 @@ import org.springframework.security.web.util.matcher.NegatedRequestMatcher;
|
||||||
import org.springframework.security.web.util.matcher.OrRequestMatcher;
|
import org.springframework.security.web.util.matcher.OrRequestMatcher;
|
||||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds
|
* Adds
|
||||||
|
@ -214,6 +221,21 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Sensible CSRF defaults when used in combination with a single page application.
|
||||||
|
* Creates a cookie-based token repository and a custom request handler to resolve the
|
||||||
|
* actual token value instead of the encoded token.
|
||||||
|
* </p>
|
||||||
|
* @return the {@link CsrfConfigurer} for further customizations
|
||||||
|
* @since 7.0
|
||||||
|
*/
|
||||||
|
public CsrfConfigurer<H> spa() {
|
||||||
|
this.csrfTokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse();
|
||||||
|
this.requestHandler = new SpaCsrfTokenRequestHandler();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Override
|
@Override
|
||||||
public void configure(H http) {
|
public void configure(H http) {
|
||||||
|
@ -375,4 +397,27 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final class SpaCsrfTokenRequestHandler implements CsrfTokenRequestHandler {
|
||||||
|
|
||||||
|
private final CsrfTokenRequestAttributeHandler plain = new CsrfTokenRequestAttributeHandler();
|
||||||
|
|
||||||
|
private final CsrfTokenRequestAttributeHandler xor = new XorCsrfTokenRequestAttributeHandler();
|
||||||
|
|
||||||
|
SpaCsrfTokenRequestHandler() {
|
||||||
|
this.xor.setCsrfRequestAttributeName(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(HttpServletRequest request, HttpServletResponse response, Supplier<CsrfToken> csrfToken) {
|
||||||
|
this.xor.handle(request, response, csrfToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String resolveCsrfTokenValue(HttpServletRequest request, CsrfToken csrfToken) {
|
||||||
|
String headerValue = request.getHeader(csrfToken.getHeaderName());
|
||||||
|
return (StringUtils.hasText(headerValue) ? this.plain : this.xor).resolveCsrfTokenValue(request, csrfToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2024 the original author or authors.
|
* Copyright 2002-2025 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.
|
||||||
|
@ -161,7 +161,10 @@ public final class X509Configurer<H extends HttpSecurityBuilder<H>>
|
||||||
* @param subjectPrincipalRegex the regex to extract the user principal from the
|
* @param subjectPrincipalRegex the regex to extract the user principal from the
|
||||||
* certificate (i.e. "CN=(.*?)(?:,|$)").
|
* certificate (i.e. "CN=(.*?)(?:,|$)").
|
||||||
* @return the {@link X509Configurer} for further customizations
|
* @return the {@link X509Configurer} for further customizations
|
||||||
|
* @deprecated Please use {{@link #x509PrincipalExtractor(X509PrincipalExtractor)}
|
||||||
|
* instead
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public X509Configurer<H> subjectPrincipalRegex(String subjectPrincipalRegex) {
|
public X509Configurer<H> subjectPrincipalRegex(String subjectPrincipalRegex) {
|
||||||
SubjectDnX509PrincipalExtractor principalExtractor = new SubjectDnX509PrincipalExtractor();
|
SubjectDnX509PrincipalExtractor principalExtractor = new SubjectDnX509PrincipalExtractor();
|
||||||
principalExtractor.setSubjectDnRegex(subjectPrincipalRegex);
|
principalExtractor.setSubjectDnRegex(subjectPrincipalRegex);
|
||||||
|
|
|
@ -18,11 +18,6 @@ package org.springframework.security.config.annotation.web.configurers.oauth2.cl
|
||||||
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import com.nimbusds.jose.JOSEObjectType;
|
|
||||||
import com.nimbusds.jose.proc.DefaultJOSEObjectTypeVerifier;
|
|
||||||
import com.nimbusds.jose.proc.JOSEObjectTypeVerifier;
|
|
||||||
import com.nimbusds.jose.proc.SecurityContext;
|
|
||||||
|
|
||||||
import org.springframework.security.authentication.AuthenticationProvider;
|
import org.springframework.security.authentication.AuthenticationProvider;
|
||||||
import org.springframework.security.authentication.AuthenticationServiceException;
|
import org.springframework.security.authentication.AuthenticationServiceException;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
|
@ -38,6 +33,7 @@ import org.springframework.security.oauth2.jwt.BadJwtException;
|
||||||
import org.springframework.security.oauth2.jwt.Jwt;
|
import org.springframework.security.oauth2.jwt.Jwt;
|
||||||
import org.springframework.security.oauth2.jwt.JwtDecoder;
|
import org.springframework.security.oauth2.jwt.JwtDecoder;
|
||||||
import org.springframework.security.oauth2.jwt.JwtDecoderFactory;
|
import org.springframework.security.oauth2.jwt.JwtDecoderFactory;
|
||||||
|
import org.springframework.security.oauth2.jwt.JwtTypeValidator;
|
||||||
import org.springframework.security.oauth2.jwt.JwtValidators;
|
import org.springframework.security.oauth2.jwt.JwtValidators;
|
||||||
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
|
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
@ -67,8 +63,10 @@ final class OidcBackChannelLogoutAuthenticationProvider implements Authenticatio
|
||||||
* Construct an {@link OidcBackChannelLogoutAuthenticationProvider}
|
* Construct an {@link OidcBackChannelLogoutAuthenticationProvider}
|
||||||
*/
|
*/
|
||||||
OidcBackChannelLogoutAuthenticationProvider() {
|
OidcBackChannelLogoutAuthenticationProvider() {
|
||||||
|
JwtTypeValidator type = new JwtTypeValidator("JWT", "logout+jwt");
|
||||||
|
type.setAllowEmpty(true);
|
||||||
Function<ClientRegistration, OAuth2TokenValidator<Jwt>> jwtValidator = (clientRegistration) -> JwtValidators
|
Function<ClientRegistration, OAuth2TokenValidator<Jwt>> jwtValidator = (clientRegistration) -> JwtValidators
|
||||||
.createDefaultWithValidators(new OidcBackChannelLogoutTokenValidator(clientRegistration));
|
.createDefaultWithValidators(type, new OidcBackChannelLogoutTokenValidator(clientRegistration));
|
||||||
this.logoutTokenDecoderFactory = (clientRegistration) -> {
|
this.logoutTokenDecoderFactory = (clientRegistration) -> {
|
||||||
String jwkSetUri = clientRegistration.getProviderDetails().getJwkSetUri();
|
String jwkSetUri = clientRegistration.getProviderDetails().getJwkSetUri();
|
||||||
if (!StringUtils.hasText(jwkSetUri)) {
|
if (!StringUtils.hasText(jwkSetUri)) {
|
||||||
|
@ -79,11 +77,7 @@ final class OidcBackChannelLogoutAuthenticationProvider implements Authenticatio
|
||||||
null);
|
null);
|
||||||
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
|
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
|
||||||
}
|
}
|
||||||
JOSEObjectTypeVerifier<SecurityContext> typeVerifier = new DefaultJOSEObjectTypeVerifier<>(null,
|
NimbusJwtDecoder decoder = NimbusJwtDecoder.withJwkSetUri(jwkSetUri).build();
|
||||||
JOSEObjectType.JWT, new JOSEObjectType("logout+jwt"));
|
|
||||||
NimbusJwtDecoder decoder = NimbusJwtDecoder.withJwkSetUri(jwkSetUri)
|
|
||||||
.jwtProcessorCustomizer((processor) -> processor.setJWSTypeVerifier(typeVerifier))
|
|
||||||
.build();
|
|
||||||
decoder.setJwtValidator(jwtValidator.apply(clientRegistration));
|
decoder.setJwtValidator(jwtValidator.apply(clientRegistration));
|
||||||
decoder.setClaimSetConverter(OidcIdTokenDecoderFactory.createDefaultClaimTypeConverter());
|
decoder.setClaimSetConverter(OidcIdTokenDecoderFactory.createDefaultClaimTypeConverter());
|
||||||
return decoder;
|
return decoder;
|
||||||
|
|
|
@ -37,6 +37,7 @@ import org.springframework.security.config.annotation.web.configurers.AbstractHt
|
||||||
import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer;
|
import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer;
|
||||||
import org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer;
|
import org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer;
|
||||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||||
import org.springframework.security.oauth2.jwt.Jwt;
|
import org.springframework.security.oauth2.jwt.Jwt;
|
||||||
import org.springframework.security.oauth2.jwt.JwtDecoder;
|
import org.springframework.security.oauth2.jwt.JwtDecoder;
|
||||||
|
@ -49,13 +50,14 @@ import org.springframework.security.oauth2.server.resource.introspection.OpaqueT
|
||||||
import org.springframework.security.oauth2.server.resource.introspection.SpringOpaqueTokenIntrospector;
|
import org.springframework.security.oauth2.server.resource.introspection.SpringOpaqueTokenIntrospector;
|
||||||
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;
|
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;
|
||||||
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
|
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
|
||||||
import org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver;
|
|
||||||
import org.springframework.security.oauth2.server.resource.web.access.BearerTokenAccessDeniedHandler;
|
import org.springframework.security.oauth2.server.resource.web.access.BearerTokenAccessDeniedHandler;
|
||||||
|
import org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationConverter;
|
||||||
import org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter;
|
import org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter;
|
||||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||||
import org.springframework.security.web.access.AccessDeniedHandler;
|
import org.springframework.security.web.access.AccessDeniedHandler;
|
||||||
import org.springframework.security.web.access.AccessDeniedHandlerImpl;
|
import org.springframework.security.web.access.AccessDeniedHandlerImpl;
|
||||||
import org.springframework.security.web.access.DelegatingAccessDeniedHandler;
|
import org.springframework.security.web.access.DelegatingAccessDeniedHandler;
|
||||||
|
import org.springframework.security.web.authentication.AuthenticationConverter;
|
||||||
import org.springframework.security.web.csrf.CsrfException;
|
import org.springframework.security.web.csrf.CsrfException;
|
||||||
import org.springframework.security.web.util.matcher.AndRequestMatcher;
|
import org.springframework.security.web.util.matcher.AndRequestMatcher;
|
||||||
import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;
|
import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;
|
||||||
|
@ -156,7 +158,7 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
|
||||||
|
|
||||||
private AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver;
|
private AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver;
|
||||||
|
|
||||||
private BearerTokenResolver bearerTokenResolver;
|
private AuthenticationConverter authenticationConverter;
|
||||||
|
|
||||||
private JwtConfigurer jwtConfigurer;
|
private JwtConfigurer jwtConfigurer;
|
||||||
|
|
||||||
|
@ -196,7 +198,19 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
|
||||||
|
|
||||||
public OAuth2ResourceServerConfigurer<H> bearerTokenResolver(BearerTokenResolver bearerTokenResolver) {
|
public OAuth2ResourceServerConfigurer<H> bearerTokenResolver(BearerTokenResolver bearerTokenResolver) {
|
||||||
Assert.notNull(bearerTokenResolver, "bearerTokenResolver cannot be null");
|
Assert.notNull(bearerTokenResolver, "bearerTokenResolver cannot be null");
|
||||||
this.bearerTokenResolver = bearerTokenResolver;
|
this.authenticationConverter = new BearerTokenResolverHoldingAuthenticationConverter(bearerTokenResolver);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@link AuthenticationConverter} to use.
|
||||||
|
* @param authenticationConverter the authentication converter
|
||||||
|
* @return the {@link OAuth2ResourceServerConfigurer} for further configuration
|
||||||
|
* @since 7.0
|
||||||
|
*/
|
||||||
|
public OAuth2ResourceServerConfigurer<H> authenticationConverter(AuthenticationConverter authenticationConverter) {
|
||||||
|
Assert.notNull(authenticationConverter, "authenticationConverter cannot be null");
|
||||||
|
this.authenticationConverter = authenticationConverter;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -271,16 +285,15 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void configure(H http) {
|
public void configure(H http) {
|
||||||
BearerTokenResolver bearerTokenResolver = getBearerTokenResolver();
|
|
||||||
this.requestMatcher.setBearerTokenResolver(bearerTokenResolver);
|
|
||||||
AuthenticationManagerResolver resolver = this.authenticationManagerResolver;
|
AuthenticationManagerResolver resolver = this.authenticationManagerResolver;
|
||||||
if (resolver == null) {
|
if (resolver == null) {
|
||||||
AuthenticationManager authenticationManager = getAuthenticationManager(http);
|
AuthenticationManager authenticationManager = getAuthenticationManager(http);
|
||||||
resolver = (request) -> authenticationManager;
|
resolver = (request) -> authenticationManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
BearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(resolver);
|
AuthenticationConverter converter = getAuthenticationConverter();
|
||||||
filter.setBearerTokenResolver(bearerTokenResolver);
|
this.requestMatcher.setAuthenticationConverter(converter);
|
||||||
|
BearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(resolver, converter);
|
||||||
filter.setAuthenticationEntryPoint(this.authenticationEntryPoint);
|
filter.setAuthenticationEntryPoint(this.authenticationEntryPoint);
|
||||||
filter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
|
filter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
|
||||||
filter = postProcess(filter);
|
filter = postProcess(filter);
|
||||||
|
@ -367,16 +380,29 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
|
||||||
return this.authenticationManagerResolver;
|
return this.authenticationManagerResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
BearerTokenResolver getBearerTokenResolver() {
|
AuthenticationConverter getAuthenticationConverter() {
|
||||||
if (this.bearerTokenResolver == null) {
|
if (this.authenticationConverter != null) {
|
||||||
if (this.context.getBeanNamesForType(BearerTokenResolver.class).length > 0) {
|
return this.authenticationConverter;
|
||||||
this.bearerTokenResolver = this.context.getBean(BearerTokenResolver.class);
|
}
|
||||||
|
if (this.context.getBeanNamesForType(AuthenticationConverter.class).length > 0) {
|
||||||
|
this.authenticationConverter = this.context.getBean(AuthenticationConverter.class);
|
||||||
|
}
|
||||||
|
else if (this.context.getBeanNamesForType(BearerTokenResolver.class).length > 0) {
|
||||||
|
BearerTokenResolver bearerTokenResolver = this.context.getBean(BearerTokenResolver.class);
|
||||||
|
this.authenticationConverter = new BearerTokenResolverHoldingAuthenticationConverter(bearerTokenResolver);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.bearerTokenResolver = new DefaultBearerTokenResolver();
|
this.authenticationConverter = new BearerTokenAuthenticationConverter();
|
||||||
}
|
}
|
||||||
|
return this.authenticationConverter;
|
||||||
}
|
}
|
||||||
return this.bearerTokenResolver;
|
|
||||||
|
BearerTokenResolver getBearerTokenResolver() {
|
||||||
|
AuthenticationConverter authenticationConverter = getAuthenticationConverter();
|
||||||
|
if (authenticationConverter instanceof OAuth2ResourceServerConfigurer.BearerTokenResolverHoldingAuthenticationConverter bearer) {
|
||||||
|
return bearer.bearerTokenResolver;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class JwtConfigurer {
|
public class JwtConfigurer {
|
||||||
|
@ -564,21 +590,41 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
|
||||||
|
|
||||||
private static final class BearerTokenRequestMatcher implements RequestMatcher {
|
private static final class BearerTokenRequestMatcher implements RequestMatcher {
|
||||||
|
|
||||||
private BearerTokenResolver bearerTokenResolver;
|
private AuthenticationConverter authenticationConverter;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean matches(HttpServletRequest request) {
|
public boolean matches(HttpServletRequest request) {
|
||||||
try {
|
try {
|
||||||
return this.bearerTokenResolver.resolve(request) != null;
|
return this.authenticationConverter.convert(request) != null;
|
||||||
}
|
}
|
||||||
catch (OAuth2AuthenticationException ex) {
|
catch (OAuth2AuthenticationException ex) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void setBearerTokenResolver(BearerTokenResolver tokenResolver) {
|
void setAuthenticationConverter(AuthenticationConverter authenticationConverter) {
|
||||||
Assert.notNull(tokenResolver, "resolver cannot be null");
|
Assert.notNull(authenticationConverter, "authenticationConverter cannot be null");
|
||||||
this.bearerTokenResolver = tokenResolver;
|
this.authenticationConverter = authenticationConverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class BearerTokenResolverHoldingAuthenticationConverter implements AuthenticationConverter {
|
||||||
|
|
||||||
|
private final BearerTokenResolver bearerTokenResolver;
|
||||||
|
|
||||||
|
private final AuthenticationConverter authenticationConverter;
|
||||||
|
|
||||||
|
BearerTokenResolverHoldingAuthenticationConverter(BearerTokenResolver bearerTokenResolver) {
|
||||||
|
this.bearerTokenResolver = bearerTokenResolver;
|
||||||
|
BearerTokenAuthenticationConverter authenticationConverter = new BearerTokenAuthenticationConverter();
|
||||||
|
authenticationConverter.setBearerTokenResolver(bearerTokenResolver);
|
||||||
|
this.authenticationConverter = authenticationConverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Authentication convert(HttpServletRequest request) {
|
||||||
|
return this.authenticationConverter.convert(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,8 @@ import org.springframework.security.config.annotation.web.configurers.LogoutConf
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
||||||
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;
|
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;
|
||||||
|
import org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;
|
||||||
|
import org.springframework.security.saml2.provider.service.authentication.Saml2ResponseAssertionAccessor;
|
||||||
import org.springframework.security.saml2.provider.service.authentication.logout.OpenSaml4LogoutRequestValidator;
|
import org.springframework.security.saml2.provider.service.authentication.logout.OpenSaml4LogoutRequestValidator;
|
||||||
import org.springframework.security.saml2.provider.service.authentication.logout.OpenSaml4LogoutResponseValidator;
|
import org.springframework.security.saml2.provider.service.authentication.logout.OpenSaml4LogoutResponseValidator;
|
||||||
import org.springframework.security.saml2.provider.service.authentication.logout.OpenSaml5LogoutRequestValidator;
|
import org.springframework.security.saml2.provider.service.authentication.logout.OpenSaml5LogoutRequestValidator;
|
||||||
|
@ -534,7 +536,13 @@ public final class Saml2LogoutConfigurer<H extends HttpSecurityBuilder<H>>
|
||||||
if (authentication == null) {
|
if (authentication == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return authentication.getPrincipal() instanceof Saml2AuthenticatedPrincipal;
|
if (authentication.getPrincipal() instanceof Saml2AuthenticatedPrincipal) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (authentication.getCredentials() instanceof Saml2ResponseAssertionAccessor) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return authentication instanceof Saml2Authentication;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -521,11 +521,23 @@ final class AuthenticationConfigBuilder {
|
||||||
filterBuilder.addPropertyValue("authenticationManager", authManager);
|
filterBuilder.addPropertyValue("authenticationManager", authManager);
|
||||||
filterBuilder.addPropertyValue("securityContextHolderStrategy",
|
filterBuilder.addPropertyValue("securityContextHolderStrategy",
|
||||||
authenticationFilterSecurityContextHolderStrategyRef);
|
authenticationFilterSecurityContextHolderStrategyRef);
|
||||||
String regex = x509Elt.getAttribute("subject-principal-regex");
|
String principalExtractorRef = x509Elt.getAttribute("principal-extractor-ref");
|
||||||
if (StringUtils.hasText(regex)) {
|
String subjectPrincipalRegex = x509Elt.getAttribute("subject-principal-regex");
|
||||||
|
boolean hasPrincipalExtractorRef = StringUtils.hasText(principalExtractorRef);
|
||||||
|
boolean hasSubjectPrincipalRegex = StringUtils.hasText(subjectPrincipalRegex);
|
||||||
|
if (hasPrincipalExtractorRef && hasSubjectPrincipalRegex) {
|
||||||
|
this.pc.getReaderContext()
|
||||||
|
.error("The attribute 'principal-extractor-ref' cannot be used together with the 'subject-principal-regex' attribute within <"
|
||||||
|
+ Elements.X509 + ">", this.pc.extractSource(x509Elt));
|
||||||
|
}
|
||||||
|
if (hasPrincipalExtractorRef) {
|
||||||
|
RuntimeBeanReference principalExtractor = new RuntimeBeanReference(principalExtractorRef);
|
||||||
|
filterBuilder.addPropertyValue("principalExtractor", principalExtractor);
|
||||||
|
}
|
||||||
|
if (hasSubjectPrincipalRegex) {
|
||||||
BeanDefinitionBuilder extractor = BeanDefinitionBuilder
|
BeanDefinitionBuilder extractor = BeanDefinitionBuilder
|
||||||
.rootBeanDefinition(SubjectDnX509PrincipalExtractor.class);
|
.rootBeanDefinition(SubjectDnX509PrincipalExtractor.class);
|
||||||
extractor.addPropertyValue("subjectDnRegex", regex);
|
extractor.addPropertyValue("subjectDnRegex", subjectPrincipalRegex);
|
||||||
filterBuilder.addPropertyValue("principalExtractor", extractor.getBeanDefinition());
|
filterBuilder.addPropertyValue("principalExtractor", extractor.getBeanDefinition());
|
||||||
}
|
}
|
||||||
injectAuthenticationDetailsSource(x509Elt, filterBuilder);
|
injectAuthenticationDetailsSource(x509Elt, filterBuilder);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2022 the original author or authors.
|
* Copyright 2002-2025 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.
|
||||||
|
@ -23,6 +23,7 @@ import jakarta.servlet.http.HttpServletRequest;
|
||||||
import org.w3c.dom.Element;
|
import org.w3c.dom.Element;
|
||||||
|
|
||||||
import org.springframework.beans.BeanMetadataElement;
|
import org.springframework.beans.BeanMetadataElement;
|
||||||
|
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||||
import org.springframework.beans.factory.FactoryBean;
|
import org.springframework.beans.factory.FactoryBean;
|
||||||
import org.springframework.beans.factory.config.BeanDefinition;
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
import org.springframework.beans.factory.config.BeanReference;
|
import org.springframework.beans.factory.config.BeanReference;
|
||||||
|
@ -43,9 +44,10 @@ import org.springframework.security.oauth2.server.resource.authentication.Opaque
|
||||||
import org.springframework.security.oauth2.server.resource.introspection.SpringOpaqueTokenIntrospector;
|
import org.springframework.security.oauth2.server.resource.introspection.SpringOpaqueTokenIntrospector;
|
||||||
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;
|
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;
|
||||||
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
|
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
|
||||||
import org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver;
|
|
||||||
import org.springframework.security.oauth2.server.resource.web.access.BearerTokenAccessDeniedHandler;
|
import org.springframework.security.oauth2.server.resource.web.access.BearerTokenAccessDeniedHandler;
|
||||||
|
import org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationConverter;
|
||||||
import org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter;
|
import org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter;
|
||||||
|
import org.springframework.security.web.authentication.AuthenticationConverter;
|
||||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
@ -64,6 +66,8 @@ final class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionPa
|
||||||
|
|
||||||
static final String BEARER_TOKEN_RESOLVER_REF = "bearer-token-resolver-ref";
|
static final String BEARER_TOKEN_RESOLVER_REF = "bearer-token-resolver-ref";
|
||||||
|
|
||||||
|
static final String AUTHENTICATION_CONVERTER_REF = "authentication-converter-ref";
|
||||||
|
|
||||||
static final String ENTRY_POINT_REF = "entry-point-ref";
|
static final String ENTRY_POINT_REF = "entry-point-ref";
|
||||||
|
|
||||||
static final String BEARER_TOKEN_RESOLVER = "bearerTokenResolver";
|
static final String BEARER_TOKEN_RESOLVER = "bearerTokenResolver";
|
||||||
|
@ -124,11 +128,16 @@ final class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionPa
|
||||||
pc.getReaderContext().registerWithGeneratedName(opaqueTokenAuthenticationProvider)));
|
pc.getReaderContext().registerWithGeneratedName(opaqueTokenAuthenticationProvider)));
|
||||||
}
|
}
|
||||||
BeanMetadataElement bearerTokenResolver = getBearerTokenResolver(oauth2ResourceServer);
|
BeanMetadataElement bearerTokenResolver = getBearerTokenResolver(oauth2ResourceServer);
|
||||||
BeanDefinitionBuilder requestMatcherBuilder = BeanDefinitionBuilder
|
BeanMetadataElement authenticationConverter = getAuthenticationConverter(oauth2ResourceServer);
|
||||||
.rootBeanDefinition(BearerTokenRequestMatcher.class);
|
if (bearerTokenResolver != null && authenticationConverter != null) {
|
||||||
requestMatcherBuilder.addConstructorArgValue(bearerTokenResolver);
|
throw new BeanDefinitionStoreException(
|
||||||
BeanDefinition requestMatcher = requestMatcherBuilder.getBeanDefinition();
|
"You cannot use bearer-token-ref and authentication-converter-ref in the same oauth2-resource-server element");
|
||||||
|
}
|
||||||
|
if (bearerTokenResolver == null && authenticationConverter == null) {
|
||||||
|
authenticationConverter = new RootBeanDefinition(BearerTokenAuthenticationConverter.class);
|
||||||
|
}
|
||||||
BeanMetadataElement authenticationEntryPoint = getEntryPoint(oauth2ResourceServer);
|
BeanMetadataElement authenticationEntryPoint = getEntryPoint(oauth2ResourceServer);
|
||||||
|
BeanDefinition requestMatcher = buildRequestMatcher(bearerTokenResolver, authenticationConverter);
|
||||||
this.entryPoints.put(requestMatcher, authenticationEntryPoint);
|
this.entryPoints.put(requestMatcher, authenticationEntryPoint);
|
||||||
this.deniedHandlers.put(requestMatcher, this.accessDeniedHandler);
|
this.deniedHandlers.put(requestMatcher, this.accessDeniedHandler);
|
||||||
this.ignoreCsrfRequestMatchers.add(requestMatcher);
|
this.ignoreCsrfRequestMatchers.add(requestMatcher);
|
||||||
|
@ -136,13 +145,35 @@ final class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionPa
|
||||||
.rootBeanDefinition(BearerTokenAuthenticationFilter.class);
|
.rootBeanDefinition(BearerTokenAuthenticationFilter.class);
|
||||||
BeanMetadataElement authenticationManagerResolver = getAuthenticationManagerResolver(oauth2ResourceServer);
|
BeanMetadataElement authenticationManagerResolver = getAuthenticationManagerResolver(oauth2ResourceServer);
|
||||||
filterBuilder.addConstructorArgValue(authenticationManagerResolver);
|
filterBuilder.addConstructorArgValue(authenticationManagerResolver);
|
||||||
filterBuilder.addPropertyValue(BEARER_TOKEN_RESOLVER, bearerTokenResolver);
|
|
||||||
filterBuilder.addPropertyValue(AUTHENTICATION_ENTRY_POINT, authenticationEntryPoint);
|
filterBuilder.addPropertyValue(AUTHENTICATION_ENTRY_POINT, authenticationEntryPoint);
|
||||||
filterBuilder.addPropertyValue("securityContextHolderStrategy",
|
filterBuilder.addPropertyValue("securityContextHolderStrategy",
|
||||||
this.authenticationFilterSecurityContextHolderStrategy);
|
this.authenticationFilterSecurityContextHolderStrategy);
|
||||||
|
|
||||||
|
if (authenticationConverter != null) {
|
||||||
|
filterBuilder.addConstructorArgValue(authenticationConverter);
|
||||||
|
}
|
||||||
|
if (bearerTokenResolver != null) {
|
||||||
|
filterBuilder.addPropertyValue(BEARER_TOKEN_RESOLVER, bearerTokenResolver);
|
||||||
|
}
|
||||||
return filterBuilder.getBeanDefinition();
|
return filterBuilder.getBeanDefinition();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private BeanDefinition buildRequestMatcher(BeanMetadataElement bearerTokenResolver,
|
||||||
|
BeanMetadataElement authenticationConverter) {
|
||||||
|
if (bearerTokenResolver != null) {
|
||||||
|
BeanDefinitionBuilder requestMatcherBuilder = BeanDefinitionBuilder
|
||||||
|
.rootBeanDefinition(BearerTokenRequestMatcher.class);
|
||||||
|
requestMatcherBuilder.addConstructorArgValue(bearerTokenResolver);
|
||||||
|
return requestMatcherBuilder.getBeanDefinition();
|
||||||
|
}
|
||||||
|
BeanDefinitionBuilder requestMatcherBuilder = BeanDefinitionBuilder
|
||||||
|
.rootBeanDefinition(BearerTokenAuthenticationRequestMatcher.class);
|
||||||
|
if (authenticationConverter != null) {
|
||||||
|
requestMatcherBuilder.addConstructorArgValue(authenticationConverter);
|
||||||
|
}
|
||||||
|
return requestMatcherBuilder.getBeanDefinition();
|
||||||
|
}
|
||||||
|
|
||||||
void validateConfiguration(Element oauth2ResourceServer, Element jwt, Element opaqueToken, ParserContext pc) {
|
void validateConfiguration(Element oauth2ResourceServer, Element jwt, Element opaqueToken, ParserContext pc) {
|
||||||
if (!oauth2ResourceServer.hasAttribute(AUTHENTICATION_MANAGER_RESOLVER_REF)) {
|
if (!oauth2ResourceServer.hasAttribute(AUTHENTICATION_MANAGER_RESOLVER_REF)) {
|
||||||
if (jwt == null && opaqueToken == null) {
|
if (jwt == null && opaqueToken == null) {
|
||||||
|
@ -178,11 +209,19 @@ final class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionPa
|
||||||
BeanMetadataElement getBearerTokenResolver(Element element) {
|
BeanMetadataElement getBearerTokenResolver(Element element) {
|
||||||
String bearerTokenResolverRef = element.getAttribute(BEARER_TOKEN_RESOLVER_REF);
|
String bearerTokenResolverRef = element.getAttribute(BEARER_TOKEN_RESOLVER_REF);
|
||||||
if (!StringUtils.hasLength(bearerTokenResolverRef)) {
|
if (!StringUtils.hasLength(bearerTokenResolverRef)) {
|
||||||
return new RootBeanDefinition(DefaultBearerTokenResolver.class);
|
return null;
|
||||||
}
|
}
|
||||||
return new RuntimeBeanReference(bearerTokenResolverRef);
|
return new RuntimeBeanReference(bearerTokenResolverRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BeanMetadataElement getAuthenticationConverter(Element element) {
|
||||||
|
String authenticationConverterRef = element.getAttribute(AUTHENTICATION_CONVERTER_REF);
|
||||||
|
if (!StringUtils.hasLength(authenticationConverterRef)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new RuntimeBeanReference(authenticationConverterRef);
|
||||||
|
}
|
||||||
|
|
||||||
BeanMetadataElement getEntryPoint(Element element) {
|
BeanMetadataElement getEntryPoint(Element element) {
|
||||||
String entryPointRef = element.getAttribute(ENTRY_POINT_REF);
|
String entryPointRef = element.getAttribute(ENTRY_POINT_REF);
|
||||||
if (!StringUtils.hasLength(entryPointRef)) {
|
if (!StringUtils.hasLength(entryPointRef)) {
|
||||||
|
@ -366,4 +405,29 @@ final class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionPa
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static final class BearerTokenAuthenticationRequestMatcher implements RequestMatcher {
|
||||||
|
|
||||||
|
private final AuthenticationConverter authenticationConverter;
|
||||||
|
|
||||||
|
BearerTokenAuthenticationRequestMatcher() {
|
||||||
|
this.authenticationConverter = new BearerTokenAuthenticationConverter();
|
||||||
|
}
|
||||||
|
|
||||||
|
BearerTokenAuthenticationRequestMatcher(AuthenticationConverter authenticationConverter) {
|
||||||
|
Assert.notNull(authenticationConverter, "authenticationConverter cannot be null");
|
||||||
|
this.authenticationConverter = authenticationConverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(HttpServletRequest request) {
|
||||||
|
try {
|
||||||
|
return this.authenticationConverter.convert(request) != null;
|
||||||
|
}
|
||||||
|
catch (OAuth2AuthenticationException ex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,8 @@ import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
||||||
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;
|
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;
|
||||||
|
import org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;
|
||||||
|
import org.springframework.security.saml2.provider.service.authentication.Saml2ResponseAssertionAccessor;
|
||||||
import org.springframework.security.saml2.provider.service.web.DefaultRelyingPartyRegistrationResolver;
|
import org.springframework.security.saml2.provider.service.web.DefaultRelyingPartyRegistrationResolver;
|
||||||
import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestFilter;
|
import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestFilter;
|
||||||
import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutResponseFilter;
|
import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutResponseFilter;
|
||||||
|
@ -239,7 +241,13 @@ final class Saml2LogoutBeanDefinitionParser implements BeanDefinitionParser {
|
||||||
if (authentication == null) {
|
if (authentication == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return authentication.getPrincipal() instanceof Saml2AuthenticatedPrincipal;
|
if (authentication.getPrincipal() instanceof Saml2AuthenticatedPrincipal) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (authentication.getCredentials() instanceof Saml2ResponseAssertionAccessor) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return authentication instanceof Saml2Authentication;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
|
public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
|
||||||
|
|
|
@ -32,7 +32,6 @@ import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.ApplicationContextAware;
|
import org.springframework.context.ApplicationContextAware;
|
||||||
import org.springframework.security.config.BeanIds;
|
import org.springframework.security.config.BeanIds;
|
||||||
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
|
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
|
||||||
import org.springframework.security.ldap.server.ApacheDSContainer;
|
|
||||||
import org.springframework.security.ldap.server.UnboundIdContainer;
|
import org.springframework.security.ldap.server.UnboundIdContainer;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
@ -47,7 +46,7 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
|
||||||
private static final String CONTEXT_SOURCE_CLASS = "org.springframework.security.ldap.DefaultSpringSecurityContextSource";
|
private static final String CONTEXT_SOURCE_CLASS = "org.springframework.security.ldap.DefaultSpringSecurityContextSource";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the Url of the ldap server to use. If not specified, an embedded apache DS
|
* Defines the Url of the ldap server to use. If not specified, an embedded UnboundID
|
||||||
* instance will be created
|
* instance will be created
|
||||||
*/
|
*/
|
||||||
private static final String ATT_URL = "url";
|
private static final String ATT_URL = "url";
|
||||||
|
@ -78,22 +77,15 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
|
||||||
|
|
||||||
private static final int DEFAULT_PORT = 33389;
|
private static final int DEFAULT_PORT = 33389;
|
||||||
|
|
||||||
private static final String APACHEDS_CLASSNAME = "org.apache.directory.server.core.DefaultDirectoryService";
|
|
||||||
|
|
||||||
private static final String UNBOUNID_CLASSNAME = "com.unboundid.ldap.listener.InMemoryDirectoryServer";
|
private static final String UNBOUNID_CLASSNAME = "com.unboundid.ldap.listener.InMemoryDirectoryServer";
|
||||||
|
|
||||||
private static final String APACHEDS_CONTAINER_CLASSNAME = "org.springframework.security.ldap.server.ApacheDSContainer";
|
|
||||||
|
|
||||||
private static final String UNBOUNDID_CONTAINER_CLASSNAME = "org.springframework.security.ldap.server.UnboundIdContainer";
|
private static final String UNBOUNDID_CONTAINER_CLASSNAME = "org.springframework.security.ldap.server.UnboundIdContainer";
|
||||||
|
|
||||||
private static final boolean unboundIdPresent;
|
private static final boolean unboundIdPresent;
|
||||||
|
|
||||||
private static final boolean apacheDsPresent;
|
|
||||||
|
|
||||||
static {
|
static {
|
||||||
ClassLoader classLoader = LdapServerBeanDefinitionParser.class.getClassLoader();
|
ClassLoader classLoader = LdapServerBeanDefinitionParser.class.getClassLoader();
|
||||||
unboundIdPresent = ClassUtils.isPresent(UNBOUNID_CLASSNAME, classLoader);
|
unboundIdPresent = ClassUtils.isPresent(UNBOUNID_CLASSNAME, classLoader);
|
||||||
apacheDsPresent = ClassUtils.isPresent(APACHEDS_CLASSNAME, classLoader);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -128,10 +120,9 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
|
||||||
/**
|
/**
|
||||||
* Will be called if no url attribute is supplied.
|
* Will be called if no url attribute is supplied.
|
||||||
*
|
*
|
||||||
* Registers beans to create an embedded apache directory server.
|
* Registers beans to create an embedded UnboundID Server.
|
||||||
* @return the BeanDefinition for the ContextSource for the embedded server.
|
* @return the BeanDefinition for the ContextSource for the embedded server.
|
||||||
*
|
*
|
||||||
* @see ApacheDSContainer
|
|
||||||
* @see UnboundIdContainer
|
* @see UnboundIdContainer
|
||||||
*/
|
*/
|
||||||
private RootBeanDefinition createEmbeddedServer(Element element, ParserContext parserContext) {
|
private RootBeanDefinition createEmbeddedServer(Element element, ParserContext parserContext) {
|
||||||
|
@ -162,8 +153,7 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
|
||||||
}
|
}
|
||||||
ldapContainer.getConstructorArgumentValues().addGenericArgumentValue(ldifs);
|
ldapContainer.getConstructorArgumentValues().addGenericArgumentValue(ldifs);
|
||||||
ldapContainer.getPropertyValues().addPropertyValue("port", getPort(element));
|
ldapContainer.getPropertyValues().addPropertyValue("port", getPort(element));
|
||||||
if (parserContext.getRegistry().containsBeanDefinition(BeanIds.EMBEDDED_APACHE_DS)
|
if (parserContext.getRegistry().containsBeanDefinition(BeanIds.EMBEDDED_UNBOUNDID)) {
|
||||||
|| parserContext.getRegistry().containsBeanDefinition(BeanIds.EMBEDDED_UNBOUNDID)) {
|
|
||||||
parserContext.getReaderContext()
|
parserContext.getReaderContext()
|
||||||
.error("Only one embedded server bean is allowed per application context", element);
|
.error("Only one embedded server bean is allowed per application context", element);
|
||||||
}
|
}
|
||||||
|
@ -175,9 +165,6 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
private RootBeanDefinition getRootBeanDefinition(String mode) {
|
private RootBeanDefinition getRootBeanDefinition(String mode) {
|
||||||
if (isApacheDsEnabled(mode)) {
|
|
||||||
return new RootBeanDefinition(APACHEDS_CONTAINER_CLASSNAME, null, null);
|
|
||||||
}
|
|
||||||
if (isUnboundidEnabled(mode)) {
|
if (isUnboundidEnabled(mode)) {
|
||||||
return new RootBeanDefinition(UNBOUNDID_CONTAINER_CLASSNAME, null, null);
|
return new RootBeanDefinition(UNBOUNDID_CONTAINER_CLASSNAME, null, null);
|
||||||
}
|
}
|
||||||
|
@ -185,19 +172,12 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
private String resolveBeanId(String mode) {
|
private String resolveBeanId(String mode) {
|
||||||
if (isApacheDsEnabled(mode)) {
|
|
||||||
return BeanIds.EMBEDDED_APACHE_DS;
|
|
||||||
}
|
|
||||||
if (isUnboundidEnabled(mode)) {
|
if (isUnboundidEnabled(mode)) {
|
||||||
return BeanIds.EMBEDDED_UNBOUNDID;
|
return BeanIds.EMBEDDED_UNBOUNDID;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isApacheDsEnabled(String mode) {
|
|
||||||
return "apacheds".equals(mode) || apacheDsPresent;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isUnboundidEnabled(String mode) {
|
private boolean isUnboundidEnabled(String mode) {
|
||||||
return "unboundid".equals(mode) || unboundIdPresent;
|
return "unboundid".equals(mode) || unboundIdPresent;
|
||||||
}
|
}
|
||||||
|
@ -233,10 +213,6 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getPort() {
|
private int getPort() {
|
||||||
if (apacheDsPresent) {
|
|
||||||
ApacheDSContainer apacheDSContainer = this.applicationContext.getBean(ApacheDSContainer.class);
|
|
||||||
return apacheDSContainer.getLocalPort();
|
|
||||||
}
|
|
||||||
if (unboundIdPresent) {
|
if (unboundIdPresent) {
|
||||||
UnboundIdContainer unboundIdContainer = this.applicationContext.getBean(UnboundIdContainer.class);
|
UnboundIdContainer unboundIdContainer = this.applicationContext.getBean(UnboundIdContainer.class);
|
||||||
return unboundIdContainer.getPort();
|
return unboundIdContainer.getPort();
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2022 the original author or authors.
|
* Copyright 2002-2025 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.
|
||||||
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package org.springframework.security.config.oauth2.client;
|
package org.springframework.security.config.oauth2.client;
|
||||||
|
|
||||||
|
import org.springframework.security.config.Customizer;
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||||
import org.springframework.security.oauth2.client.registration.ClientRegistration.Builder;
|
import org.springframework.security.oauth2.client.registration.ClientRegistration.Builder;
|
||||||
|
@ -27,7 +28,7 @@ import org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;
|
||||||
* Common OAuth2 Providers that can be used to create
|
* Common OAuth2 Providers that can be used to create
|
||||||
* {@link org.springframework.security.oauth2.client.registration.ClientRegistration.Builder
|
* {@link org.springframework.security.oauth2.client.registration.ClientRegistration.Builder
|
||||||
* builders} pre-configured with sensible defaults for the
|
* builders} pre-configured with sensible defaults for the
|
||||||
* {@link HttpSecurity#oauth2Login()} flow.
|
* {@link HttpSecurity#oauth2Login(Customizer)} flow.
|
||||||
*
|
*
|
||||||
* @author Phillip Webb
|
* @author Phillip Webb
|
||||||
* @since 5.0
|
* @since 5.0
|
||||||
|
@ -87,6 +88,23 @@ public enum CommonOAuth2Provider {
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
X {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Builder getBuilder(String registrationId) {
|
||||||
|
ClientRegistration.Builder builder = getBuilder(registrationId,
|
||||||
|
ClientAuthenticationMethod.CLIENT_SECRET_POST, DEFAULT_REDIRECT_URL);
|
||||||
|
builder.scope("users.read", "tweet.read");
|
||||||
|
builder.authorizationUri("https://x.com/i/oauth2/authorize");
|
||||||
|
builder.tokenUri("https://api.x.com/2/oauth2/token");
|
||||||
|
builder.userInfoUri("https://api.x.com/2/users/me");
|
||||||
|
builder.userNameAttributeName("username");
|
||||||
|
builder.clientName("X");
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
OKTA {
|
OKTA {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -18,10 +18,6 @@ package org.springframework.security.config.web.server;
|
||||||
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import com.nimbusds.jose.JOSEObjectType;
|
|
||||||
import com.nimbusds.jose.proc.DefaultJOSEObjectTypeVerifier;
|
|
||||||
import com.nimbusds.jose.proc.JOSEObjectTypeVerifier;
|
|
||||||
import com.nimbusds.jose.proc.JWKSecurityContext;
|
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
import org.springframework.security.authentication.AuthenticationProvider;
|
import org.springframework.security.authentication.AuthenticationProvider;
|
||||||
|
@ -41,6 +37,7 @@ import org.springframework.security.oauth2.jwt.BadJwtException;
|
||||||
import org.springframework.security.oauth2.jwt.Jwt;
|
import org.springframework.security.oauth2.jwt.Jwt;
|
||||||
import org.springframework.security.oauth2.jwt.JwtDecoder;
|
import org.springframework.security.oauth2.jwt.JwtDecoder;
|
||||||
import org.springframework.security.oauth2.jwt.JwtDecoderFactory;
|
import org.springframework.security.oauth2.jwt.JwtDecoderFactory;
|
||||||
|
import org.springframework.security.oauth2.jwt.JwtTypeValidator;
|
||||||
import org.springframework.security.oauth2.jwt.JwtValidators;
|
import org.springframework.security.oauth2.jwt.JwtValidators;
|
||||||
import org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder;
|
import org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder;
|
||||||
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
|
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
|
||||||
|
@ -72,8 +69,10 @@ final class OidcBackChannelLogoutReactiveAuthenticationManager implements Reacti
|
||||||
* Construct an {@link OidcBackChannelLogoutReactiveAuthenticationManager}
|
* Construct an {@link OidcBackChannelLogoutReactiveAuthenticationManager}
|
||||||
*/
|
*/
|
||||||
OidcBackChannelLogoutReactiveAuthenticationManager() {
|
OidcBackChannelLogoutReactiveAuthenticationManager() {
|
||||||
|
JwtTypeValidator type = new JwtTypeValidator("JWT", "logout+jwt");
|
||||||
|
type.setAllowEmpty(true);
|
||||||
Function<ClientRegistration, OAuth2TokenValidator<Jwt>> jwtValidator = (clientRegistration) -> JwtValidators
|
Function<ClientRegistration, OAuth2TokenValidator<Jwt>> jwtValidator = (clientRegistration) -> JwtValidators
|
||||||
.createDefaultWithValidators(new OidcBackChannelLogoutTokenValidator(clientRegistration));
|
.createDefaultWithValidators(type, new OidcBackChannelLogoutTokenValidator(clientRegistration));
|
||||||
this.logoutTokenDecoderFactory = (clientRegistration) -> {
|
this.logoutTokenDecoderFactory = (clientRegistration) -> {
|
||||||
String jwkSetUri = clientRegistration.getProviderDetails().getJwkSetUri();
|
String jwkSetUri = clientRegistration.getProviderDetails().getJwkSetUri();
|
||||||
if (!StringUtils.hasText(jwkSetUri)) {
|
if (!StringUtils.hasText(jwkSetUri)) {
|
||||||
|
@ -84,11 +83,7 @@ final class OidcBackChannelLogoutReactiveAuthenticationManager implements Reacti
|
||||||
null);
|
null);
|
||||||
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
|
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
|
||||||
}
|
}
|
||||||
JOSEObjectTypeVerifier<JWKSecurityContext> typeVerifier = new DefaultJOSEObjectTypeVerifier<>(null,
|
NimbusReactiveJwtDecoder decoder = NimbusReactiveJwtDecoder.withJwkSetUri(jwkSetUri).build();
|
||||||
JOSEObjectType.JWT, new JOSEObjectType("logout+jwt"));
|
|
||||||
NimbusReactiveJwtDecoder decoder = NimbusReactiveJwtDecoder.withJwkSetUri(jwkSetUri)
|
|
||||||
.jwtProcessorCustomizer((processor) -> processor.setJWSTypeVerifier(typeVerifier))
|
|
||||||
.build();
|
|
||||||
decoder.setJwtValidator(jwtValidator.apply(clientRegistration));
|
decoder.setJwtValidator(jwtValidator.apply(clientRegistration));
|
||||||
decoder.setClaimSetConverter(
|
decoder.setClaimSetConverter(
|
||||||
new ClaimTypeConverter(OidcIdTokenDecoderFactory.createDefaultClaimTypeConverters()));
|
new ClaimTypeConverter(OidcIdTokenDecoderFactory.createDefaultClaimTypeConverters()));
|
||||||
|
|
|
@ -119,7 +119,7 @@ import org.springframework.security.oauth2.server.resource.web.server.BearerToke
|
||||||
import org.springframework.security.oauth2.server.resource.web.server.authentication.ServerBearerTokenAuthenticationConverter;
|
import org.springframework.security.oauth2.server.resource.web.server.authentication.ServerBearerTokenAuthenticationConverter;
|
||||||
import org.springframework.security.web.PortMapper;
|
import org.springframework.security.web.PortMapper;
|
||||||
import org.springframework.security.web.authentication.logout.LogoutHandler;
|
import org.springframework.security.web.authentication.logout.LogoutHandler;
|
||||||
import org.springframework.security.web.authentication.preauth.x509.SubjectDnX509PrincipalExtractor;
|
import org.springframework.security.web.authentication.preauth.x509.SubjectX500PrincipalExtractor;
|
||||||
import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor;
|
import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor;
|
||||||
import org.springframework.security.web.server.DefaultServerRedirectStrategy;
|
import org.springframework.security.web.server.DefaultServerRedirectStrategy;
|
||||||
import org.springframework.security.web.server.DelegatingServerAuthenticationEntryPoint;
|
import org.springframework.security.web.server.DelegatingServerAuthenticationEntryPoint;
|
||||||
|
@ -298,6 +298,7 @@ import org.springframework.web.util.pattern.PathPatternParser;
|
||||||
* @author Parikshit Dutta
|
* @author Parikshit Dutta
|
||||||
* @author Ankur Pathak
|
* @author Ankur Pathak
|
||||||
* @author Alexey Nesterov
|
* @author Alexey Nesterov
|
||||||
|
* @author Yanming Zhou
|
||||||
* @since 5.0
|
* @since 5.0
|
||||||
*/
|
*/
|
||||||
public class ServerHttpSecurity {
|
public class ServerHttpSecurity {
|
||||||
|
@ -943,8 +944,8 @@ public class ServerHttpSecurity {
|
||||||
* }
|
* }
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* Note that if extractor is not specified, {@link SubjectDnX509PrincipalExtractor}
|
* Note that if extractor is not specified, {@link SubjectX500PrincipalExtractor} will
|
||||||
* will be used. If authenticationManager is not specified,
|
* be used. If authenticationManager is not specified,
|
||||||
* {@link ReactivePreAuthenticatedAuthenticationManager} will be used.
|
* {@link ReactivePreAuthenticatedAuthenticationManager} will be used.
|
||||||
* @return the {@link X509Spec} to customize
|
* @return the {@link X509Spec} to customize
|
||||||
* @since 5.2
|
* @since 5.2
|
||||||
|
@ -978,8 +979,8 @@ public class ServerHttpSecurity {
|
||||||
* }
|
* }
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* Note that if extractor is not specified, {@link SubjectDnX509PrincipalExtractor}
|
* Note that if extractor is not specified, {@link SubjectX500PrincipalExtractor} will
|
||||||
* will be used. If authenticationManager is not specified,
|
* be used. If authenticationManager is not specified,
|
||||||
* {@link ReactivePreAuthenticatedAuthenticationManager} will be used.
|
* {@link ReactivePreAuthenticatedAuthenticationManager} will be used.
|
||||||
* @param x509Customizer the {@link Customizer} to provide more options for the
|
* @param x509Customizer the {@link Customizer} to provide more options for the
|
||||||
* {@link X509Spec}
|
* {@link X509Spec}
|
||||||
|
@ -4180,7 +4181,7 @@ public class ServerHttpSecurity {
|
||||||
if (this.principalExtractor != null) {
|
if (this.principalExtractor != null) {
|
||||||
return this.principalExtractor;
|
return this.principalExtractor;
|
||||||
}
|
}
|
||||||
return new SubjectDnX509PrincipalExtractor();
|
return new SubjectX500PrincipalExtractor();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ReactiveAuthenticationManager getAuthenticationManager() {
|
private ReactiveAuthenticationManager getAuthenticationManager() {
|
||||||
|
@ -5443,8 +5444,11 @@ public class ServerHttpSecurity {
|
||||||
public OpaqueTokenSpec introspectionUri(String introspectionUri) {
|
public OpaqueTokenSpec introspectionUri(String introspectionUri) {
|
||||||
Assert.hasText(introspectionUri, "introspectionUri cannot be empty");
|
Assert.hasText(introspectionUri, "introspectionUri cannot be empty");
|
||||||
this.introspectionUri = introspectionUri;
|
this.introspectionUri = introspectionUri;
|
||||||
this.introspector = () -> new SpringReactiveOpaqueTokenIntrospector(this.introspectionUri,
|
this.introspector = () -> SpringReactiveOpaqueTokenIntrospector
|
||||||
this.clientId, this.clientSecret);
|
.withIntrospectionUri(this.introspectionUri)
|
||||||
|
.clientId(this.clientId)
|
||||||
|
.clientSecret(this.clientSecret)
|
||||||
|
.build();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5459,8 +5463,11 @@ public class ServerHttpSecurity {
|
||||||
Assert.notNull(clientSecret, "clientSecret cannot be null");
|
Assert.notNull(clientSecret, "clientSecret cannot be null");
|
||||||
this.clientId = clientId;
|
this.clientId = clientId;
|
||||||
this.clientSecret = clientSecret;
|
this.clientSecret = clientSecret;
|
||||||
this.introspector = () -> new SpringReactiveOpaqueTokenIntrospector(this.introspectionUri,
|
this.introspector = () -> SpringReactiveOpaqueTokenIntrospector
|
||||||
this.clientId, this.clientSecret);
|
.withIntrospectionUri(this.introspectionUri)
|
||||||
|
.clientId(this.clientId)
|
||||||
|
.clientSecret(this.clientSecret)
|
||||||
|
.build();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,9 +16,13 @@
|
||||||
|
|
||||||
package org.springframework.security.config.websocket;
|
package org.springframework.security.config.websocket;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
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.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import org.w3c.dom.Element;
|
import org.w3c.dom.Element;
|
||||||
|
@ -307,6 +311,11 @@ public final class WebSocketMessageBrokerSecurityBeanDefinitionParser implements
|
||||||
|
|
||||||
private static final String TEMPLATE_EXPRESSION_BEAN_ID = "annotationExpressionTemplateDefaults";
|
private static final String TEMPLATE_EXPRESSION_BEAN_ID = "annotationExpressionTemplateDefaults";
|
||||||
|
|
||||||
|
private static final Set<String> CSRF_HANDSHAKE_HANDLER_CLASSES = Collections.unmodifiableSet(
|
||||||
|
new HashSet<>(Arrays.asList("org.springframework.web.socket.server.support.WebSocketHttpRequestHandler",
|
||||||
|
"org.springframework.web.socket.sockjs.transport.TransportHandlingSockJsService",
|
||||||
|
"org.springframework.web.socket.sockjs.transport.handler.DefaultSockJsService")));
|
||||||
|
|
||||||
private final String inboundSecurityInterceptorId;
|
private final String inboundSecurityInterceptorId;
|
||||||
|
|
||||||
private final boolean sameOriginDisabled;
|
private final boolean sameOriginDisabled;
|
||||||
|
@ -345,16 +354,7 @@ public final class WebSocketMessageBrokerSecurityBeanDefinitionParser implements
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ("org.springframework.web.socket.server.support.WebSocketHttpRequestHandler"
|
else if (CSRF_HANDSHAKE_HANDLER_CLASSES.contains(beanClassName)) {
|
||||||
.equals(beanClassName)) {
|
|
||||||
addCsrfTokenHandshakeInterceptor(bd);
|
|
||||||
}
|
|
||||||
else if ("org.springframework.web.socket.sockjs.transport.TransportHandlingSockJsService"
|
|
||||||
.equals(beanClassName)) {
|
|
||||||
addCsrfTokenHandshakeInterceptor(bd);
|
|
||||||
}
|
|
||||||
else if ("org.springframework.web.socket.sockjs.transport.handler.DefaultSockJsService"
|
|
||||||
.equals(beanClassName)) {
|
|
||||||
addCsrfTokenHandshakeInterceptor(bd);
|
addCsrfTokenHandshakeInterceptor(bd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,7 @@ class X509Dsl {
|
||||||
var authenticationDetailsSource: AuthenticationDetailsSource<HttpServletRequest, PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails>? = null
|
var authenticationDetailsSource: AuthenticationDetailsSource<HttpServletRequest, PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails>? = null
|
||||||
var userDetailsService: UserDetailsService? = null
|
var userDetailsService: UserDetailsService? = null
|
||||||
var authenticationUserDetailsService: AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken>? = null
|
var authenticationUserDetailsService: AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken>? = null
|
||||||
|
@Deprecated("Use x509PrincipalExtractor instead")
|
||||||
var subjectPrincipalRegex: String? = null
|
var subjectPrincipalRegex: String? = null
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,8 @@
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
|
||||||
http\://www.springframework.org/schema/security/spring-security.xsd=org/springframework/security/config/spring-security-6.5.xsd
|
http\://www.springframework.org/schema/security/spring-security.xsd=org/springframework/security/config/spring-security-7.0.xsd
|
||||||
|
http\://www.springframework.org/schema/security/spring-security-7.0.xsd=org/springframework/security/config/spring-security-7.0.xsd
|
||||||
http\://www.springframework.org/schema/security/spring-security-6.5.xsd=org/springframework/security/config/spring-security-6.5.xsd
|
http\://www.springframework.org/schema/security/spring-security-6.5.xsd=org/springframework/security/config/spring-security-6.5.xsd
|
||||||
http\://www.springframework.org/schema/security/spring-security-6.4.xsd=org/springframework/security/config/spring-security-6.4.xsd
|
http\://www.springframework.org/schema/security/spring-security-6.4.xsd=org/springframework/security/config/spring-security-6.4.xsd
|
||||||
http\://www.springframework.org/schema/security/spring-security-6.3.xsd=org/springframework/security/config/spring-security-6.3.xsd
|
http\://www.springframework.org/schema/security/spring-security-6.3.xsd=org/springframework/security/config/spring-security-6.3.xsd
|
||||||
|
@ -41,7 +42,8 @@ http\://www.springframework.org/schema/security/spring-security-2.0.xsd=org/spri
|
||||||
http\://www.springframework.org/schema/security/spring-security-2.0.1.xsd=org/springframework/security/config/spring-security-2.0.1.xsd
|
http\://www.springframework.org/schema/security/spring-security-2.0.1.xsd=org/springframework/security/config/spring-security-2.0.1.xsd
|
||||||
http\://www.springframework.org/schema/security/spring-security-2.0.2.xsd=org/springframework/security/config/spring-security-2.0.2.xsd
|
http\://www.springframework.org/schema/security/spring-security-2.0.2.xsd=org/springframework/security/config/spring-security-2.0.2.xsd
|
||||||
http\://www.springframework.org/schema/security/spring-security-2.0.4.xsd=org/springframework/security/config/spring-security-2.0.4.xsd
|
http\://www.springframework.org/schema/security/spring-security-2.0.4.xsd=org/springframework/security/config/spring-security-2.0.4.xsd
|
||||||
https\://www.springframework.org/schema/security/spring-security.xsd=org/springframework/security/config/spring-security-6.5.xsd
|
https\://www.springframework.org/schema/security/spring-security.xsd=org/springframework/security/config/spring-security-7.0.xsd
|
||||||
|
https\://www.springframework.org/schema/security/spring-security-7.0.xsd=org/springframework/security/config/spring-security-7.0.xsd
|
||||||
https\://www.springframework.org/schema/security/spring-security-6.5.xsd=org/springframework/security/config/spring-security-6.5.xsd
|
https\://www.springframework.org/schema/security/spring-security-6.5.xsd=org/springframework/security/config/spring-security-6.5.xsd
|
||||||
https\://www.springframework.org/schema/security/spring-security-6.4.xsd=org/springframework/security/config/spring-security-6.4.xsd
|
https\://www.springframework.org/schema/security/spring-security-6.4.xsd=org/springframework/security/config/spring-security-6.4.xsd
|
||||||
https\://www.springframework.org/schema/security/spring-security-6.3.xsd=org/springframework/security/config/spring-security-6.3.xsd
|
https\://www.springframework.org/schema/security/spring-security-6.3.xsd=org/springframework/security/config/spring-security-6.3.xsd
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -170,11 +170,14 @@ import org.springframework.security.saml2.core.Saml2Error;
|
||||||
import org.springframework.security.saml2.core.Saml2X509Credential;
|
import org.springframework.security.saml2.core.Saml2X509Credential;
|
||||||
import org.springframework.security.saml2.credentials.TestSaml2X509Credentials;
|
import org.springframework.security.saml2.credentials.TestSaml2X509Credentials;
|
||||||
import org.springframework.security.saml2.provider.service.authentication.DefaultSaml2AuthenticatedPrincipal;
|
import org.springframework.security.saml2.provider.service.authentication.DefaultSaml2AuthenticatedPrincipal;
|
||||||
|
import org.springframework.security.saml2.provider.service.authentication.Saml2AssertionAuthentication;
|
||||||
import org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;
|
import org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;
|
||||||
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;
|
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;
|
||||||
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationToken;
|
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationToken;
|
||||||
import org.springframework.security.saml2.provider.service.authentication.Saml2PostAuthenticationRequest;
|
import org.springframework.security.saml2.provider.service.authentication.Saml2PostAuthenticationRequest;
|
||||||
import org.springframework.security.saml2.provider.service.authentication.Saml2RedirectAuthenticationRequest;
|
import org.springframework.security.saml2.provider.service.authentication.Saml2RedirectAuthenticationRequest;
|
||||||
|
import org.springframework.security.saml2.provider.service.authentication.Saml2ResponseAssertion;
|
||||||
|
import org.springframework.security.saml2.provider.service.authentication.Saml2ResponseAssertionAccessor;
|
||||||
import org.springframework.security.saml2.provider.service.authentication.TestSaml2AuthenticationTokens;
|
import org.springframework.security.saml2.provider.service.authentication.TestSaml2AuthenticationTokens;
|
||||||
import org.springframework.security.saml2.provider.service.authentication.TestSaml2Authentications;
|
import org.springframework.security.saml2.provider.service.authentication.TestSaml2Authentications;
|
||||||
import org.springframework.security.saml2.provider.service.authentication.TestSaml2LogoutRequests;
|
import org.springframework.security.saml2.provider.service.authentication.TestSaml2LogoutRequests;
|
||||||
|
@ -520,8 +523,16 @@ final class SerializationSamples {
|
||||||
generatorByClassName.put(Saml2Exception.class, (r) -> new Saml2Exception("message", new IOException("fail")));
|
generatorByClassName.put(Saml2Exception.class, (r) -> new Saml2Exception("message", new IOException("fail")));
|
||||||
generatorByClassName.put(DefaultSaml2AuthenticatedPrincipal.class,
|
generatorByClassName.put(DefaultSaml2AuthenticatedPrincipal.class,
|
||||||
(r) -> TestSaml2Authentications.authentication().getPrincipal());
|
(r) -> TestSaml2Authentications.authentication().getPrincipal());
|
||||||
generatorByClassName.put(Saml2Authentication.class,
|
Saml2Authentication saml2 = TestSaml2Authentications.authentication();
|
||||||
(r) -> applyDetails(TestSaml2Authentications.authentication()));
|
generatorByClassName.put(Saml2Authentication.class, (r) -> applyDetails(saml2));
|
||||||
|
Saml2ResponseAssertionAccessor assertion = Saml2ResponseAssertion.withResponseValue("response")
|
||||||
|
.nameId("name")
|
||||||
|
.sessionIndexes(List.of("id"))
|
||||||
|
.attributes(Map.of("key", List.of("value")))
|
||||||
|
.build();
|
||||||
|
generatorByClassName.put(Saml2ResponseAssertion.class, (r) -> assertion);
|
||||||
|
generatorByClassName.put(Saml2AssertionAuthentication.class, (r) -> applyDetails(
|
||||||
|
new Saml2AssertionAuthentication(assertion, authentication.getAuthorities(), "id")));
|
||||||
generatorByClassName.put(Saml2PostAuthenticationRequest.class,
|
generatorByClassName.put(Saml2PostAuthenticationRequest.class,
|
||||||
(r) -> TestSaml2PostAuthenticationRequests.create());
|
(r) -> TestSaml2PostAuthenticationRequests.create());
|
||||||
generatorByClassName.put(Saml2RedirectAuthenticationRequest.class,
|
generatorByClassName.put(Saml2RedirectAuthenticationRequest.class,
|
||||||
|
|
|
@ -260,6 +260,12 @@ class SpringSecurityCoreVersionSerializableTests {
|
||||||
String version = System.getProperty("springSecurityVersion");
|
String version = System.getProperty("springSecurityVersion");
|
||||||
String[] parts = version.split("\\.");
|
String[] parts = version.split("\\.");
|
||||||
parts[1] = String.valueOf(Integer.parseInt(parts[1]) - 1);
|
parts[1] = String.valueOf(Integer.parseInt(parts[1]) - 1);
|
||||||
|
// FIXME: the 7 should not be hardcoded
|
||||||
|
if ("7".equals(parts[0]) && "-1".equals(parts[1])) {
|
||||||
|
// if it is version 7.0.x, the previous version is 6.5.x
|
||||||
|
parts[0] = String.valueOf(Integer.parseInt(parts[0]) - 1);
|
||||||
|
parts[1] = "5"; // FIXME: this should not be hard coded
|
||||||
|
}
|
||||||
parts[2] = "x";
|
parts[2] = "x";
|
||||||
return String.join(".", parts);
|
return String.join(".", parts);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2024 the original author or authors.
|
* Copyright 2002-2025 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.
|
||||||
|
@ -58,7 +58,7 @@ public class AuthorizationProxyConfigurationTests {
|
||||||
@Test
|
@Test
|
||||||
public void proxyWhenNotPreAuthorizedThenDenies() {
|
public void proxyWhenNotPreAuthorizedThenDenies() {
|
||||||
this.spring.register(DefaultsConfig.class).autowire();
|
this.spring.register(DefaultsConfig.class).autowire();
|
||||||
Toaster toaster = (Toaster) this.proxyFactory.proxy(new Toaster());
|
Toaster toaster = this.proxyFactory.proxy(new Toaster());
|
||||||
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(toaster::makeToast)
|
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(toaster::makeToast)
|
||||||
.withMessage("Access Denied");
|
.withMessage("Access Denied");
|
||||||
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(toaster::extractBread)
|
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(toaster::extractBread)
|
||||||
|
@ -69,7 +69,7 @@ public class AuthorizationProxyConfigurationTests {
|
||||||
@Test
|
@Test
|
||||||
public void proxyWhenPreAuthorizedThenAllows() {
|
public void proxyWhenPreAuthorizedThenAllows() {
|
||||||
this.spring.register(DefaultsConfig.class).autowire();
|
this.spring.register(DefaultsConfig.class).autowire();
|
||||||
Toaster toaster = (Toaster) this.proxyFactory.proxy(new Toaster());
|
Toaster toaster = this.proxyFactory.proxy(new Toaster());
|
||||||
toaster.makeToast();
|
toaster.makeToast();
|
||||||
assertThat(toaster.extractBread()).isEqualTo("yummy");
|
assertThat(toaster.extractBread()).isEqualTo("yummy");
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,7 @@ public class AuthorizationProxyConfigurationTests {
|
||||||
@Test
|
@Test
|
||||||
public void proxyReactiveWhenNotPreAuthorizedThenDenies() {
|
public void proxyReactiveWhenNotPreAuthorizedThenDenies() {
|
||||||
this.spring.register(ReactiveDefaultsConfig.class).autowire();
|
this.spring.register(ReactiveDefaultsConfig.class).autowire();
|
||||||
Toaster toaster = (Toaster) this.proxyFactory.proxy(new Toaster());
|
Toaster toaster = this.proxyFactory.proxy(new Toaster());
|
||||||
Authentication user = TestAuthentication.authenticatedUser();
|
Authentication user = TestAuthentication.authenticatedUser();
|
||||||
StepVerifier
|
StepVerifier
|
||||||
.create(toaster.reactiveMakeToast().contextWrite(ReactiveSecurityContextHolder.withAuthentication(user)))
|
.create(toaster.reactiveMakeToast().contextWrite(ReactiveSecurityContextHolder.withAuthentication(user)))
|
||||||
|
@ -90,7 +90,7 @@ public class AuthorizationProxyConfigurationTests {
|
||||||
@Test
|
@Test
|
||||||
public void proxyReactiveWhenPreAuthorizedThenAllows() {
|
public void proxyReactiveWhenPreAuthorizedThenAllows() {
|
||||||
this.spring.register(ReactiveDefaultsConfig.class).autowire();
|
this.spring.register(ReactiveDefaultsConfig.class).autowire();
|
||||||
Toaster toaster = (Toaster) this.proxyFactory.proxy(new Toaster());
|
Toaster toaster = this.proxyFactory.proxy(new Toaster());
|
||||||
Authentication admin = TestAuthentication.authenticatedAdmin();
|
Authentication admin = TestAuthentication.authenticatedAdmin();
|
||||||
StepVerifier
|
StepVerifier
|
||||||
.create(toaster.reactiveMakeToast().contextWrite(ReactiveSecurityContextHolder.withAuthentication(admin)))
|
.create(toaster.reactiveMakeToast().contextWrite(ReactiveSecurityContextHolder.withAuthentication(admin)))
|
||||||
|
|
|
@ -33,15 +33,16 @@ import io.micrometer.observation.ObservationHandler;
|
||||||
import io.micrometer.observation.ObservationRegistry;
|
import io.micrometer.observation.ObservationRegistry;
|
||||||
import io.micrometer.observation.ObservationTextPublisher;
|
import io.micrometer.observation.ObservationTextPublisher;
|
||||||
import jakarta.annotation.security.DenyAll;
|
import jakarta.annotation.security.DenyAll;
|
||||||
|
import org.aopalliance.aop.Advice;
|
||||||
import org.aopalliance.intercept.MethodInterceptor;
|
import org.aopalliance.intercept.MethodInterceptor;
|
||||||
import org.aopalliance.intercept.MethodInvocation;
|
import org.aopalliance.intercept.MethodInvocation;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
import org.junit.jupiter.params.provider.ValueSource;
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
import org.mockito.Mockito;
|
|
||||||
|
|
||||||
import org.springframework.aop.Advisor;
|
import org.springframework.aop.Advisor;
|
||||||
|
import org.springframework.aop.Pointcut;
|
||||||
import org.springframework.aop.config.AopConfigUtils;
|
import org.springframework.aop.config.AopConfigUtils;
|
||||||
import org.springframework.aop.support.DefaultPointcutAdvisor;
|
import org.springframework.aop.support.DefaultPointcutAdvisor;
|
||||||
import org.springframework.aop.support.JdkRegexpMethodPointcut;
|
import org.springframework.aop.support.JdkRegexpMethodPointcut;
|
||||||
|
@ -61,9 +62,19 @@ import org.springframework.context.annotation.Role;
|
||||||
import org.springframework.context.event.EventListener;
|
import org.springframework.context.event.EventListener;
|
||||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||||
import org.springframework.core.annotation.AnnotationConfigurationException;
|
import org.springframework.core.annotation.AnnotationConfigurationException;
|
||||||
import org.springframework.core.annotation.Order;
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.PageImpl;
|
||||||
|
import org.springframework.data.domain.Slice;
|
||||||
|
import org.springframework.data.domain.SliceImpl;
|
||||||
|
import org.springframework.data.geo.Distance;
|
||||||
|
import org.springframework.data.geo.GeoPage;
|
||||||
|
import org.springframework.data.geo.GeoResult;
|
||||||
|
import org.springframework.data.geo.GeoResults;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.HttpStatusCode;
|
import org.springframework.http.HttpStatusCode;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.http.converter.HttpMessageNotWritableException;
|
||||||
import org.springframework.security.access.AccessDeniedException;
|
import org.springframework.security.access.AccessDeniedException;
|
||||||
import org.springframework.security.access.PermissionEvaluator;
|
import org.springframework.security.access.PermissionEvaluator;
|
||||||
import org.springframework.security.access.annotation.BusinessService;
|
import org.springframework.security.access.annotation.BusinessService;
|
||||||
|
@ -95,6 +106,7 @@ import org.springframework.security.authorization.method.MethodAuthorizationDeni
|
||||||
import org.springframework.security.authorization.method.MethodInvocationResult;
|
import org.springframework.security.authorization.method.MethodInvocationResult;
|
||||||
import org.springframework.security.authorization.method.PrePostTemplateDefaults;
|
import org.springframework.security.authorization.method.PrePostTemplateDefaults;
|
||||||
import org.springframework.security.config.annotation.SecurityContextChangedListenerConfig;
|
import org.springframework.security.config.annotation.SecurityContextChangedListenerConfig;
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
import org.springframework.security.config.core.GrantedAuthorityDefaults;
|
import org.springframework.security.config.core.GrantedAuthorityDefaults;
|
||||||
import org.springframework.security.config.observation.SecurityObservationSettings;
|
import org.springframework.security.config.observation.SecurityObservationSettings;
|
||||||
import org.springframework.security.config.test.SpringTestContext;
|
import org.springframework.security.config.test.SpringTestContext;
|
||||||
|
@ -106,13 +118,24 @@ import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
||||||
import org.springframework.security.test.context.support.WithAnonymousUser;
|
import org.springframework.security.test.context.support.WithAnonymousUser;
|
||||||
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.security.test.context.support.WithSecurityContextTestExecutionListener;
|
||||||
|
import org.springframework.security.web.util.ThrowableAnalyzer;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.test.context.ContextConfiguration;
|
import org.springframework.test.context.ContextConfiguration;
|
||||||
import org.springframework.test.context.TestExecutionListeners;
|
import org.springframework.test.context.TestExecutionListeners;
|
||||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||||
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
import org.springframework.test.web.servlet.MvcResult;
|
||||||
|
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
|
||||||
|
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||||
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.context.ConfigurableWebApplicationContext;
|
import org.springframework.web.context.ConfigurableWebApplicationContext;
|
||||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
||||||
import org.springframework.web.servlet.ModelAndView;
|
import org.springframework.web.servlet.ModelAndView;
|
||||||
|
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
|
@ -127,6 +150,9 @@ import static org.mockito.Mockito.spy;
|
||||||
import static org.mockito.Mockito.times;
|
import static org.mockito.Mockito.times;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.verifyNoInteractions;
|
import static org.mockito.Mockito.verifyNoInteractions;
|
||||||
|
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link PrePostMethodSecurityConfiguration}.
|
* Tests for {@link PrePostMethodSecurityConfiguration}.
|
||||||
|
@ -148,6 +174,9 @@ public class PrePostMethodSecurityConfigurationTests {
|
||||||
@Autowired(required = false)
|
@Autowired(required = false)
|
||||||
BusinessService businessService;
|
BusinessService businessService;
|
||||||
|
|
||||||
|
@Autowired(required = false)
|
||||||
|
MockMvc mvc;
|
||||||
|
|
||||||
@WithMockUser
|
@WithMockUser
|
||||||
@Test
|
@Test
|
||||||
public void customMethodSecurityPreAuthorizeAdminWhenRoleUserThenAccessDeniedException() {
|
public void customMethodSecurityPreAuthorizeAdminWhenRoleUserThenAccessDeniedException() {
|
||||||
|
@ -733,6 +762,28 @@ public class PrePostMethodSecurityConfigurationTests {
|
||||||
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(flight::getAltitude);
|
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(flight::getAltitude);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockUser(authorities = "airplane:read")
|
||||||
|
public void findGeoResultByIdWhenAuthorizedResultThenAuthorizes() {
|
||||||
|
this.spring.register(AuthorizeResultConfig.class).autowire();
|
||||||
|
FlightRepository flights = this.spring.getContext().getBean(FlightRepository.class);
|
||||||
|
GeoResult<Flight> geoResultFlight = flights.findGeoResultFlightById("1");
|
||||||
|
Flight flight = geoResultFlight.getContent();
|
||||||
|
assertThatNoException().isThrownBy(flight::getAltitude);
|
||||||
|
assertThatNoException().isThrownBy(flight::getSeats);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockUser(authorities = "seating:read")
|
||||||
|
public void findGeoResultByIdWhenUnauthorizedResultThenDenies() {
|
||||||
|
this.spring.register(AuthorizeResultConfig.class).autowire();
|
||||||
|
FlightRepository flights = this.spring.getContext().getBean(FlightRepository.class);
|
||||||
|
GeoResult<Flight> geoResultFlight = flights.findGeoResultFlightById("1");
|
||||||
|
Flight flight = geoResultFlight.getContent();
|
||||||
|
assertThatNoException().isThrownBy(flight::getSeats);
|
||||||
|
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(flight::getAltitude);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@WithMockUser(authorities = "airplane:read")
|
@WithMockUser(authorities = "airplane:read")
|
||||||
public void findByIdWhenAuthorizedResponseEntityThenAuthorizes() {
|
public void findByIdWhenAuthorizedResponseEntityThenAuthorizes() {
|
||||||
|
@ -804,6 +855,46 @@ public class PrePostMethodSecurityConfigurationTests {
|
||||||
.doesNotContain("Kevin Mitnick"));
|
.doesNotContain("Kevin Mitnick"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockUser(authorities = "airplane:read")
|
||||||
|
public void findPageWhenPostFilterThenFilters() {
|
||||||
|
this.spring.register(AuthorizeResultConfig.class).autowire();
|
||||||
|
FlightRepository flights = this.spring.getContext().getBean(FlightRepository.class);
|
||||||
|
flights.findPage()
|
||||||
|
.forEach((flight) -> assertThat(flight.getPassengers()).extracting(Passenger::getName)
|
||||||
|
.doesNotContain("Kevin Mitnick"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockUser(authorities = "airplane:read")
|
||||||
|
public void findSliceWhenPostFilterThenFilters() {
|
||||||
|
this.spring.register(AuthorizeResultConfig.class).autowire();
|
||||||
|
FlightRepository flights = this.spring.getContext().getBean(FlightRepository.class);
|
||||||
|
flights.findSlice()
|
||||||
|
.forEach((flight) -> assertThat(flight.getPassengers()).extracting(Passenger::getName)
|
||||||
|
.doesNotContain("Kevin Mitnick"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockUser(authorities = "airplane:read")
|
||||||
|
public void findGeoPageWhenPostFilterThenFilters() {
|
||||||
|
this.spring.register(AuthorizeResultConfig.class).autowire();
|
||||||
|
FlightRepository flights = this.spring.getContext().getBean(FlightRepository.class);
|
||||||
|
flights.findGeoPage()
|
||||||
|
.forEach((flight) -> assertThat(flight.getContent().getPassengers()).extracting(Passenger::getName)
|
||||||
|
.doesNotContain("Kevin Mitnick"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockUser(authorities = "airplane:read")
|
||||||
|
public void findGeoResultsWhenPostFilterThenFilters() {
|
||||||
|
this.spring.register(AuthorizeResultConfig.class).autowire();
|
||||||
|
FlightRepository flights = this.spring.getContext().getBean(FlightRepository.class);
|
||||||
|
flights.findGeoResults()
|
||||||
|
.forEach((flight) -> assertThat(flight.getContent().getPassengers()).extracting(Passenger::getName)
|
||||||
|
.doesNotContain("Kevin Mitnick"));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@WithMockUser(authorities = "airplane:read")
|
@WithMockUser(authorities = "airplane:read")
|
||||||
public void findAllWhenPreFilterThenFilters() {
|
public void findAllWhenPreFilterThenFilters() {
|
||||||
|
@ -1181,6 +1272,97 @@ public class PrePostMethodSecurityConfigurationTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getWhenPostAuthorizeAuthenticationNameMatchesThenRespondsWithOk() throws Exception {
|
||||||
|
this.spring.register(WebMvcMethodSecurityConfig.class, BasicController.class).autowire();
|
||||||
|
// @formatter:off
|
||||||
|
MockHttpServletRequestBuilder requestWithUser = get("/authorized-person")
|
||||||
|
.param("name", "rob")
|
||||||
|
.with(user("rob"));
|
||||||
|
// @formatter:on
|
||||||
|
this.mvc.perform(requestWithUser).andExpect(status().isOk());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getWhenPostAuthorizeAuthenticationNameNotMatchThenRespondsWithForbidden() throws Exception {
|
||||||
|
this.spring.register(WebMvcMethodSecurityConfig.class, BasicController.class).autowire();
|
||||||
|
// @formatter:off
|
||||||
|
MockHttpServletRequestBuilder requestWithUser = get("/authorized-person")
|
||||||
|
.param("name", "john")
|
||||||
|
.with(user("rob"));
|
||||||
|
// @formatter:on
|
||||||
|
this.mvc.perform(requestWithUser).andExpect(status().isForbidden());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getWhenPostAuthorizeWithinServiceAuthenticationNameMatchesThenRespondsWithOk() throws Exception {
|
||||||
|
this.spring.register(WebMvcMethodSecurityConfig.class, BasicController.class, BasicService.class).autowire();
|
||||||
|
// @formatter:off
|
||||||
|
MockHttpServletRequestBuilder requestWithUser = get("/greetings/authorized-person")
|
||||||
|
.param("name", "rob")
|
||||||
|
.with(user("rob"));
|
||||||
|
// @formatter:on
|
||||||
|
MvcResult mvcResult = this.mvc.perform(requestWithUser).andExpect(status().isOk()).andReturn();
|
||||||
|
assertThat(mvcResult.getResponse().getContentAsString()).isEqualTo("Hello: rob");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getWhenPostAuthorizeWithinServiceAuthenticationNameNotMatchThenCustomHandlerRespondsWithForbidden()
|
||||||
|
throws Exception {
|
||||||
|
this.spring
|
||||||
|
.register(WebMvcMethodSecurityConfig.class, BasicController.class, BasicService.class,
|
||||||
|
BasicControllerAdvice.class)
|
||||||
|
.autowire();
|
||||||
|
// @formatter:off
|
||||||
|
MockHttpServletRequestBuilder requestWithUser = get("/greetings/authorized-person")
|
||||||
|
.param("name", "john")
|
||||||
|
.with(user("rob"));
|
||||||
|
// @formatter:on
|
||||||
|
MvcResult mvcResult = this.mvc.perform(requestWithUser).andExpect(status().isForbidden()).andReturn();
|
||||||
|
assertThat(mvcResult.getResponse().getContentAsString()).isEqualTo("""
|
||||||
|
{"message":"Access Denied"}\
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getWhenPostAuthorizeAuthenticationNameNotMatchThenCustomHandlerRespondsWithForbidden() throws Exception {
|
||||||
|
this.spring
|
||||||
|
.register(WebMvcMethodSecurityConfig.class, BasicController.class, BasicService.class,
|
||||||
|
BasicControllerAdvice.class)
|
||||||
|
.autowire();
|
||||||
|
// @formatter:off
|
||||||
|
MockHttpServletRequestBuilder requestWithUser = get("/authorized-person")
|
||||||
|
.param("name", "john")
|
||||||
|
.with(user("rob"));
|
||||||
|
// @formatter:on
|
||||||
|
MvcResult mvcResult = this.mvc.perform(requestWithUser).andExpect(status().isForbidden()).andReturn();
|
||||||
|
assertThat(mvcResult.getResponse().getContentAsString()).isEqualTo("""
|
||||||
|
{"message":"Could not write JSON: Access Denied"}\
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getWhenCustomAdvisorAuthenticationNameMatchesThenRespondsWithOk() throws Exception {
|
||||||
|
this.spring.register(WebMvcMethodSecurityCustomAdvisorConfig.class, BasicController.class).autowire();
|
||||||
|
// @formatter:off
|
||||||
|
MockHttpServletRequestBuilder requestWithUser = get("/authorized-person")
|
||||||
|
.param("name", "rob")
|
||||||
|
.with(user("rob"));
|
||||||
|
// @formatter:on
|
||||||
|
this.mvc.perform(requestWithUser).andExpect(status().isOk());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getWhenCustomAdvisorAuthenticationNameNotMatchThenRespondsWithForbidden() throws Exception {
|
||||||
|
this.spring.register(WebMvcMethodSecurityCustomAdvisorConfig.class, BasicController.class).autowire();
|
||||||
|
// @formatter:off
|
||||||
|
MockHttpServletRequestBuilder requestWithUser = get("/authorized-person")
|
||||||
|
.param("name", "john")
|
||||||
|
.with(user("rob"));
|
||||||
|
// @formatter:on
|
||||||
|
this.mvc.perform(requestWithUser).andExpect(status().isForbidden());
|
||||||
|
}
|
||||||
|
|
||||||
private static Consumer<ConfigurableWebApplicationContext> disallowBeanOverriding() {
|
private static Consumer<ConfigurableWebApplicationContext> disallowBeanOverriding() {
|
||||||
return (context) -> ((AnnotationConfigWebApplicationContext) context).setAllowBeanDefinitionOverriding(false);
|
return (context) -> ((AnnotationConfigWebApplicationContext) context).setAllowBeanDefinitionOverriding(false);
|
||||||
}
|
}
|
||||||
|
@ -1648,14 +1830,6 @@ public class PrePostMethodSecurityConfigurationTests {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||||
@Order(1)
|
|
||||||
static TargetVisitor mock() {
|
|
||||||
return Mockito.mock(TargetVisitor.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
|
||||||
@Order(0)
|
|
||||||
static TargetVisitor skipValueTypes() {
|
static TargetVisitor skipValueTypes() {
|
||||||
return TargetVisitor.defaultsSkipValueTypes();
|
return TargetVisitor.defaultsSkipValueTypes();
|
||||||
}
|
}
|
||||||
|
@ -1688,10 +1862,39 @@ public class PrePostMethodSecurityConfigurationTests {
|
||||||
return this.flights.values().iterator();
|
return this.flights.values().iterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Page<Flight> findPage() {
|
||||||
|
return new PageImpl<>(new ArrayList<>(this.flights.values()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Slice<Flight> findSlice() {
|
||||||
|
return new SliceImpl<>(new ArrayList<>(this.flights.values()));
|
||||||
|
}
|
||||||
|
|
||||||
|
GeoPage<Flight> findGeoPage() {
|
||||||
|
List<GeoResult<Flight>> results = new ArrayList<>();
|
||||||
|
for (Flight flight : this.flights.values()) {
|
||||||
|
results.add(new GeoResult<>(flight, new Distance(flight.altitude)));
|
||||||
|
}
|
||||||
|
return new GeoPage<>(new GeoResults<>(results));
|
||||||
|
}
|
||||||
|
|
||||||
|
GeoResults<Flight> findGeoResults() {
|
||||||
|
List<GeoResult<Flight>> results = new ArrayList<>();
|
||||||
|
for (Flight flight : this.flights.values()) {
|
||||||
|
results.add(new GeoResult<>(flight, new Distance(flight.altitude)));
|
||||||
|
}
|
||||||
|
return new GeoResults<>(results);
|
||||||
|
}
|
||||||
|
|
||||||
Flight findById(String id) {
|
Flight findById(String id) {
|
||||||
return this.flights.get(id);
|
return this.flights.get(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GeoResult<Flight> findGeoResultFlightById(String id) {
|
||||||
|
Flight flight = this.flights.get(id);
|
||||||
|
return new GeoResult<>(flight, new Distance(flight.altitude));
|
||||||
|
}
|
||||||
|
|
||||||
Flight save(Flight flight) {
|
Flight save(Flight flight) {
|
||||||
this.flights.put(flight.getId(), flight);
|
this.flights.put(flight.getId(), flight);
|
||||||
return flight;
|
return flight;
|
||||||
|
@ -1919,4 +2122,118 @@ public class PrePostMethodSecurityConfigurationTests {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@EnableWebMvc
|
||||||
|
@EnableWebSecurity
|
||||||
|
@EnableMethodSecurity
|
||||||
|
static class WebMvcMethodSecurityConfig {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebMvc
|
||||||
|
@EnableWebSecurity
|
||||||
|
@EnableMethodSecurity
|
||||||
|
static class WebMvcMethodSecurityCustomAdvisorConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
AuthorizationAdvisor customAdvisor(SecurityContextHolderStrategy strategy) {
|
||||||
|
JdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut();
|
||||||
|
pointcut.setPattern(".*AuthorizedPerson.*getName");
|
||||||
|
return new AuthorizationAdvisor() {
|
||||||
|
@Override
|
||||||
|
public Object invoke(MethodInvocation mi) throws Throwable {
|
||||||
|
Authentication auth = strategy.getContext().getAuthentication();
|
||||||
|
Object result = mi.proceed();
|
||||||
|
if (auth.getName().equals(result)) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
throw new AccessDeniedException("Access Denied for User '" + auth.getName() + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Pointcut getPointcut() {
|
||||||
|
return pointcut;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Advice getAdvice() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOrder() {
|
||||||
|
return AuthorizationInterceptorsOrder.POST_FILTER.getOrder() + 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
static class BasicController {
|
||||||
|
|
||||||
|
@Autowired(required = false)
|
||||||
|
BasicService service;
|
||||||
|
|
||||||
|
@GetMapping("/greetings/authorized-person")
|
||||||
|
String getAuthorizedPersonGreeting(@RequestParam String name) {
|
||||||
|
AuthorizedPerson authorizedPerson = this.service.getAuthorizedPerson(name);
|
||||||
|
return "Hello: " + authorizedPerson.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@AuthorizeReturnObject
|
||||||
|
@GetMapping(value = "/authorized-person", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||||
|
AuthorizedPerson getAuthorizedPerson(@RequestParam String name) {
|
||||||
|
return new AuthorizedPerson(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@ControllerAdvice
|
||||||
|
static class BasicControllerAdvice {
|
||||||
|
|
||||||
|
@ExceptionHandler(AccessDeniedException.class)
|
||||||
|
ResponseEntity<Map<String, String>> handleAccessDenied(AccessDeniedException ex) {
|
||||||
|
Map<String, String> responseBody = Map.of("message", ex.getMessage());
|
||||||
|
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(responseBody);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(HttpMessageNotWritableException.class)
|
||||||
|
ResponseEntity<Map<String, String>> handleHttpMessageNotWritable(HttpMessageNotWritableException ex) {
|
||||||
|
ThrowableAnalyzer throwableAnalyzer = new ThrowableAnalyzer();
|
||||||
|
Throwable[] causeChain = throwableAnalyzer.determineCauseChain(ex);
|
||||||
|
Throwable t = throwableAnalyzer.getFirstThrowableOfType(AccessDeniedException.class, causeChain);
|
||||||
|
if (t != null) {
|
||||||
|
Map<String, String> responseBody = Map.of("message", ex.getMessage());
|
||||||
|
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(responseBody);
|
||||||
|
}
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Service
|
||||||
|
static class BasicService {
|
||||||
|
|
||||||
|
@AuthorizeReturnObject
|
||||||
|
AuthorizedPerson getAuthorizedPerson(String name) {
|
||||||
|
return new AuthorizedPerson(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class AuthorizedPerson {
|
||||||
|
|
||||||
|
final String name;
|
||||||
|
|
||||||
|
AuthorizedPerson(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostAuthorize("returnObject == authentication.name")
|
||||||
|
public String getName() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,8 +47,6 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
||||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||||
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
|
|
||||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
|
||||||
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
|
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
@ -84,26 +82,6 @@ public class WebSecurityTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void ignoringMvcMatcher() throws Exception {
|
|
||||||
loadConfig(MvcMatcherConfig.class, LegacyMvcMatchingConfig.class);
|
|
||||||
this.request.setRequestURI("/path");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
|
|
||||||
setup();
|
|
||||||
this.request.setRequestURI("/path.html");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
|
|
||||||
setup();
|
|
||||||
this.request.setRequestURI("/path/");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
|
|
||||||
setup();
|
|
||||||
this.request.setRequestURI("/other");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void requestRejectedHandlerInvoked() throws ServletException, IOException {
|
public void requestRejectedHandlerInvoked() throws ServletException, IOException {
|
||||||
loadConfig(DefaultConfig.class);
|
loadConfig(DefaultConfig.class);
|
||||||
|
@ -132,30 +110,6 @@ public class WebSecurityTests {
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_BAD_REQUEST);
|
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void ignoringMvcMatcherServletPath() throws Exception {
|
|
||||||
loadConfig(MvcMatcherServletPathConfig.class, LegacyMvcMatchingConfig.class);
|
|
||||||
this.request.setServletPath("/spring");
|
|
||||||
this.request.setRequestURI("/spring/path");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
|
|
||||||
setup();
|
|
||||||
this.request.setServletPath("/spring");
|
|
||||||
this.request.setRequestURI("/spring/path.html");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
|
|
||||||
setup();
|
|
||||||
this.request.setServletPath("/spring");
|
|
||||||
this.request.setRequestURI("/spring/path/");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
|
|
||||||
setup();
|
|
||||||
this.request.setServletPath("/other");
|
|
||||||
this.request.setRequestURI("/other/path");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void loadConfig(Class<?>... configs) {
|
public void loadConfig(Class<?>... configs) {
|
||||||
this.context = new AnnotationConfigWebApplicationContext();
|
this.context = new AnnotationConfigWebApplicationContext();
|
||||||
this.context.register(configs);
|
this.context.register(configs);
|
||||||
|
@ -246,17 +200,6 @@ public class WebSecurityTests {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
|
||||||
static class LegacyMvcMatchingConfig implements WebMvcConfigurer {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void configurePathMatch(PathMatchConfigurer configurer) {
|
|
||||||
configurer.setUseSuffixPatternMatch(true);
|
|
||||||
configurer.setUseTrailingSlashMatch(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
static class RequestRejectedHandlerConfig {
|
static class RequestRejectedHandlerConfig {
|
||||||
|
|
|
@ -21,7 +21,6 @@ import okhttp3.mockwebserver.Dispatcher;
|
||||||
import okhttp3.mockwebserver.MockResponse;
|
import okhttp3.mockwebserver.MockResponse;
|
||||||
import okhttp3.mockwebserver.MockWebServer;
|
import okhttp3.mockwebserver.MockWebServer;
|
||||||
import okhttp3.mockwebserver.RecordedRequest;
|
import okhttp3.mockwebserver.RecordedRequest;
|
||||||
import org.apache.commons.lang.StringUtils;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
|
||||||
|
@ -39,6 +38,7 @@ import org.springframework.security.oauth2.server.resource.web.reactive.function
|
||||||
import org.springframework.security.web.SecurityFilterChain;
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
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;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.reactive.function.client.WebClient;
|
import org.springframework.web.reactive.function.client.WebClient;
|
||||||
|
@ -197,7 +197,7 @@ public class SecurityReactorContextConfigurationResourceServerTests {
|
||||||
public MockResponse dispatch(RecordedRequest request) {
|
public MockResponse dispatch(RecordedRequest request) {
|
||||||
MockResponse response = new MockResponse().setResponseCode(200);
|
MockResponse response = new MockResponse().setResponseCode(200);
|
||||||
String header = request.getHeader("Authorization");
|
String header = request.getHeader("Authorization");
|
||||||
if (StringUtils.isBlank(header)) {
|
if (!StringUtils.hasText(header)) {
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
return response.setBody(header);
|
return response.setBody(header);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2024 the original author or authors.
|
* Copyright 2002-2025 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.
|
||||||
|
@ -85,6 +85,7 @@ import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
|
import static org.mockito.BDDMockito.given;
|
||||||
import static org.mockito.Mockito.any;
|
import static org.mockito.Mockito.any;
|
||||||
import static org.mockito.Mockito.atLeastOnce;
|
import static org.mockito.Mockito.atLeastOnce;
|
||||||
import static org.mockito.Mockito.doCallRealMethod;
|
import static org.mockito.Mockito.doCallRealMethod;
|
||||||
|
@ -153,6 +154,7 @@ public class AuthorizeHttpRequestsConfigurerTests {
|
||||||
@Test
|
@Test
|
||||||
public void configureMvcMatcherAccessAuthorizationManagerWhenNotNullThenVerifyUse() throws Exception {
|
public void configureMvcMatcherAccessAuthorizationManagerWhenNotNullThenVerifyUse() throws Exception {
|
||||||
CustomAuthorizationManagerConfig.authorizationManager = mock(AuthorizationManager.class);
|
CustomAuthorizationManagerConfig.authorizationManager = mock(AuthorizationManager.class);
|
||||||
|
given(CustomAuthorizationManagerConfig.authorizationManager.authorize(any(), any())).willCallRealMethod();
|
||||||
this.spring.register(CustomAuthorizationManagerConfig.class, BasicController.class).autowire();
|
this.spring.register(CustomAuthorizationManagerConfig.class, BasicController.class).autowire();
|
||||||
this.mvc.perform(get("/")).andExpect(status().isOk());
|
this.mvc.perform(get("/")).andExpect(status().isOk());
|
||||||
verify(CustomAuthorizationManagerConfig.authorizationManager).check(any(), any());
|
verify(CustomAuthorizationManagerConfig.authorizationManager).check(any(), any());
|
||||||
|
@ -161,6 +163,8 @@ public class AuthorizeHttpRequestsConfigurerTests {
|
||||||
@Test
|
@Test
|
||||||
public void configureNoParameterMvcMatcherAccessAuthorizationManagerWhenNotNullThenVerifyUse() throws Exception {
|
public void configureNoParameterMvcMatcherAccessAuthorizationManagerWhenNotNullThenVerifyUse() throws Exception {
|
||||||
CustomAuthorizationManagerNoParameterConfig.authorizationManager = mock(AuthorizationManager.class);
|
CustomAuthorizationManagerNoParameterConfig.authorizationManager = mock(AuthorizationManager.class);
|
||||||
|
given(CustomAuthorizationManagerNoParameterConfig.authorizationManager.authorize(any(), any()))
|
||||||
|
.willCallRealMethod();
|
||||||
this.spring.register(CustomAuthorizationManagerNoParameterConfig.class, BasicController.class).autowire();
|
this.spring.register(CustomAuthorizationManagerNoParameterConfig.class, BasicController.class).autowire();
|
||||||
this.mvc.perform(get("/")).andExpect(status().isOk());
|
this.mvc.perform(get("/")).andExpect(status().isOk());
|
||||||
verify(CustomAuthorizationManagerNoParameterConfig.authorizationManager).check(any(), any());
|
verify(CustomAuthorizationManagerNoParameterConfig.authorizationManager).check(any(), any());
|
||||||
|
|
|
@ -48,8 +48,6 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
||||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||||
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
|
|
||||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
|
||||||
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
|
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
@ -160,67 +158,6 @@ public class AuthorizeRequestsTests {
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
|
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void mvcMatcher() throws Exception {
|
|
||||||
loadConfig(MvcMatcherConfig.class, LegacyMvcMatchingConfig.class);
|
|
||||||
this.request.setRequestURI("/path");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
|
||||||
setup();
|
|
||||||
this.request.setRequestURI("/path.html");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
|
||||||
setup();
|
|
||||||
this.request.setServletPath("/path/");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void requestWhenMvcMatcherDenyAllThenRespondsWithUnauthorized() throws Exception {
|
|
||||||
loadConfig(MvcMatcherInLambdaConfig.class, LegacyMvcMatchingConfig.class);
|
|
||||||
this.request.setRequestURI("/path");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
|
||||||
setup();
|
|
||||||
this.request.setRequestURI("/path.html");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
|
||||||
setup();
|
|
||||||
this.request.setServletPath("/path/");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void requestWhenMvcMatcherServletPathDenyAllThenMatchesOnServletPath() throws Exception {
|
|
||||||
loadConfig(MvcMatcherServletPathInLambdaConfig.class, LegacyMvcMatchingConfig.class);
|
|
||||||
this.request.setServletPath("/spring");
|
|
||||||
this.request.setRequestURI("/spring/path");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
|
||||||
setup();
|
|
||||||
this.request.setServletPath("/spring");
|
|
||||||
this.request.setRequestURI("/spring/path.html");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
|
||||||
setup();
|
|
||||||
this.request.setServletPath("/spring");
|
|
||||||
this.request.setRequestURI("/spring/path/");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
|
||||||
setup();
|
|
||||||
this.request.setServletPath("/foo");
|
|
||||||
this.request.setRequestURI("/foo/path");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
|
|
||||||
setup();
|
|
||||||
this.request.setServletPath("/");
|
|
||||||
this.request.setRequestURI("/path");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void mvcMatcherPathVariables() throws Exception {
|
public void mvcMatcherPathVariables() throws Exception {
|
||||||
loadConfig(MvcMatcherPathVariablesConfig.class);
|
loadConfig(MvcMatcherPathVariablesConfig.class);
|
||||||
|
@ -245,35 +182,6 @@ public class AuthorizeRequestsTests {
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void mvcMatcherServletPath() throws Exception {
|
|
||||||
loadConfig(MvcMatcherServletPathConfig.class, LegacyMvcMatchingConfig.class);
|
|
||||||
this.request.setServletPath("/spring");
|
|
||||||
this.request.setRequestURI("/spring/path");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
|
||||||
setup();
|
|
||||||
this.request.setServletPath("/spring");
|
|
||||||
this.request.setRequestURI("/spring/path.html");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
|
||||||
setup();
|
|
||||||
this.request.setServletPath("/spring");
|
|
||||||
this.request.setRequestURI("/spring/path/");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
|
||||||
setup();
|
|
||||||
this.request.setServletPath("/foo");
|
|
||||||
this.request.setRequestURI("/foo/path");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
|
|
||||||
setup();
|
|
||||||
this.request.setServletPath("/");
|
|
||||||
this.request.setRequestURI("/path");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void loadConfig(Class<?>... configs) {
|
public void loadConfig(Class<?>... configs) {
|
||||||
this.context = new AnnotationConfigWebApplicationContext();
|
this.context = new AnnotationConfigWebApplicationContext();
|
||||||
this.context.register(configs);
|
this.context.register(configs);
|
||||||
|
@ -639,15 +547,4 @@ public class AuthorizeRequestsTests {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
|
||||||
static class LegacyMvcMatchingConfig implements WebMvcConfigurer {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void configurePathMatch(PathMatchConfigurer configurer) {
|
|
||||||
configurer.setUseSuffixPatternMatch(true);
|
|
||||||
configurer.setUseTrailingSlashMatch(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2022 the original author or authors.
|
* Copyright 2002-2025 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.
|
||||||
|
@ -93,6 +93,7 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.request;
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.request;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.cookie;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
|
@ -613,6 +614,37 @@ public class CsrfConfigurerTests {
|
||||||
assertThat(cookies).isEmpty();
|
assertThat(cookies).isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void spaConfigForbidden() throws Exception {
|
||||||
|
this.spring.register(CsrfSpaConfig.class, AllowHttpMethodsFirewallConfig.class, BasicController.class)
|
||||||
|
.autowire();
|
||||||
|
this.mvc.perform(post("/")).andExpect(status().isForbidden());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void spaConfigOk() throws Exception {
|
||||||
|
this.spring.register(CsrfSpaConfig.class, AllowHttpMethodsFirewallConfig.class, BasicController.class)
|
||||||
|
.autowire();
|
||||||
|
this.mvc.perform(post("/").with(csrf())).andExpect(status().isOk());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void spaConfigDoubleSubmit() throws Exception {
|
||||||
|
this.spring.register(CsrfSpaConfig.class, AllowHttpMethodsFirewallConfig.class, BasicController.class)
|
||||||
|
.autowire();
|
||||||
|
var token = this.mvc.perform(post("/"))
|
||||||
|
.andExpect(status().isForbidden())
|
||||||
|
.andExpect(cookie().exists("XSRF-TOKEN"))
|
||||||
|
.andReturn()
|
||||||
|
.getResponse()
|
||||||
|
.getCookie("XSRF-TOKEN");
|
||||||
|
|
||||||
|
this.mvc
|
||||||
|
.perform(post("/").header("X-XSRF-TOKEN", token.getValue())
|
||||||
|
.cookie(new Cookie("XSRF-TOKEN", token.getValue())))
|
||||||
|
.andExpect(status().isOk());
|
||||||
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
static class AllowHttpMethodsFirewallConfig {
|
static class AllowHttpMethodsFirewallConfig {
|
||||||
|
|
||||||
|
@ -1006,6 +1038,18 @@ public class CsrfConfigurerTests {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
static class CsrfSpaConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
|
http.csrf(CsrfConfigurer::spa);
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
static class HttpBasicCsrfTokenRequestHandlerConfig {
|
static class HttpBasicCsrfTokenRequestHandlerConfig {
|
||||||
|
|
|
@ -41,8 +41,6 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
||||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||||
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
|
|
||||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
|
||||||
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
|
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
@ -80,60 +78,12 @@ public class HttpSecurityRequestMatchersTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void mvcMatcher() throws Exception {
|
|
||||||
loadConfig(MvcMatcherConfig.class, LegacyMvcMatchingConfig.class);
|
|
||||||
this.request.setServletPath("/path");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
|
||||||
setup();
|
|
||||||
this.request.setServletPath("/path.html");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
|
||||||
setup();
|
|
||||||
this.request.setServletPath("/path/");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void mvcMatcherGetFiltersNoUnsupportedMethodExceptionFromDummyRequest() {
|
public void mvcMatcherGetFiltersNoUnsupportedMethodExceptionFromDummyRequest() {
|
||||||
loadConfig(MvcMatcherConfig.class);
|
loadConfig(MvcMatcherConfig.class);
|
||||||
assertThat(this.springSecurityFilterChain.getFilters("/path")).isNotEmpty();
|
assertThat(this.springSecurityFilterChain.getFilters("/path")).isNotEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void requestMatchersMvcMatcher() throws Exception {
|
|
||||||
loadConfig(RequestMatchersMvcMatcherConfig.class, LegacyMvcMatchingConfig.class);
|
|
||||||
this.request.setServletPath("/path");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
|
||||||
setup();
|
|
||||||
this.request.setServletPath("/path.html");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
|
||||||
setup();
|
|
||||||
this.request.setServletPath("/path/");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void requestMatchersWhenMvcMatcherInLambdaThenPathIsSecured() throws Exception {
|
|
||||||
loadConfig(RequestMatchersMvcMatcherInLambdaConfig.class, LegacyMvcMatchingConfig.class);
|
|
||||||
this.request.setServletPath("/path");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
|
||||||
setup();
|
|
||||||
this.request.setServletPath("/path.html");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
|
||||||
setup();
|
|
||||||
this.request.setServletPath("/path/");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void requestMatchersMvcMatcherServletPath() throws Exception {
|
public void requestMatchersMvcMatcherServletPath() throws Exception {
|
||||||
loadConfig(RequestMatchersMvcMatcherServeltPathConfig.class);
|
loadConfig(RequestMatchersMvcMatcherServeltPathConfig.class);
|
||||||
|
@ -491,15 +441,4 @@ public class HttpSecurityRequestMatchersTests {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
|
||||||
static class LegacyMvcMatchingConfig implements WebMvcConfigurer {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void configurePathMatch(PathMatchConfigurer configurer) {
|
|
||||||
configurer.setUseSuffixPatternMatch(true);
|
|
||||||
configurer.setUseTrailingSlashMatch(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,8 +16,6 @@
|
||||||
|
|
||||||
package org.springframework.security.config.annotation.web.configurers;
|
package org.springframework.security.config.annotation.web.configurers;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
@ -38,19 +36,14 @@ import org.springframework.security.core.userdetails.User;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||||
import org.springframework.security.web.DefaultSecurityFilterChain;
|
|
||||||
import org.springframework.security.web.FilterChainProxy;
|
import org.springframework.security.web.FilterChainProxy;
|
||||||
import org.springframework.security.web.SecurityFilterChain;
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
import org.springframework.security.web.servlet.MockServletContext;
|
import org.springframework.security.web.servlet.MockServletContext;
|
||||||
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
|
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
|
||||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
|
||||||
import org.springframework.test.util.ReflectionTestUtils;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
||||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||||
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
|
|
||||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
|
||||||
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
|
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
@ -88,68 +81,12 @@ public class HttpSecuritySecurityMatchersTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void securityMatcherWhenMvcThenMvcMatcher() throws Exception {
|
|
||||||
loadConfig(SecurityMatcherMvcConfig.class, LegacyMvcMatchingConfig.class);
|
|
||||||
this.request.setServletPath("/path");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
|
||||||
setup();
|
|
||||||
this.request.setServletPath("/path.html");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
|
||||||
setup();
|
|
||||||
this.request.setServletPath("/path/");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void securityMatcherWhenMvcMatcherAndGetFiltersNoUnsupportedMethodExceptionFromDummyRequest() {
|
public void securityMatcherWhenMvcMatcherAndGetFiltersNoUnsupportedMethodExceptionFromDummyRequest() {
|
||||||
loadConfig(SecurityMatcherMvcConfig.class);
|
loadConfig(SecurityMatcherMvcConfig.class);
|
||||||
assertThat(this.springSecurityFilterChain.getFilters("/path")).isNotEmpty();
|
assertThat(this.springSecurityFilterChain.getFilters("/path")).isNotEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void securityMatchersWhenMvcThenMvcMatcher() throws Exception {
|
|
||||||
loadConfig(SecurityMatchersMvcMatcherConfig.class, LegacyMvcMatchingConfig.class);
|
|
||||||
this.request.setServletPath("/path");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
|
||||||
setup();
|
|
||||||
this.request.setServletPath("/path.html");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
|
||||||
setup();
|
|
||||||
this.request.setServletPath("/path/");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
List<RequestMatcher> requestMatchers = this.springSecurityFilterChain.getFilterChains()
|
|
||||||
.stream()
|
|
||||||
.map((chain) -> ((DefaultSecurityFilterChain) chain).getRequestMatcher())
|
|
||||||
.map((matcher) -> ReflectionTestUtils.getField(matcher, "requestMatchers"))
|
|
||||||
.map((matchers) -> (List<RequestMatcher>) matchers)
|
|
||||||
.findFirst()
|
|
||||||
.get();
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
|
||||||
assertThat(requestMatchers).hasOnlyElementsOfType(MvcRequestMatcher.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void securityMatchersWhenMvcMatcherInLambdaThenPathIsSecured() throws Exception {
|
|
||||||
loadConfig(SecurityMatchersMvcMatcherInLambdaConfig.class, LegacyMvcMatchingConfig.class);
|
|
||||||
this.request.setServletPath("/path");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
|
||||||
setup();
|
|
||||||
this.request.setServletPath("/path.html");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
|
||||||
setup();
|
|
||||||
this.request.setServletPath("/path/");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void securityMatchersMvcMatcherServletPath() throws Exception {
|
public void securityMatchersMvcMatcherServletPath() throws Exception {
|
||||||
loadConfig(SecurityMatchersMvcMatcherServletPathConfig.class);
|
loadConfig(SecurityMatchersMvcMatcherServletPathConfig.class);
|
||||||
|
@ -501,15 +438,4 @@ public class HttpSecuritySecurityMatchersTests {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
|
||||||
static class LegacyMvcMatchingConfig implements WebMvcConfigurer {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void configurePathMatch(PathMatchConfigurer configurer) {
|
|
||||||
configurer.setUseSuffixPatternMatch(true);
|
|
||||||
configurer.setUseTrailingSlashMatch(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,8 +46,6 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
||||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||||
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
|
|
||||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
|
||||||
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
|
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
@ -85,51 +83,6 @@ public class UrlAuthorizationConfigurerTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void mvcMatcher() throws Exception {
|
|
||||||
loadConfig(MvcMatcherConfig.class, LegacyMvcMatchingConfig.class);
|
|
||||||
this.request.setRequestURI("/path");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
|
||||||
setup();
|
|
||||||
this.request.setRequestURI("/path.html");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
|
||||||
setup();
|
|
||||||
this.request.setServletPath("/path/");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void mvcMatcherServletPath() throws Exception {
|
|
||||||
loadConfig(MvcMatcherServletPathConfig.class, LegacyMvcMatchingConfig.class);
|
|
||||||
this.request.setServletPath("/spring");
|
|
||||||
this.request.setRequestURI("/spring/path");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
|
||||||
setup();
|
|
||||||
this.request.setServletPath("/spring");
|
|
||||||
this.request.setRequestURI("/spring/path.html");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
|
||||||
setup();
|
|
||||||
this.request.setServletPath("/spring");
|
|
||||||
this.request.setRequestURI("/spring/path/");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
|
||||||
setup();
|
|
||||||
this.request.setServletPath("/foo");
|
|
||||||
this.request.setRequestURI("/foo/path");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
|
|
||||||
setup();
|
|
||||||
this.request.setServletPath("/");
|
|
||||||
this.request.setRequestURI("/path");
|
|
||||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
|
||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void anonymousUrlAuthorization() {
|
public void anonymousUrlAuthorization() {
|
||||||
loadConfig(AnonymousUrlAuthorizationConfig.class);
|
loadConfig(AnonymousUrlAuthorizationConfig.class);
|
||||||
|
@ -258,17 +211,6 @@ public class UrlAuthorizationConfigurerTests {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
|
||||||
static class LegacyMvcMatchingConfig implements WebMvcConfigurer {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void configurePathMatch(PathMatchConfigurer configurer) {
|
|
||||||
configurer.setUseSuffixPatternMatch(true);
|
|
||||||
configurer.setUseTrailingSlashMatch(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebMvc
|
@EnableWebMvc
|
||||||
|
|
|
@ -43,7 +43,9 @@ import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||||
import org.springframework.security.web.SecurityFilterChain;
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
|
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
|
||||||
|
import org.springframework.security.web.authentication.preauth.x509.SubjectX500PrincipalExtractor;
|
||||||
import org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter;
|
import org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter;
|
||||||
|
import org.springframework.security.web.authentication.preauth.x509.X509TestUtils;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
@ -155,6 +157,28 @@ public class X509ConfigurerTests {
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void x509WhenSubjectX500PrincipalExtractor() throws Exception {
|
||||||
|
this.spring.register(SubjectX500PrincipalExtractorConfig.class).autowire();
|
||||||
|
X509Certificate certificate = loadCert("rod.cer");
|
||||||
|
// @formatter:off
|
||||||
|
this.mvc.perform(get("/").with(x509(certificate)))
|
||||||
|
.andExpect((result) -> assertThat(result.getRequest().getSession(false)).isNull())
|
||||||
|
.andExpect(authenticated().withUsername("rod"));
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void x509WhenSubjectX500PrincipalExtractorBean() throws Exception {
|
||||||
|
this.spring.register(SubjectX500PrincipalExtractorEmailConfig.class).autowire();
|
||||||
|
X509Certificate certificate = X509TestUtils.buildTestCertificate();
|
||||||
|
// @formatter:off
|
||||||
|
this.mvc.perform(get("/").with(x509(certificate)))
|
||||||
|
.andExpect((result) -> assertThat(result.getRequest().getSession(false)).isNull())
|
||||||
|
.andExpect(authenticated().withUsername("luke@monkeymachine"));
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
private <T extends Certificate> T loadCert(String location) {
|
private <T extends Certificate> T loadCert(String location) {
|
||||||
try (InputStream is = new ClassPathResource(location).getInputStream()) {
|
try (InputStream is = new ClassPathResource(location).getInputStream()) {
|
||||||
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
|
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
|
||||||
|
@ -360,4 +384,60 @@ public class X509ConfigurerTests {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
static class SubjectX500PrincipalExtractorConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
http
|
||||||
|
.x509((x509) -> x509
|
||||||
|
.x509PrincipalExtractor(new SubjectX500PrincipalExtractor())
|
||||||
|
);
|
||||||
|
// @formatter:on
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
UserDetailsService userDetailsService() {
|
||||||
|
UserDetails user = User.withDefaultPasswordEncoder()
|
||||||
|
.username("rod")
|
||||||
|
.password("password")
|
||||||
|
.roles("USER", "ADMIN")
|
||||||
|
.build();
|
||||||
|
return new InMemoryUserDetailsManager(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
static class SubjectX500PrincipalExtractorEmailConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||||
|
SubjectX500PrincipalExtractor principalExtractor = new SubjectX500PrincipalExtractor();
|
||||||
|
principalExtractor.setExtractPrincipalNameFromEmail(true);
|
||||||
|
// @formatter:off
|
||||||
|
http
|
||||||
|
.x509((x509) -> x509
|
||||||
|
.x509PrincipalExtractor(principalExtractor)
|
||||||
|
);
|
||||||
|
// @formatter:on
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
UserDetailsService userDetailsService() {
|
||||||
|
UserDetails user = User.withDefaultPasswordEncoder()
|
||||||
|
.username("luke@monkeymachine")
|
||||||
|
.password("password")
|
||||||
|
.roles("USER", "ADMIN")
|
||||||
|
.build();
|
||||||
|
return new InMemoryUserDetailsManager(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,12 +128,14 @@ import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthen
|
||||||
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
|
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
|
||||||
import org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver;
|
import org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver;
|
||||||
import org.springframework.security.oauth2.server.resource.web.access.BearerTokenAccessDeniedHandler;
|
import org.springframework.security.oauth2.server.resource.web.access.BearerTokenAccessDeniedHandler;
|
||||||
|
import org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationConverter;
|
||||||
import org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter;
|
import org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter;
|
||||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||||
import org.springframework.security.web.SecurityFilterChain;
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
import org.springframework.security.web.access.AccessDeniedHandler;
|
import org.springframework.security.web.access.AccessDeniedHandler;
|
||||||
import org.springframework.security.web.access.AccessDeniedHandlerImpl;
|
import org.springframework.security.web.access.AccessDeniedHandlerImpl;
|
||||||
|
import org.springframework.security.web.authentication.AuthenticationConverter;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
import org.springframework.test.web.servlet.MvcResult;
|
import org.springframework.test.web.servlet.MvcResult;
|
||||||
import org.springframework.test.web.servlet.ResultMatcher;
|
import org.springframework.test.web.servlet.ResultMatcher;
|
||||||
|
@ -760,13 +762,6 @@ public class OAuth2ResourceServerConfigurerTests {
|
||||||
assertThat(oauth2.getBearerTokenResolver()).isEqualTo(resolver);
|
assertThat(oauth2.getBearerTokenResolver()).isEqualTo(resolver);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getBearerTokenResolverWhenNoResolverSpecifiedThenTheDefaultIsUsed() {
|
|
||||||
ApplicationContext context = this.spring.context(new GenericWebApplicationContext()).getContext();
|
|
||||||
OAuth2ResourceServerConfigurer oauth2 = new OAuth2ResourceServerConfigurer(context);
|
|
||||||
assertThat(oauth2.getBearerTokenResolver()).isInstanceOf(DefaultBearerTokenResolver.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void requestWhenCustomAuthenticationDetailsSourceThenUsed() throws Exception {
|
public void requestWhenCustomAuthenticationDetailsSourceThenUsed() throws Exception {
|
||||||
this.spring.register(CustomAuthenticationDetailsSource.class, JwtDecoderConfig.class, BasicController.class)
|
this.spring.register(CustomAuthenticationDetailsSource.class, JwtDecoderConfig.class, BasicController.class)
|
||||||
|
@ -1416,6 +1411,47 @@ public class OAuth2ResourceServerConfigurerTests {
|
||||||
verify(authenticationConverter).convert(any(), any());
|
verify(authenticationConverter).convert(any(), any());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getAuthenticationConverterWhenDuplicateConverterBeansAndAnotherOnTheDslThenTheDslOneIsUsed() {
|
||||||
|
AuthenticationConverter converter = mock(AuthenticationConverter.class);
|
||||||
|
AuthenticationConverter converterBean = mock(AuthenticationConverter.class);
|
||||||
|
GenericWebApplicationContext context = new GenericWebApplicationContext();
|
||||||
|
context.registerBean("converterOne", AuthenticationConverter.class, () -> converterBean);
|
||||||
|
context.registerBean("converterTwo", AuthenticationConverter.class, () -> converterBean);
|
||||||
|
this.spring.context(context).autowire();
|
||||||
|
OAuth2ResourceServerConfigurer oauth2 = new OAuth2ResourceServerConfigurer(context);
|
||||||
|
oauth2.authenticationConverter(converter);
|
||||||
|
assertThat(oauth2.getAuthenticationConverter()).isEqualTo(converter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getAuthenticationConverterWhenConverterBeanAndAnotherOnTheDslThenTheDslOneIsUsed() {
|
||||||
|
AuthenticationConverter converter = mock(AuthenticationConverter.class);
|
||||||
|
AuthenticationConverter converterBean = mock(AuthenticationConverter.class);
|
||||||
|
GenericWebApplicationContext context = new GenericWebApplicationContext();
|
||||||
|
context.registerBean(AuthenticationConverter.class, () -> converterBean);
|
||||||
|
this.spring.context(context).autowire();
|
||||||
|
OAuth2ResourceServerConfigurer oauth2 = new OAuth2ResourceServerConfigurer(context);
|
||||||
|
oauth2.authenticationConverter(converter);
|
||||||
|
assertThat(oauth2.getAuthenticationConverter()).isEqualTo(converter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getAuthenticationConverterWhenDuplicateConverterBeansThenWiringException() {
|
||||||
|
assertThatExceptionOfType(BeanCreationException.class)
|
||||||
|
.isThrownBy(
|
||||||
|
() -> this.spring.register(MultipleAuthenticationConverterBeansConfig.class, JwtDecoderConfig.class)
|
||||||
|
.autowire())
|
||||||
|
.withRootCauseInstanceOf(NoUniqueBeanDefinitionException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getAuthenticationConverterWhenNoConverterSpecifiedThenTheDefaultIsUsed() {
|
||||||
|
ApplicationContext context = this.spring.context(new GenericWebApplicationContext()).getContext();
|
||||||
|
OAuth2ResourceServerConfigurer oauth2 = new OAuth2ResourceServerConfigurer(context);
|
||||||
|
assertThat(oauth2.getAuthenticationConverter()).isInstanceOf(BearerTokenAuthenticationConverter.class);
|
||||||
|
}
|
||||||
|
|
||||||
private static <T> void registerMockBean(GenericApplicationContext context, String name, Class<T> clazz) {
|
private static <T> void registerMockBean(GenericApplicationContext context, String name, Class<T> clazz) {
|
||||||
context.registerBean(name, clazz, () -> mock(clazz));
|
context.registerBean(name, clazz, () -> mock(clazz));
|
||||||
}
|
}
|
||||||
|
@ -2517,6 +2553,43 @@ public class OAuth2ResourceServerConfigurerTests {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
static class MultipleAuthenticationConverterBeansConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
http
|
||||||
|
.authorizeRequests()
|
||||||
|
.anyRequest().authenticated()
|
||||||
|
.and()
|
||||||
|
.oauth2ResourceServer()
|
||||||
|
.jwt();
|
||||||
|
return http.build();
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
AuthenticationConverter authenticationConverterOne() {
|
||||||
|
DefaultBearerTokenResolver resolver = new DefaultBearerTokenResolver();
|
||||||
|
resolver.setAllowUriQueryParameter(true);
|
||||||
|
BearerTokenAuthenticationConverter authenticationConverter = new BearerTokenAuthenticationConverter();
|
||||||
|
authenticationConverter.setBearerTokenResolver(resolver);
|
||||||
|
return authenticationConverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
AuthenticationConverter authenticationConverterTwo() {
|
||||||
|
DefaultBearerTokenResolver resolver = new DefaultBearerTokenResolver();
|
||||||
|
resolver.setAllowUriQueryParameter(true);
|
||||||
|
BearerTokenAuthenticationConverter authenticationConverter = new BearerTokenAuthenticationConverter();
|
||||||
|
authenticationConverter.setBearerTokenResolver(resolver);
|
||||||
|
return authenticationConverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
static class MultipleIssuersConfig {
|
static class MultipleIssuersConfig {
|
||||||
|
|
|
@ -253,7 +253,7 @@ public class Saml2LoginConfigurerTests {
|
||||||
public void authenticationRequestWhenAuthenticationRequestResolverBeanThenUses() throws Exception {
|
public void authenticationRequestWhenAuthenticationRequestResolverBeanThenUses() throws Exception {
|
||||||
this.spring.register(CustomAuthenticationRequestResolverBean.class).autowire();
|
this.spring.register(CustomAuthenticationRequestResolverBean.class).autowire();
|
||||||
MvcResult result = this.mvc.perform(get("/saml2/authenticate/registration-id")).andReturn();
|
MvcResult result = this.mvc.perform(get("/saml2/authenticate/registration-id")).andReturn();
|
||||||
UriComponents components = UriComponentsBuilder.fromHttpUrl(result.getResponse().getRedirectedUrl()).build();
|
UriComponents components = UriComponentsBuilder.fromUriString(result.getResponse().getRedirectedUrl()).build();
|
||||||
String samlRequest = components.getQueryParams().getFirst("SAMLRequest");
|
String samlRequest = components.getQueryParams().getFirst("SAMLRequest");
|
||||||
String decoded = URLDecoder.decode(samlRequest, "UTF-8");
|
String decoded = URLDecoder.decode(samlRequest, "UTF-8");
|
||||||
String inflated = Saml2Utils.samlInflate(Saml2Utils.samlDecode(decoded));
|
String inflated = Saml2Utils.samlInflate(Saml2Utils.samlDecode(decoded));
|
||||||
|
@ -264,7 +264,7 @@ public class Saml2LoginConfigurerTests {
|
||||||
public void authenticationRequestWhenAuthenticationRequestResolverDslThenUses() throws Exception {
|
public void authenticationRequestWhenAuthenticationRequestResolverDslThenUses() throws Exception {
|
||||||
this.spring.register(CustomAuthenticationRequestResolverDsl.class).autowire();
|
this.spring.register(CustomAuthenticationRequestResolverDsl.class).autowire();
|
||||||
MvcResult result = this.mvc.perform(get("/saml2/authenticate/registration-id")).andReturn();
|
MvcResult result = this.mvc.perform(get("/saml2/authenticate/registration-id")).andReturn();
|
||||||
UriComponents components = UriComponentsBuilder.fromHttpUrl(result.getResponse().getRedirectedUrl()).build();
|
UriComponents components = UriComponentsBuilder.fromUriString(result.getResponse().getRedirectedUrl()).build();
|
||||||
String samlRequest = components.getQueryParams().getFirst("SAMLRequest");
|
String samlRequest = components.getQueryParams().getFirst("SAMLRequest");
|
||||||
String decoded = URLDecoder.decode(samlRequest, "UTF-8");
|
String decoded = URLDecoder.decode(samlRequest, "UTF-8");
|
||||||
String inflated = Saml2Utils.samlInflate(Saml2Utils.samlDecode(decoded));
|
String inflated = Saml2Utils.samlInflate(Saml2Utils.samlDecode(decoded));
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2022 the original author or authors.
|
* Copyright 2002-2025 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.
|
||||||
|
@ -389,14 +389,14 @@ public class Saml2LogoutConfigurerTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void saml2LogoutRequestWhenInvalidSamlRequestThen401() throws Exception {
|
public void saml2LogoutRequestWhenInvalidSamlRequestThen302Redirect() throws Exception {
|
||||||
this.spring.register(Saml2LogoutDefaultsConfig.class).autowire();
|
this.spring.register(Saml2LogoutDefaultsConfig.class).autowire();
|
||||||
this.mvc
|
this.mvc
|
||||||
.perform(get("/logout/saml2/slo").param("SAMLRequest", this.apLogoutRequest)
|
.perform(get("/logout/saml2/slo").param("SAMLRequest", this.apLogoutRequest)
|
||||||
.param("RelayState", this.apLogoutRequestRelayState)
|
.param("RelayState", this.apLogoutRequestRelayState)
|
||||||
.param("SigAlg", this.apLogoutRequestSigAlg)
|
.param("SigAlg", this.apLogoutRequestSigAlg)
|
||||||
.with(authentication(this.user)))
|
.with(authentication(this.user)))
|
||||||
.andExpect(status().isUnauthorized());
|
.andExpect(status().isFound());
|
||||||
verifyNoInteractions(getBean(LogoutHandler.class));
|
verifyNoInteractions(getBean(LogoutHandler.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,12 +30,12 @@ import java.util.TreeMap;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.apache.commons.lang.StringUtils;
|
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import org.springframework.core.io.ClassPathResource;
|
import org.springframework.core.io.ClassPathResource;
|
||||||
import org.springframework.security.config.http.SecurityFiltersAssertions;
|
import org.springframework.security.config.http.SecurityFiltersAssertions;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ public class XsdDocumentedTests {
|
||||||
|
|
||||||
String schema31xDocumentLocation = "org/springframework/security/config/spring-security-3.1.xsd";
|
String schema31xDocumentLocation = "org/springframework/security/config/spring-security-3.1.xsd";
|
||||||
|
|
||||||
String schemaDocumentLocation = "org/springframework/security/config/spring-security-6.5.xsd";
|
String schemaDocumentLocation = "org/springframework/security/config/spring-security-7.0.xsd";
|
||||||
|
|
||||||
XmlSupport xml = new XmlSupport();
|
XmlSupport xml = new XmlSupport();
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ public class XsdDocumentedTests {
|
||||||
.flatMap(XmlNode::children)
|
.flatMap(XmlNode::children)
|
||||||
.flatMap(XmlNode::children)
|
.flatMap(XmlNode::children)
|
||||||
.map((node) -> node.attribute("value"))
|
.map((node) -> node.attribute("value"))
|
||||||
.filter(StringUtils::isNotEmpty)
|
.filter(StringUtils::hasText)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
SecurityFiltersAssertions.assertEquals(nodes);
|
SecurityFiltersAssertions.assertEquals(nodes);
|
||||||
|
@ -129,7 +129,7 @@ public class XsdDocumentedTests {
|
||||||
.flatMap(XmlNode::children)
|
.flatMap(XmlNode::children)
|
||||||
.flatMap(XmlNode::children)
|
.flatMap(XmlNode::children)
|
||||||
.map((node) -> node.attribute("value"))
|
.map((node) -> node.attribute("value"))
|
||||||
.filter(StringUtils::isNotEmpty)
|
.filter(StringUtils::hasText)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
assertThat(nodes).isEqualTo(expected);
|
assertThat(nodes).isEqualTo(expected);
|
||||||
|
@ -151,8 +151,8 @@ public class XsdDocumentedTests {
|
||||||
.list((dir, name) -> name.endsWith(".xsd"));
|
.list((dir, name) -> name.endsWith(".xsd"));
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
assertThat(schemas.length)
|
assertThat(schemas.length)
|
||||||
.withFailMessage("the count is equal to 27, if not then schemaDocument needs updating")
|
.withFailMessage("the count is equal to 28, if not then schemaDocument needs updating")
|
||||||
.isEqualTo(27);
|
.isEqualTo(28);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -254,8 +254,6 @@ public class InterceptUrlConfigTests {
|
||||||
public void requestWhenUsingMvcMatchersThenAuthorizesRequestsAccordingly() throws Exception {
|
public void requestWhenUsingMvcMatchersThenAuthorizesRequestsAccordingly() throws Exception {
|
||||||
this.spring.configLocations(this.xml("MvcMatchers")).autowire();
|
this.spring.configLocations(this.xml("MvcMatchers")).autowire();
|
||||||
this.mvc.perform(get("/path")).andExpect(status().isUnauthorized());
|
this.mvc.perform(get("/path")).andExpect(status().isUnauthorized());
|
||||||
this.mvc.perform(get("/path.html")).andExpect(status().isUnauthorized());
|
|
||||||
this.mvc.perform(get("/path/")).andExpect(status().isUnauthorized());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -304,10 +302,6 @@ public class InterceptUrlConfigTests {
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
this.mvc.perform(get("/spring/path").servletPath("/spring"))
|
this.mvc.perform(get("/spring/path").servletPath("/spring"))
|
||||||
.andExpect(status().isUnauthorized());
|
.andExpect(status().isUnauthorized());
|
||||||
this.mvc.perform(get("/spring/path.html").servletPath("/spring"))
|
|
||||||
.andExpect(status().isUnauthorized());
|
|
||||||
this.mvc.perform(get("/spring/path/").servletPath("/spring"))
|
|
||||||
.andExpect(status().isUnauthorized());
|
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.security.AccessController;
|
import java.security.AccessController;
|
||||||
import java.security.Principal;
|
import java.security.Principal;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
@ -91,6 +92,7 @@ import org.springframework.security.web.authentication.AnonymousAuthenticationFi
|
||||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||||
import org.springframework.security.web.authentication.logout.LogoutFilter;
|
import org.springframework.security.web.authentication.logout.LogoutFilter;
|
||||||
import org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter;
|
import org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter;
|
||||||
|
import org.springframework.security.web.authentication.preauth.x509.X509TestUtils;
|
||||||
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
|
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
|
||||||
import org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter;
|
import org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter;
|
||||||
import org.springframework.security.web.authentication.ui.DefaultResourcesFilter;
|
import org.springframework.security.web.authentication.ui.DefaultResourcesFilter;
|
||||||
|
@ -398,6 +400,27 @@ public class MiscHttpConfigTests {
|
||||||
.containsSubsequence(CsrfFilter.class, X509AuthenticationFilter.class, ExceptionTranslationFilter.class);
|
.containsSubsequence(CsrfFilter.class, X509AuthenticationFilter.class, ExceptionTranslationFilter.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getWhenUsingX509PrincipalExtractorRef() throws Exception {
|
||||||
|
this.spring.configLocations(xml("X509PrincipalExtractorRef")).autowire();
|
||||||
|
X509Certificate certificate = X509TestUtils.buildTestCertificate();
|
||||||
|
RequestPostProcessor x509 = x509(certificate);
|
||||||
|
// @formatter:off
|
||||||
|
this.mvc.perform(get("/protected").with(x509))
|
||||||
|
.andExpect(status().isOk());
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getWhenUsingX509PrincipalExtractorRefAndSubjectPrincipalRegex() throws Exception {
|
||||||
|
String xmlResourceName = "X509PrincipalExtractorRefAndSubjectPrincipalRegex";
|
||||||
|
// @formatter:off
|
||||||
|
assertThatExceptionOfType(BeanDefinitionParsingException.class)
|
||||||
|
.isThrownBy(() -> this.spring.configLocations(xml(xmlResourceName)).autowire())
|
||||||
|
.withMessage("Configuration problem: The attribute 'principal-extractor-ref' cannot be used together with the 'subject-principal-regex' attribute within <x509>\n" + "Offending resource: class path resource [org/springframework/security/config/http/MiscHttpConfigTests-X509PrincipalExtractorRefAndSubjectPrincipalRegex.xml]");
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getWhenUsingX509AndPropertyPlaceholderThenSubjectPrincipalRegexIsConfigured() throws Exception {
|
public void getWhenUsingX509AndPropertyPlaceholderThenSubjectPrincipalRegexIsConfigured() throws Exception {
|
||||||
System.setProperty("subject_principal_regex", "OU=(.*?)(?:,|$)");
|
System.setProperty("subject_principal_regex", "OU=(.*?)(?:,|$)");
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2022 the original author or authors.
|
* Copyright 2002-2025 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.
|
||||||
|
@ -25,7 +25,6 @@ import java.time.Instant;
|
||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
@ -50,13 +49,11 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
import org.w3c.dom.Element;
|
import org.w3c.dom.Element;
|
||||||
|
|
||||||
import org.springframework.beans.BeanMetadataElement;
|
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||||
import org.springframework.beans.factory.DisposableBean;
|
import org.springframework.beans.factory.DisposableBean;
|
||||||
import org.springframework.beans.factory.FactoryBean;
|
import org.springframework.beans.factory.FactoryBean;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.config.BeanReference;
|
|
||||||
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
||||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
|
||||||
import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate;
|
import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate;
|
||||||
import org.springframework.beans.factory.xml.ParserContext;
|
import org.springframework.beans.factory.xml.ParserContext;
|
||||||
import org.springframework.beans.factory.xml.XmlReaderContext;
|
import org.springframework.beans.factory.xml.XmlReaderContext;
|
||||||
|
@ -85,12 +82,14 @@ import org.springframework.security.oauth2.jwt.JwtClaimNames;
|
||||||
import org.springframework.security.oauth2.jwt.JwtDecoder;
|
import org.springframework.security.oauth2.jwt.JwtDecoder;
|
||||||
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
|
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
|
||||||
import org.springframework.security.oauth2.jwt.TestJwts;
|
import org.springframework.security.oauth2.jwt.TestJwts;
|
||||||
|
import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthenticationToken;
|
||||||
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
|
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
|
||||||
import org.springframework.security.oauth2.server.resource.introspection.NimbusOpaqueTokenIntrospector;
|
import org.springframework.security.oauth2.server.resource.introspection.NimbusOpaqueTokenIntrospector;
|
||||||
import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenAuthenticationConverter;
|
import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenAuthenticationConverter;
|
||||||
import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;
|
import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;
|
||||||
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
|
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
|
||||||
import org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;
|
import org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;
|
||||||
|
import org.springframework.security.web.authentication.AuthenticationConverter;
|
||||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
import org.springframework.test.web.servlet.MvcResult;
|
import org.springframework.test.web.servlet.MvcResult;
|
||||||
|
@ -462,6 +461,24 @@ public class OAuth2ResourceServerBeanDefinitionParserTests {
|
||||||
verify(bearerTokenResolver).resolve(any(HttpServletRequest.class));
|
verify(bearerTokenResolver).resolve(any(HttpServletRequest.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getWhenCustomAuthenticationConverterThenUses() throws Exception {
|
||||||
|
this.spring
|
||||||
|
.configLocations(xml("MockAuthenticationConverter"), xml("MockJwtDecoder"), xml("AuthenticationConverter"))
|
||||||
|
.autowire();
|
||||||
|
JwtDecoder decoder = this.spring.getContext().getBean(JwtDecoder.class);
|
||||||
|
given(decoder.decode("token")).willReturn(TestJwts.jwt().build());
|
||||||
|
AuthenticationConverter authenticationConverter = this.spring.getContext()
|
||||||
|
.getBean(AuthenticationConverter.class);
|
||||||
|
given(authenticationConverter.convert(any(HttpServletRequest.class)))
|
||||||
|
.willReturn(new BearerTokenAuthenticationToken("token"));
|
||||||
|
|
||||||
|
this.mvc.perform(get("/")).andExpect(status().isNotFound());
|
||||||
|
|
||||||
|
verify(decoder).decode("token");
|
||||||
|
verify(authenticationConverter).convert(any(HttpServletRequest.class));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void requestWhenBearerTokenResolverAllowsRequestBodyThenEitherHeaderOrRequestBodyIsAccepted()
|
public void requestWhenBearerTokenResolverAllowsRequestBodyThenEitherHeaderOrRequestBodyIsAccepted()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
@ -521,14 +538,6 @@ public class OAuth2ResourceServerBeanDefinitionParserTests {
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getBearerTokenResolverWhenNoResolverSpecifiedThenTheDefaultIsUsed() {
|
|
||||||
OAuth2ResourceServerBeanDefinitionParser oauth2 = new OAuth2ResourceServerBeanDefinitionParser(
|
|
||||||
mock(BeanReference.class), mock(List.class), mock(Map.class), mock(Map.class), mock(List.class),
|
|
||||||
mock(BeanMetadataElement.class));
|
|
||||||
assertThat(oauth2.getBearerTokenResolver(mock(Element.class))).isInstanceOf(RootBeanDefinition.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void requestWhenCustomJwtDecoderThenUsed() throws Exception {
|
public void requestWhenCustomJwtDecoderThenUsed() throws Exception {
|
||||||
this.spring.configLocations(xml("MockJwtDecoder"), xml("Jwt")).autowire();
|
this.spring.configLocations(xml("MockJwtDecoder"), xml("Jwt")).autowire();
|
||||||
|
@ -545,6 +554,12 @@ public class OAuth2ResourceServerBeanDefinitionParserTests {
|
||||||
.isThrownBy(() -> this.spring.configLocations(xml("JwtDecoderAndJwkSetUri")).autowire());
|
.isThrownBy(() -> this.spring.configLocations(xml("JwtDecoderAndJwkSetUri")).autowire());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void configureWhenAuthenticationConverterAndJwkSetUriThenException() {
|
||||||
|
assertThatExceptionOfType(BeanDefinitionStoreException.class).isThrownBy(
|
||||||
|
() -> this.spring.configLocations(xml("AuthenticationConverterAndBearerTokenResolver")).autowire());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void requestWhenRealmNameConfiguredThenUsesOnUnauthenticated() throws Exception {
|
public void requestWhenRealmNameConfiguredThenUsesOnUnauthenticated() throws Exception {
|
||||||
this.spring.configLocations(xml("MockJwtDecoder"), xml("AuthenticationEntryPoint")).autowire();
|
this.spring.configLocations(xml("MockJwtDecoder"), xml("AuthenticationEntryPoint")).autowire();
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2022 the original author or authors.
|
* Copyright 2002-2025 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.
|
||||||
|
@ -287,15 +287,16 @@ public class Saml2LogoutBeanDefinitionParserTests {
|
||||||
.andExpect(status().isBadRequest());
|
.andExpect(status().isBadRequest());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// gh-14635
|
||||||
@Test
|
@Test
|
||||||
public void saml2LogoutRequestWhenInvalidSamlRequestThen401() throws Exception {
|
public void saml2LogoutRequestWhenInvalidSamlRequestThen302Redirect() throws Exception {
|
||||||
this.spring.configLocations(this.xml("Default")).autowire();
|
this.spring.configLocations(this.xml("Default")).autowire();
|
||||||
this.mvc
|
this.mvc
|
||||||
.perform(get("/logout/saml2/slo").param("SAMLRequest", this.apLogoutRequest)
|
.perform(get("/logout/saml2/slo").param("SAMLRequest", this.apLogoutRequest)
|
||||||
.param("RelayState", this.apLogoutRequestRelayState)
|
.param("RelayState", this.apLogoutRequestRelayState)
|
||||||
.param("SigAlg", this.apLogoutRequestSigAlg)
|
.param("SigAlg", this.apLogoutRequestSigAlg)
|
||||||
.with(authentication(this.saml2User)))
|
.with(authentication(this.saml2User)))
|
||||||
.andExpect(status().isUnauthorized());
|
.andExpect(status().isFound());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2020 the original author or authors.
|
* Copyright 2002-2025 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.
|
||||||
|
@ -115,6 +115,24 @@ public class CommonOAuth2ProviderTests {
|
||||||
assertThat(registration.getRegistrationId()).isEqualTo("123");
|
assertThat(registration.getRegistrationId()).isEqualTo("123");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getBuilderWhenXShouldHaveXSettings() {
|
||||||
|
ClientRegistration registration = build(CommonOAuth2Provider.X);
|
||||||
|
ProviderDetails providerDetails = registration.getProviderDetails();
|
||||||
|
assertThat(providerDetails.getAuthorizationUri()).isEqualTo("https://x.com/i/oauth2/authorize");
|
||||||
|
assertThat(providerDetails.getTokenUri()).isEqualTo("https://api.x.com/2/oauth2/token");
|
||||||
|
assertThat(providerDetails.getUserInfoEndpoint().getUri()).isEqualTo("https://api.x.com/2/users/me");
|
||||||
|
assertThat(providerDetails.getUserInfoEndpoint().getUserNameAttributeName()).isEqualTo("username");
|
||||||
|
assertThat(providerDetails.getJwkSetUri()).isNull();
|
||||||
|
assertThat(registration.getClientAuthenticationMethod())
|
||||||
|
.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_POST);
|
||||||
|
assertThat(registration.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);
|
||||||
|
assertThat(registration.getRedirectUri()).isEqualTo(DEFAULT_REDIRECT_URL);
|
||||||
|
assertThat(registration.getScopes()).containsOnly("users.read", "tweet.read");
|
||||||
|
assertThat(registration.getClientName()).isEqualTo("X");
|
||||||
|
assertThat(registration.getRegistrationId()).isEqualTo("123");
|
||||||
|
}
|
||||||
|
|
||||||
private ClientRegistration build(CommonOAuth2Provider provider) {
|
private ClientRegistration build(CommonOAuth2Provider provider) {
|
||||||
return builder(provider).build();
|
return builder(provider).build();
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,8 +18,6 @@ package org.springframework.security.config.web.server;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
@ -114,12 +112,13 @@ public class CorsSpecTests {
|
||||||
.exchange()
|
.exchange()
|
||||||
.returnResult(String.class);
|
.returnResult(String.class);
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
Map<String, List<String>> responseHeaders = response.getResponseHeaders();
|
HttpHeaders responseHeaders = response.getResponseHeaders();
|
||||||
if (!this.expectedHeaders.isEmpty()) {
|
if (!this.expectedHeaders.isEmpty()) {
|
||||||
assertThat(responseHeaders).describedAs(response.toString()).containsAllEntriesOf(this.expectedHeaders);
|
this.expectedHeaders.forEach(
|
||||||
|
(headerName, headerValues) -> assertThat(responseHeaders.get(headerName)).isEqualTo(headerValues));
|
||||||
}
|
}
|
||||||
if (!this.headerNamesNotPresent.isEmpty()) {
|
if (!this.headerNamesNotPresent.isEmpty()) {
|
||||||
assertThat(responseHeaders.keySet()).doesNotContainAnyElementsOf(this.headerNamesNotPresent);
|
assertThat(responseHeaders.headerNames()).doesNotContainAnyElementsOf(this.headerNamesNotPresent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,8 +18,6 @@ package org.springframework.security.config.web.server;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
@ -80,14 +78,14 @@ public class HeaderSpecTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void headersWhenDisableThenNoSecurityHeaders() {
|
public void headersWhenDisableThenNoSecurityHeaders() {
|
||||||
new HashSet<>(this.expectedHeaders.keySet()).forEach(this::expectHeaderNamesNotPresent);
|
new HashSet<>(this.expectedHeaders.headerNames()).forEach(this::expectHeaderNamesNotPresent);
|
||||||
this.http.headers().disable();
|
this.http.headers().disable();
|
||||||
assertHeaders();
|
assertHeaders();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void headersWhenDisableInLambdaThenNoSecurityHeaders() {
|
public void headersWhenDisableInLambdaThenNoSecurityHeaders() {
|
||||||
new HashSet<>(this.expectedHeaders.keySet()).forEach(this::expectHeaderNamesNotPresent);
|
new HashSet<>(this.expectedHeaders.headerNames()).forEach(this::expectHeaderNamesNotPresent);
|
||||||
this.http.headers((headers) -> headers.disable());
|
this.http.headers((headers) -> headers.disable());
|
||||||
assertHeaders();
|
assertHeaders();
|
||||||
}
|
}
|
||||||
|
@ -515,12 +513,13 @@ public class HeaderSpecTests {
|
||||||
.uri("https://example.com/")
|
.uri("https://example.com/")
|
||||||
.exchange()
|
.exchange()
|
||||||
.returnResult(String.class);
|
.returnResult(String.class);
|
||||||
Map<String, List<String>> responseHeaders = response.getResponseHeaders();
|
HttpHeaders responseHeaders = response.getResponseHeaders();
|
||||||
if (!this.expectedHeaders.isEmpty()) {
|
if (!this.expectedHeaders.isEmpty()) {
|
||||||
assertThat(responseHeaders).describedAs(response.toString()).containsAllEntriesOf(this.expectedHeaders);
|
this.expectedHeaders.forEach(
|
||||||
|
(headerName, headerValues) -> assertThat(responseHeaders.get(headerName)).isEqualTo(headerValues));
|
||||||
}
|
}
|
||||||
if (!this.headerNamesNotPresent.isEmpty()) {
|
if (!this.headerNamesNotPresent.isEmpty()) {
|
||||||
assertThat(responseHeaders.keySet()).doesNotContainAnyElementsOf(this.headerNamesNotPresent);
|
assertThat(responseHeaders.headerNames()).doesNotContainAnyElementsOf(this.headerNamesNotPresent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -945,7 +945,7 @@ public class OidcLogoutSpecTests {
|
||||||
private MockResponse toMockResponse(FluxExchangeResult<String> result) {
|
private MockResponse toMockResponse(FluxExchangeResult<String> result) {
|
||||||
MockResponse response = new MockResponse();
|
MockResponse response = new MockResponse();
|
||||||
response.setResponseCode(result.getStatus().value());
|
response.setResponseCode(result.getStatus().value());
|
||||||
for (String name : result.getResponseHeaders().keySet()) {
|
for (String name : result.getResponseHeaders().headerNames()) {
|
||||||
response.addHeader(name, result.getResponseHeaders().getFirst(name));
|
response.addHeader(name, result.getResponseHeaders().getFirst(name));
|
||||||
}
|
}
|
||||||
String body = result.getResponseBody().blockFirst();
|
String body = result.getResponseBody().blockFirst();
|
||||||
|
|
|
@ -85,7 +85,7 @@ final class HtmlUnitWebTestClient {
|
||||||
}
|
}
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
return request.body(BodyInserters.fromObject(requestBody));
|
return request.body(BodyInserters.fromProducer(requestBody, String.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
private MultiValueMap<String, String> formData(List<NameValuePair> params) {
|
private MultiValueMap<String, String> formData(List<NameValuePair> params) {
|
||||||
|
@ -161,7 +161,7 @@ final class HtmlUnitWebTestClient {
|
||||||
redirectUrl = scheme + "://" + host + location.toASCIIString();
|
redirectUrl = scheme + "://" + host + location.toASCIIString();
|
||||||
}
|
}
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
ClientRequest redirect = ClientRequest.method(HttpMethod.GET, URI.create(redirectUrl))
|
ClientRequest redirect = ClientRequest.create(HttpMethod.GET, URI.create(redirectUrl))
|
||||||
.headers((headers) -> headers.addAll(request.headers()))
|
.headers((headers) -> headers.addAll(request.headers()))
|
||||||
.cookies((cookies) -> cookies.addAll(request.cookies()))
|
.cookies((cookies) -> cookies.addAll(request.cookies()))
|
||||||
.attributes((attributes) -> attributes.putAll(request.attributes()))
|
.attributes((attributes) -> attributes.putAll(request.attributes()))
|
||||||
|
|
|
@ -150,26 +150,6 @@ class AuthorizeHttpRequestsDslTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `request when allowed by mvc then responds with OK`() {
|
|
||||||
this.spring.register(AuthorizeHttpRequestsByMvcConfig::class.java, LegacyMvcMatchingConfig::class.java).autowire()
|
|
||||||
|
|
||||||
this.mockMvc.get("/path")
|
|
||||||
.andExpect {
|
|
||||||
status { isOk() }
|
|
||||||
}
|
|
||||||
|
|
||||||
this.mockMvc.get("/path.html")
|
|
||||||
.andExpect {
|
|
||||||
status { isOk() }
|
|
||||||
}
|
|
||||||
|
|
||||||
this.mockMvc.get("/path/")
|
|
||||||
.andExpect {
|
|
||||||
status { isOk() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
@EnableWebMvc
|
@EnableWebMvc
|
||||||
|
@ -193,14 +173,6 @@ class AuthorizeHttpRequestsDslTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
|
||||||
open class LegacyMvcMatchingConfig : WebMvcConfigurer {
|
|
||||||
override fun configurePathMatch(configurer: PathMatchConfigurer) {
|
|
||||||
configurer.setUseSuffixPatternMatch(true)
|
|
||||||
configurer.setUseTrailingSlashMatch(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `request when secured by mvc path variables then responds based on path variable value`() {
|
fun `request when secured by mvc path variables then responds based on path variable value`() {
|
||||||
this.spring.register(MvcMatcherPathVariablesConfig::class.java).autowire()
|
this.spring.register(MvcMatcherPathVariablesConfig::class.java).autowire()
|
||||||
|
|
|
@ -135,26 +135,6 @@ class AuthorizeRequestsDslTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `request when allowed by mvc then responds with OK`() {
|
|
||||||
this.spring.register(AuthorizeRequestsByMvcConfig::class.java, LegacyMvcMatchingConfig::class.java).autowire()
|
|
||||||
|
|
||||||
this.mockMvc.get("/path")
|
|
||||||
.andExpect {
|
|
||||||
status { isOk() }
|
|
||||||
}
|
|
||||||
|
|
||||||
this.mockMvc.get("/path.html")
|
|
||||||
.andExpect {
|
|
||||||
status { isOk() }
|
|
||||||
}
|
|
||||||
|
|
||||||
this.mockMvc.get("/path/")
|
|
||||||
.andExpect {
|
|
||||||
status { isOk() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
@EnableWebMvc
|
@EnableWebMvc
|
||||||
|
@ -179,14 +159,6 @@ class AuthorizeRequestsDslTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
|
||||||
open class LegacyMvcMatchingConfig : WebMvcConfigurer {
|
|
||||||
override fun configurePathMatch(configurer: PathMatchConfigurer) {
|
|
||||||
configurer.setUseSuffixPatternMatch(true)
|
|
||||||
configurer.setUseTrailingSlashMatch(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `request when secured by mvc path variables then responds based on path variable value`() {
|
fun `request when secured by mvc path variables then responds based on path variable value`() {
|
||||||
this.spring.register(MvcMatcherPathVariablesConfig::class.java).autowire()
|
this.spring.register(MvcMatcherPathVariablesConfig::class.java).autowire()
|
||||||
|
|
|
@ -127,7 +127,7 @@ class ServerHttpsRedirectDslTests {
|
||||||
return http {
|
return http {
|
||||||
redirectToHttps {
|
redirectToHttps {
|
||||||
httpsRedirectWhen {
|
httpsRedirectWhen {
|
||||||
it.request.headers.containsKey("X-Requires-Https")
|
it.request.headers.headerNames().contains("X-Requires-Https")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -165,7 +165,7 @@ class ServerHttpsRedirectDslTests {
|
||||||
redirectToHttps {
|
redirectToHttps {
|
||||||
httpsRedirectWhen(PathPatternParserServerWebExchangeMatcher("/secure"))
|
httpsRedirectWhen(PathPatternParserServerWebExchangeMatcher("/secure"))
|
||||||
httpsRedirectWhen {
|
httpsRedirectWhen {
|
||||||
it.request.headers.containsKey("X-Requires-Https")
|
it.request.headers.headerNames().contains("X-Requires-Https")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,6 @@
|
||||||
|
|
||||||
<logger name="org.springframework.security" level="${sec.log.level:-WARN}"/>
|
<logger name="org.springframework.security" level="${sec.log.level:-WARN}"/>
|
||||||
|
|
||||||
<logger name="org.apache.directory" level="ERROR"/>
|
|
||||||
|
|
||||||
<root level="${root.level:-WARN}">
|
<root level="${root.level:-WARN}">
|
||||||
<appender-ref ref="STDOUT" />
|
<appender-ref ref="STDOUT" />
|
||||||
</root>
|
</root>
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
</http>
|
</http>
|
||||||
|
|
||||||
<mvc:annotation-driven>
|
<mvc:annotation-driven>
|
||||||
<mvc:path-matching suffix-pattern="true" trailing-slash="true"/>
|
<mvc:path-matching />
|
||||||
</mvc:annotation-driven>
|
</mvc:annotation-driven>
|
||||||
|
|
||||||
<b:bean name="path" class="org.springframework.security.config.http.InterceptUrlConfigTests.PathController"/>
|
<b:bean name="path" class="org.springframework.security.config.http.InterceptUrlConfigTests.PathController"/>
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
</http>
|
</http>
|
||||||
|
|
||||||
<mvc:annotation-driven>
|
<mvc:annotation-driven>
|
||||||
<mvc:path-matching suffix-pattern="true"/>
|
<mvc:path-matching />
|
||||||
</mvc:annotation-driven>
|
</mvc:annotation-driven>
|
||||||
|
|
||||||
<b:bean name="path" class="org.springframework.security.config.http.InterceptUrlConfigTests.PathController"/>
|
<b:bean name="path" class="org.springframework.security.config.http.InterceptUrlConfigTests.PathController"/>
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
</http>
|
</http>
|
||||||
|
|
||||||
<mvc:annotation-driven>
|
<mvc:annotation-driven>
|
||||||
<mvc:path-matching suffix-pattern="true" trailing-slash="true"/>
|
<mvc:path-matching />
|
||||||
</mvc:annotation-driven>
|
</mvc:annotation-driven>
|
||||||
|
|
||||||
<b:bean name="path" class="org.springframework.security.config.http.InterceptUrlConfigTests.PathController"/>
|
<b:bean name="path" class="org.springframework.security.config.http.InterceptUrlConfigTests.PathController"/>
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
</http>
|
</http>
|
||||||
|
|
||||||
<mvc:annotation-driven>
|
<mvc:annotation-driven>
|
||||||
<mvc:path-matching suffix-pattern="true"/>
|
<mvc:path-matching />
|
||||||
</mvc:annotation-driven>
|
</mvc:annotation-driven>
|
||||||
|
|
||||||
<b:bean name="path" class="org.springframework.security.config.http.InterceptUrlConfigTests.PathController"/>
|
<b:bean name="path" class="org.springframework.security.config.http.InterceptUrlConfigTests.PathController"/>
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
~ 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
|
||||||
|
~
|
||||||
|
~ https://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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
|
||||||
|
xmlns:p="http://www.springframework.org/schema/p"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns="http://www.springframework.org/schema/security"
|
||||||
|
xsi:schemaLocation="
|
||||||
|
http://www.springframework.org/schema/security
|
||||||
|
https://www.springframework.org/schema/security/spring-security.xsd
|
||||||
|
http://www.springframework.org/schema/beans
|
||||||
|
https://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||||
|
|
||||||
|
<http>
|
||||||
|
<x509 principal-extractor-ref="principalExtractor"/>
|
||||||
|
<intercept-url pattern="/**" access="authenticated"/>
|
||||||
|
</http>
|
||||||
|
|
||||||
|
<user-service id="us">
|
||||||
|
<user name="luke@monkeymachine" password="{noop}password" authorities="ROLE_USER"/>
|
||||||
|
</user-service>
|
||||||
|
|
||||||
|
<b:bean name="principalExtractor" class="org.springframework.security.web.authentication.preauth.x509.SubjectX500PrincipalExtractor"
|
||||||
|
p:extractPrincipalNameFromEmail="true"/>
|
||||||
|
|
||||||
|
<b:import resource="MiscHttpConfigTests-controllers.xml"/>
|
||||||
|
</b:beans>
|
|
@ -0,0 +1,41 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
~ 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
|
||||||
|
~
|
||||||
|
~ https://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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
|
||||||
|
xmlns:p="http://www.springframework.org/schema/p"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns="http://www.springframework.org/schema/security"
|
||||||
|
xsi:schemaLocation="
|
||||||
|
http://www.springframework.org/schema/security
|
||||||
|
https://www.springframework.org/schema/security/spring-security.xsd
|
||||||
|
http://www.springframework.org/schema/beans
|
||||||
|
https://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||||
|
|
||||||
|
<http>
|
||||||
|
<x509 principal-extractor-ref="principalExtractor" subject-principal-regex="(.*)"/>
|
||||||
|
<intercept-url pattern="/**" access="authenticated"/>
|
||||||
|
</http>
|
||||||
|
|
||||||
|
<user-service id="us">
|
||||||
|
<user name="luke@monkeymachine" password="{noop}password" authorities="ROLE_USER"/>
|
||||||
|
</user-service>
|
||||||
|
|
||||||
|
<b:bean name="principalExtractor" class="org.springframework.security.web.authentication.preauth.x509.SubjectX500PrincipalExtractor"
|
||||||
|
p:extractPrincipalNameFromEmail="true"/>
|
||||||
|
|
||||||
|
<b:import resource="MiscHttpConfigTests-controllers.xml"/>
|
||||||
|
</b:beans>
|
|
@ -0,0 +1,32 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright 2002-2020 the original author or authors.
|
||||||
|
~
|
||||||
|
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
~ you may not use this file except in compliance with the License.
|
||||||
|
~ You may obtain a copy of the License at
|
||||||
|
~
|
||||||
|
~ https://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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns="http://www.springframework.org/schema/security"
|
||||||
|
xsi:schemaLocation="http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd
|
||||||
|
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||||
|
|
||||||
|
<http>
|
||||||
|
<intercept-url pattern="/**" access="authenticated"/>
|
||||||
|
<oauth2-resource-server authentication-converter-ref="authenticationConverter">
|
||||||
|
<jwt decoder-ref="decoder"/>
|
||||||
|
</oauth2-resource-server>
|
||||||
|
</http>
|
||||||
|
|
||||||
|
<b:import resource="userservice.xml"/>
|
||||||
|
</b:beans>
|
|
@ -0,0 +1,32 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright 2002-2020 the original author or authors.
|
||||||
|
~
|
||||||
|
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
~ you may not use this file except in compliance with the License.
|
||||||
|
~ You may obtain a copy of the License at
|
||||||
|
~
|
||||||
|
~ https://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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns="http://www.springframework.org/schema/security"
|
||||||
|
xsi:schemaLocation="http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd
|
||||||
|
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||||
|
|
||||||
|
<http>
|
||||||
|
<intercept-url pattern="/**" access="authenticated"/>
|
||||||
|
<oauth2-resource-server authentication-converter-ref="authenticationConverter" bearer-token-resolver-ref="bearerTokenResolver">
|
||||||
|
<jwt decoder-ref="decoder"/>
|
||||||
|
</oauth2-resource-server>
|
||||||
|
</http>
|
||||||
|
|
||||||
|
<b:import resource="userservice.xml"/>
|
||||||
|
</b:beans>
|
|
@ -0,0 +1,27 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright 2002-2020 the original author or authors.
|
||||||
|
~
|
||||||
|
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
~ you may not use this file except in compliance with the License.
|
||||||
|
~ You may obtain a copy of the License at
|
||||||
|
~
|
||||||
|
~ https://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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns="http://www.springframework.org/schema/security"
|
||||||
|
xsi:schemaLocation="http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd
|
||||||
|
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||||
|
|
||||||
|
<b:bean name="authenticationConverter" class="org.mockito.Mockito" factory-method="mock">
|
||||||
|
<b:constructor-arg value="org.springframework.security.web.authentication.AuthenticationConverter" type="java.lang.Class"/>
|
||||||
|
</b:bean>
|
||||||
|
</b:beans>
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue