Merge branch 'spring-projects:main' into fix-ror-link
This commit is contained in:
commit
18d8a3a56e
|
@ -4,6 +4,32 @@ registries:
|
|||
type: maven-repository
|
||||
url: https://repo.spring.io/milestone
|
||||
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
|
||||
target-branch: 6.4.x
|
||||
directory: /
|
||||
|
|
|
@ -39,48 +39,25 @@ jobs:
|
|||
toolchain: 17
|
||||
with:
|
||||
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
|
||||
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:
|
||||
name: Deploy Artifacts
|
||||
needs: [ build, test, check-samples ]
|
||||
needs: [ build, test]
|
||||
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-artifacts.yml@v1
|
||||
with:
|
||||
should-deploy-artifacts: ${{ needs.build.outputs.should-deploy-artifacts }}
|
||||
secrets: inherit
|
||||
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
|
||||
with:
|
||||
should-deploy-docs: ${{ needs.build.outputs.should-deploy-artifacts }}
|
||||
secrets: inherit
|
||||
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
|
||||
with:
|
||||
should-deploy-schema: ${{ needs.build.outputs.should-deploy-artifacts }}
|
||||
|
|
|
@ -11,7 +11,7 @@ jobs:
|
|||
strategy:
|
||||
matrix:
|
||||
# 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
|
||||
steps:
|
||||
- name: Checkout
|
||||
|
|
|
@ -80,6 +80,11 @@ class RepositoryConventionPlugin implements Plugin<Project> {
|
|||
}
|
||||
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()) {
|
||||
def shortName = key.replaceAll(/http.*schema.(.*).spring-.*/, '$1')
|
||||
assert shortName != key
|
||||
def schemaResourceName = schemas.get(key)
|
||||
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) {
|
||||
duplicatesStrategy 'exclude'
|
||||
from xsdFile.path
|
||||
|
|
|
@ -81,9 +81,6 @@ public class CheckClasspathForProhibitedDependencies extends DefaultTask {
|
|||
if (group.startsWith("javax")) {
|
||||
return true;
|
||||
}
|
||||
if (group.equals("commons-logging")) {
|
||||
return true;
|
||||
}
|
||||
if (group.equals("org.slf4j") && id.getName().equals("jcl-over-slf4j")) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -78,12 +78,6 @@ dependencies {
|
|||
exclude group: 'commons-logging', module: 'commons-logging'
|
||||
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 'jakarta.persistence:jakarta.persistence-api'
|
||||
testImplementation "org.hibernate.orm:hibernate-core"
|
||||
|
@ -127,6 +121,7 @@ dependencies {
|
|||
|
||||
testRuntimeOnly 'org.hsqldb:hsqldb'
|
||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
||||
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
|
||||
}
|
||||
|
||||
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.ldap.DefaultSpringSecurityContextSource;
|
||||
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.test.util.ReflectionTestUtils;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
@ -326,11 +326,11 @@ public class LdapAuthenticationProviderBuilderSecurityBuilderTests {
|
|||
abstract static class BaseLdapServerConfig extends BaseLdapProviderConfig {
|
||||
|
||||
@Bean
|
||||
ApacheDSContainer ldapServer() throws Exception {
|
||||
ApacheDSContainer apacheDSContainer = new ApacheDSContainer("dc=springframework,dc=org",
|
||||
UnboundIdContainer ldapServer() throws Exception {
|
||||
UnboundIdContainer unboundIdContainer = new UnboundIdContainer("dc=springframework,dc=org",
|
||||
"classpath:/test-server.ldif");
|
||||
apacheDSContainer.setPort(getPort());
|
||||
return apacheDSContainer;
|
||||
unboundIdContainer.setPort(getPort());
|
||||
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.UserDetails;
|
||||
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.LdapAuthoritiesPopulator;
|
||||
import org.springframework.security.ldap.userdetails.UserDetailsContextMapper;
|
||||
|
@ -226,18 +226,18 @@ public class LdapBindAuthenticationManagerFactoryITests {
|
|||
@EnableWebSecurity
|
||||
abstract static class BaseLdapServerConfig implements DisposableBean {
|
||||
|
||||
private ApacheDSContainer container;
|
||||
private UnboundIdContainer container;
|
||||
|
||||
@Bean
|
||||
ApacheDSContainer ldapServer() throws Exception {
|
||||
this.container = new ApacheDSContainer("dc=springframework,dc=org", "classpath:/test-server.ldif");
|
||||
UnboundIdContainer ldapServer() {
|
||||
this.container = new UnboundIdContainer("dc=springframework,dc=org", "classpath:/test-server.ldif");
|
||||
this.container.setPort(0);
|
||||
return this.container;
|
||||
}
|
||||
|
||||
@Bean
|
||||
BaseLdapPathContextSource contextSource(ApacheDSContainer container) {
|
||||
int port = container.getLocalPort();
|
||||
BaseLdapPathContextSource contextSource(UnboundIdContainer container) {
|
||||
int port = container.getPort();
|
||||
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.password.NoOpPasswordEncoder;
|
||||
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 static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;
|
||||
|
@ -93,18 +93,18 @@ public class LdapPasswordComparisonAuthenticationManagerFactoryITests {
|
|||
@EnableWebSecurity
|
||||
abstract static class BaseLdapServerConfig implements DisposableBean {
|
||||
|
||||
private ApacheDSContainer container;
|
||||
private UnboundIdContainer container;
|
||||
|
||||
@Bean
|
||||
ApacheDSContainer ldapServer() throws Exception {
|
||||
this.container = new ApacheDSContainer("dc=springframework,dc=org", "classpath:/test-server.ldif");
|
||||
UnboundIdContainer ldapServer() {
|
||||
this.container = new UnboundIdContainer("dc=springframework,dc=org", "classpath:/test-server.ldif");
|
||||
this.container.setPort(0);
|
||||
return this.container;
|
||||
}
|
||||
|
||||
@Bean
|
||||
BaseLdapPathContextSource contextSource(ApacheDSContainer container) {
|
||||
int port = container.getLocalPort();
|
||||
BaseLdapPathContextSource contextSource(UnboundIdContainer container) {
|
||||
int port = container.getPort();
|
||||
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.class);
|
||||
Authentication auth = authenticationManager
|
||||
.authenticate(UsernamePasswordAuthenticationToken.unauthenticated("ben", "benspassword"));
|
||||
.authenticate(UsernamePasswordAuthenticationToken.unauthenticated("otherben", "otherbenspassword"));
|
||||
UserDetails ben = (UserDetails) auth.getPrincipal();
|
||||
assertThat(ben.getAuthorities()).hasSize(3);
|
||||
}
|
||||
|
@ -127,6 +127,27 @@ public class LdapProviderBeanDefinitionParserTests {
|
|||
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
|
||||
public void inetOrgContextMapperIsSupported() {
|
||||
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.util.InMemoryXmlApplicationContext;
|
||||
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 static org.assertj.core.api.Assertions.assertThat;
|
||||
|
@ -92,9 +92,9 @@ public class LdapServerBeanDefinitionParserTests {
|
|||
@Test
|
||||
public void defaultLdifFileIsSuccessful() {
|
||||
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 {
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
<logger name="org.springframework.security" level="${sec.log.level:-WARN}"/>
|
||||
|
||||
<logger name="org.apache.directory" level="ERROR"/>
|
||||
<logger name="JdbmTable" level="INFO"/>
|
||||
<logger name="JdbmIndex" level="INFO"/>
|
||||
<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 EMBEDDED_APACHE_DS = PREFIX + "apacheDirectoryServerContainer";
|
||||
|
||||
public static final String EMBEDDED_UNBOUNDID = PREFIX + "unboundidServerContainer";
|
||||
|
||||
public static final String CONTEXT_SOURCE = PREFIX + "securityContextSource";
|
||||
|
|
|
@ -96,7 +96,7 @@ public final class SecurityNamespaceHandler implements NamespaceHandler {
|
|||
pc.getReaderContext()
|
||||
.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 "
|
||||
+ "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);
|
||||
}
|
||||
String name = pc.getDelegate().getLocalName(element);
|
||||
|
@ -221,7 +221,7 @@ public final class SecurityNamespaceHandler implements NamespaceHandler {
|
|||
|
||||
private boolean matchesVersionInternal(Element element) {
|
||||
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.*");
|
||||
}
|
||||
|
|
|
@ -37,7 +37,6 @@ import org.springframework.security.ldap.authentication.LdapAuthenticator;
|
|||
import org.springframework.security.ldap.authentication.PasswordComparisonAuthenticator;
|
||||
import org.springframework.security.ldap.search.FilterBasedLdapUserSearch;
|
||||
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.userdetails.DefaultLdapAuthoritiesPopulator;
|
||||
import org.springframework.security.ldap.userdetails.InetOrgPersonContextMapper;
|
||||
|
@ -60,12 +59,8 @@ import org.springframework.util.ClassUtils;
|
|||
public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuilder<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 boolean apacheDsPresent;
|
||||
|
||||
private static final boolean unboundIdPresent;
|
||||
|
||||
private String groupRoleAttribute = "cn";
|
||||
|
@ -100,7 +95,6 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
|
|||
|
||||
static {
|
||||
ClassLoader classLoader = LdapAuthenticationProviderConfigurer.class.getClassLoader();
|
||||
apacheDsPresent = ClassUtils.isPresent(APACHEDS_CLASSNAME, classLoader);
|
||||
unboundIdPresent = ClassUtils.isPresent(UNBOUNDID_CLASSNAME, classLoader);
|
||||
}
|
||||
|
||||
|
@ -467,8 +461,6 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
|
|||
*/
|
||||
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 int DEFAULT_PORT = 33389;
|
||||
|
@ -584,14 +576,8 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
|
|||
return contextSource;
|
||||
}
|
||||
|
||||
private void startEmbeddedLdapServer() throws Exception {
|
||||
if (apacheDsPresent) {
|
||||
ApacheDSContainer apacheDsContainer = new ApacheDSContainer(this.root, this.ldif);
|
||||
apacheDsContainer.setPort(getPort());
|
||||
postProcess(apacheDsContainer);
|
||||
this.port = apacheDsContainer.getLocalPort();
|
||||
}
|
||||
else if (unboundIdPresent) {
|
||||
private void startEmbeddedLdapServer() {
|
||||
if (unboundIdPresent) {
|
||||
UnboundIdContainer unboundIdContainer = new UnboundIdContainer(this.root, this.ldif);
|
||||
unboundIdContainer.setPort(getPort());
|
||||
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");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -16,13 +16,22 @@
|
|||
|
||||
package org.springframework.security.config.annotation.method.configuration;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.aop.framework.AopInfrastructureBean;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
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.authorization.AuthorizationProxyFactory;
|
||||
import org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory;
|
||||
import org.springframework.security.data.aot.hint.AuthorizeReturnObjectDataHintsRegistrar;
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
|
@ -34,4 +43,45 @@ final class AuthorizationProxyDataConfiguration implements AopInfrastructureBean
|
|||
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;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
@ -25,12 +29,17 @@ import org.springframework.context.annotation.Role;
|
|||
import org.springframework.core.Ordered;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
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.View;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;
|
||||
|
||||
@Configuration
|
||||
class AuthorizationProxyWebConfiguration {
|
||||
class AuthorizationProxyWebConfiguration implements WebMvcConfigurer {
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
|
@ -38,6 +47,18 @@ class AuthorizationProxyWebConfiguration {
|
|||
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 {
|
||||
|
||||
private static final int DEFAULT_ORDER = 100;
|
||||
|
@ -54,7 +75,7 @@ class AuthorizationProxyWebConfiguration {
|
|||
if (target instanceof ModelAndView mav) {
|
||||
View view = mav.getView();
|
||||
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)
|
||||
: new ModelAndView(viewName, model);
|
||||
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
|
||||
* 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
|
||||
* {@link #requestMatcher(RequestMatcher)} or other similar methods.
|
||||
* {@link #authorizeHttpRequests(Customizer)} or other similar methods.
|
||||
*
|
||||
* <h2>Example Usage</h2>
|
||||
*
|
||||
|
@ -124,7 +124,12 @@ import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
|
|||
*
|
||||
* @Bean
|
||||
* 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();
|
||||
* }
|
||||
*
|
||||
|
|
|
@ -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");
|
||||
* 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.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
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.ObservationMarkingAccessDeniedHandler;
|
||||
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.CsrfFilter;
|
||||
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.CsrfTokenRequestAttributeHandler;
|
||||
import org.springframework.security.web.csrf.CsrfTokenRequestHandler;
|
||||
import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;
|
||||
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.InvalidSessionStrategy;
|
||||
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.RequestMatcher;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Adds
|
||||
|
@ -214,6 +221,21 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
|
|||
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")
|
||||
@Override
|
||||
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");
|
||||
* 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
|
||||
* certificate (i.e. "CN=(.*?)(?:,|$)").
|
||||
* @return the {@link X509Configurer} for further customizations
|
||||
* @deprecated Please use {{@link #x509PrincipalExtractor(X509PrincipalExtractor)}
|
||||
* instead
|
||||
*/
|
||||
@Deprecated
|
||||
public X509Configurer<H> subjectPrincipalRegex(String subjectPrincipalRegex) {
|
||||
SubjectDnX509PrincipalExtractor principalExtractor = new SubjectDnX509PrincipalExtractor();
|
||||
principalExtractor.setSubjectDnRegex(subjectPrincipalRegex);
|
||||
|
|
|
@ -18,11 +18,6 @@ package org.springframework.security.config.annotation.web.configurers.oauth2.cl
|
|||
|
||||
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.AuthenticationServiceException;
|
||||
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.JwtDecoder;
|
||||
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.NimbusJwtDecoder;
|
||||
import org.springframework.util.Assert;
|
||||
|
@ -67,8 +63,10 @@ final class OidcBackChannelLogoutAuthenticationProvider implements Authenticatio
|
|||
* Construct an {@link OidcBackChannelLogoutAuthenticationProvider}
|
||||
*/
|
||||
OidcBackChannelLogoutAuthenticationProvider() {
|
||||
JwtTypeValidator type = new JwtTypeValidator("JWT", "logout+jwt");
|
||||
type.setAllowEmpty(true);
|
||||
Function<ClientRegistration, OAuth2TokenValidator<Jwt>> jwtValidator = (clientRegistration) -> JwtValidators
|
||||
.createDefaultWithValidators(new OidcBackChannelLogoutTokenValidator(clientRegistration));
|
||||
.createDefaultWithValidators(type, new OidcBackChannelLogoutTokenValidator(clientRegistration));
|
||||
this.logoutTokenDecoderFactory = (clientRegistration) -> {
|
||||
String jwkSetUri = clientRegistration.getProviderDetails().getJwkSetUri();
|
||||
if (!StringUtils.hasText(jwkSetUri)) {
|
||||
|
@ -79,11 +77,7 @@ final class OidcBackChannelLogoutAuthenticationProvider implements Authenticatio
|
|||
null);
|
||||
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
|
||||
}
|
||||
JOSEObjectTypeVerifier<SecurityContext> typeVerifier = new DefaultJOSEObjectTypeVerifier<>(null,
|
||||
JOSEObjectType.JWT, new JOSEObjectType("logout+jwt"));
|
||||
NimbusJwtDecoder decoder = NimbusJwtDecoder.withJwkSetUri(jwkSetUri)
|
||||
.jwtProcessorCustomizer((processor) -> processor.setJWSTypeVerifier(typeVerifier))
|
||||
.build();
|
||||
NimbusJwtDecoder decoder = NimbusJwtDecoder.withJwkSetUri(jwkSetUri).build();
|
||||
decoder.setJwtValidator(jwtValidator.apply(clientRegistration));
|
||||
decoder.setClaimSetConverter(OidcIdTokenDecoderFactory.createDefaultClaimTypeConverter());
|
||||
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.ExceptionHandlingConfigurer;
|
||||
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.jwt.Jwt;
|
||||
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.web.BearerTokenAuthenticationEntryPoint;
|
||||
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.authentication.BearerTokenAuthenticationConverter;
|
||||
import org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.access.AccessDeniedHandler;
|
||||
import org.springframework.security.web.access.AccessDeniedHandlerImpl;
|
||||
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.util.matcher.AndRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;
|
||||
|
@ -156,7 +158,7 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
|
|||
|
||||
private AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver;
|
||||
|
||||
private BearerTokenResolver bearerTokenResolver;
|
||||
private AuthenticationConverter authenticationConverter;
|
||||
|
||||
private JwtConfigurer jwtConfigurer;
|
||||
|
||||
|
@ -196,7 +198,19 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
|
|||
|
||||
public OAuth2ResourceServerConfigurer<H> bearerTokenResolver(BearerTokenResolver bearerTokenResolver) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -271,16 +285,15 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
|
|||
|
||||
@Override
|
||||
public void configure(H http) {
|
||||
BearerTokenResolver bearerTokenResolver = getBearerTokenResolver();
|
||||
this.requestMatcher.setBearerTokenResolver(bearerTokenResolver);
|
||||
AuthenticationManagerResolver resolver = this.authenticationManagerResolver;
|
||||
if (resolver == null) {
|
||||
AuthenticationManager authenticationManager = getAuthenticationManager(http);
|
||||
resolver = (request) -> authenticationManager;
|
||||
}
|
||||
|
||||
BearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(resolver);
|
||||
filter.setBearerTokenResolver(bearerTokenResolver);
|
||||
AuthenticationConverter converter = getAuthenticationConverter();
|
||||
this.requestMatcher.setAuthenticationConverter(converter);
|
||||
BearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(resolver, converter);
|
||||
filter.setAuthenticationEntryPoint(this.authenticationEntryPoint);
|
||||
filter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
|
||||
filter = postProcess(filter);
|
||||
|
@ -367,16 +380,29 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
|
|||
return this.authenticationManagerResolver;
|
||||
}
|
||||
|
||||
BearerTokenResolver getBearerTokenResolver() {
|
||||
if (this.bearerTokenResolver == null) {
|
||||
if (this.context.getBeanNamesForType(BearerTokenResolver.class).length > 0) {
|
||||
this.bearerTokenResolver = this.context.getBean(BearerTokenResolver.class);
|
||||
AuthenticationConverter getAuthenticationConverter() {
|
||||
if (this.authenticationConverter != null) {
|
||||
return this.authenticationConverter;
|
||||
}
|
||||
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 {
|
||||
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 {
|
||||
|
@ -564,21 +590,41 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
|
|||
|
||||
private static final class BearerTokenRequestMatcher implements RequestMatcher {
|
||||
|
||||
private BearerTokenResolver bearerTokenResolver;
|
||||
private AuthenticationConverter authenticationConverter;
|
||||
|
||||
@Override
|
||||
public boolean matches(HttpServletRequest request) {
|
||||
try {
|
||||
return this.bearerTokenResolver.resolve(request) != null;
|
||||
return this.authenticationConverter.convert(request) != null;
|
||||
}
|
||||
catch (OAuth2AuthenticationException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void setBearerTokenResolver(BearerTokenResolver tokenResolver) {
|
||||
Assert.notNull(tokenResolver, "resolver cannot be null");
|
||||
this.bearerTokenResolver = tokenResolver;
|
||||
void setAuthenticationConverter(AuthenticationConverter authenticationConverter) {
|
||||
Assert.notNull(authenticationConverter, "authenticationConverter cannot be null");
|
||||
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.context.SecurityContextHolderStrategy;
|
||||
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.OpenSaml4LogoutResponseValidator;
|
||||
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) {
|
||||
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("securityContextHolderStrategy",
|
||||
authenticationFilterSecurityContextHolderStrategyRef);
|
||||
String regex = x509Elt.getAttribute("subject-principal-regex");
|
||||
if (StringUtils.hasText(regex)) {
|
||||
String principalExtractorRef = x509Elt.getAttribute("principal-extractor-ref");
|
||||
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
|
||||
.rootBeanDefinition(SubjectDnX509PrincipalExtractor.class);
|
||||
extractor.addPropertyValue("subjectDnRegex", regex);
|
||||
extractor.addPropertyValue("subjectDnRegex", subjectPrincipalRegex);
|
||||
filterBuilder.addPropertyValue("principalExtractor", extractor.getBeanDefinition());
|
||||
}
|
||||
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");
|
||||
* 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.springframework.beans.BeanMetadataElement;
|
||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
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.web.BearerTokenAuthenticationEntryPoint;
|
||||
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.authentication.BearerTokenAuthenticationConverter;
|
||||
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.util.Assert;
|
||||
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 AUTHENTICATION_CONVERTER_REF = "authentication-converter-ref";
|
||||
|
||||
static final String ENTRY_POINT_REF = "entry-point-ref";
|
||||
|
||||
static final String BEARER_TOKEN_RESOLVER = "bearerTokenResolver";
|
||||
|
@ -124,11 +128,16 @@ final class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionPa
|
|||
pc.getReaderContext().registerWithGeneratedName(opaqueTokenAuthenticationProvider)));
|
||||
}
|
||||
BeanMetadataElement bearerTokenResolver = getBearerTokenResolver(oauth2ResourceServer);
|
||||
BeanDefinitionBuilder requestMatcherBuilder = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(BearerTokenRequestMatcher.class);
|
||||
requestMatcherBuilder.addConstructorArgValue(bearerTokenResolver);
|
||||
BeanDefinition requestMatcher = requestMatcherBuilder.getBeanDefinition();
|
||||
BeanMetadataElement authenticationConverter = getAuthenticationConverter(oauth2ResourceServer);
|
||||
if (bearerTokenResolver != null && authenticationConverter != null) {
|
||||
throw new BeanDefinitionStoreException(
|
||||
"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);
|
||||
BeanDefinition requestMatcher = buildRequestMatcher(bearerTokenResolver, authenticationConverter);
|
||||
this.entryPoints.put(requestMatcher, authenticationEntryPoint);
|
||||
this.deniedHandlers.put(requestMatcher, this.accessDeniedHandler);
|
||||
this.ignoreCsrfRequestMatchers.add(requestMatcher);
|
||||
|
@ -136,13 +145,35 @@ final class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionPa
|
|||
.rootBeanDefinition(BearerTokenAuthenticationFilter.class);
|
||||
BeanMetadataElement authenticationManagerResolver = getAuthenticationManagerResolver(oauth2ResourceServer);
|
||||
filterBuilder.addConstructorArgValue(authenticationManagerResolver);
|
||||
filterBuilder.addPropertyValue(BEARER_TOKEN_RESOLVER, bearerTokenResolver);
|
||||
filterBuilder.addPropertyValue(AUTHENTICATION_ENTRY_POINT, authenticationEntryPoint);
|
||||
filterBuilder.addPropertyValue("securityContextHolderStrategy",
|
||||
this.authenticationFilterSecurityContextHolderStrategy);
|
||||
|
||||
if (authenticationConverter != null) {
|
||||
filterBuilder.addConstructorArgValue(authenticationConverter);
|
||||
}
|
||||
if (bearerTokenResolver != null) {
|
||||
filterBuilder.addPropertyValue(BEARER_TOKEN_RESOLVER, bearerTokenResolver);
|
||||
}
|
||||
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) {
|
||||
if (!oauth2ResourceServer.hasAttribute(AUTHENTICATION_MANAGER_RESOLVER_REF)) {
|
||||
if (jwt == null && opaqueToken == null) {
|
||||
|
@ -178,11 +209,19 @@ final class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionPa
|
|||
BeanMetadataElement getBearerTokenResolver(Element element) {
|
||||
String bearerTokenResolverRef = element.getAttribute(BEARER_TOKEN_RESOLVER_REF);
|
||||
if (!StringUtils.hasLength(bearerTokenResolverRef)) {
|
||||
return new RootBeanDefinition(DefaultBearerTokenResolver.class);
|
||||
return null;
|
||||
}
|
||||
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) {
|
||||
String entryPointRef = element.getAttribute(ENTRY_POINT_REF);
|
||||
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.SecurityContextHolderStrategy;
|
||||
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.authentication.logout.Saml2LogoutRequestFilter;
|
||||
import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutResponseFilter;
|
||||
|
@ -239,7 +241,13 @@ final class Saml2LogoutBeanDefinitionParser implements BeanDefinitionParser {
|
|||
if (authentication == null) {
|
||||
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) {
|
||||
|
|
|
@ -32,7 +32,6 @@ import org.springframework.context.ApplicationContext;
|
|||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.security.config.BeanIds;
|
||||
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
|
||||
import org.springframework.security.ldap.server.ApacheDSContainer;
|
||||
import org.springframework.security.ldap.server.UnboundIdContainer;
|
||||
import org.springframework.util.ClassUtils;
|
||||
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";
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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 String APACHEDS_CLASSNAME = "org.apache.directory.server.core.DefaultDirectoryService";
|
||||
|
||||
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 boolean unboundIdPresent;
|
||||
|
||||
private static final boolean apacheDsPresent;
|
||||
|
||||
static {
|
||||
ClassLoader classLoader = LdapServerBeanDefinitionParser.class.getClassLoader();
|
||||
unboundIdPresent = ClassUtils.isPresent(UNBOUNID_CLASSNAME, classLoader);
|
||||
apacheDsPresent = ClassUtils.isPresent(APACHEDS_CLASSNAME, classLoader);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -128,10 +120,9 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
|
|||
/**
|
||||
* 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.
|
||||
*
|
||||
* @see ApacheDSContainer
|
||||
* @see UnboundIdContainer
|
||||
*/
|
||||
private RootBeanDefinition createEmbeddedServer(Element element, ParserContext parserContext) {
|
||||
|
@ -162,8 +153,7 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
|
|||
}
|
||||
ldapContainer.getConstructorArgumentValues().addGenericArgumentValue(ldifs);
|
||||
ldapContainer.getPropertyValues().addPropertyValue("port", getPort(element));
|
||||
if (parserContext.getRegistry().containsBeanDefinition(BeanIds.EMBEDDED_APACHE_DS)
|
||||
|| parserContext.getRegistry().containsBeanDefinition(BeanIds.EMBEDDED_UNBOUNDID)) {
|
||||
if (parserContext.getRegistry().containsBeanDefinition(BeanIds.EMBEDDED_UNBOUNDID)) {
|
||||
parserContext.getReaderContext()
|
||||
.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) {
|
||||
if (isApacheDsEnabled(mode)) {
|
||||
return new RootBeanDefinition(APACHEDS_CONTAINER_CLASSNAME, null, null);
|
||||
}
|
||||
if (isUnboundidEnabled(mode)) {
|
||||
return new RootBeanDefinition(UNBOUNDID_CONTAINER_CLASSNAME, null, null);
|
||||
}
|
||||
|
@ -185,19 +172,12 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
|
|||
}
|
||||
|
||||
private String resolveBeanId(String mode) {
|
||||
if (isApacheDsEnabled(mode)) {
|
||||
return BeanIds.EMBEDDED_APACHE_DS;
|
||||
}
|
||||
if (isUnboundidEnabled(mode)) {
|
||||
return BeanIds.EMBEDDED_UNBOUNDID;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isApacheDsEnabled(String mode) {
|
||||
return "apacheds".equals(mode) || apacheDsPresent;
|
||||
}
|
||||
|
||||
private boolean isUnboundidEnabled(String mode) {
|
||||
return "unboundid".equals(mode) || unboundIdPresent;
|
||||
}
|
||||
|
@ -233,10 +213,6 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
|
|||
}
|
||||
|
||||
private int getPort() {
|
||||
if (apacheDsPresent) {
|
||||
ApacheDSContainer apacheDSContainer = this.applicationContext.getBean(ApacheDSContainer.class);
|
||||
return apacheDSContainer.getLocalPort();
|
||||
}
|
||||
if (unboundIdPresent) {
|
||||
UnboundIdContainer unboundIdContainer = this.applicationContext.getBean(UnboundIdContainer.class);
|
||||
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");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -16,6 +16,7 @@
|
|||
|
||||
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.oauth2.client.registration.ClientRegistration;
|
||||
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
|
||||
* {@link org.springframework.security.oauth2.client.registration.ClientRegistration.Builder
|
||||
* builders} pre-configured with sensible defaults for the
|
||||
* {@link HttpSecurity#oauth2Login()} flow.
|
||||
* {@link HttpSecurity#oauth2Login(Customizer)} flow.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @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 {
|
||||
|
||||
@Override
|
||||
|
|
|
@ -18,10 +18,6 @@ package org.springframework.security.config.web.server;
|
|||
|
||||
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 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.JwtDecoder;
|
||||
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.NimbusReactiveJwtDecoder;
|
||||
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
|
||||
|
@ -72,8 +69,10 @@ final class OidcBackChannelLogoutReactiveAuthenticationManager implements Reacti
|
|||
* Construct an {@link OidcBackChannelLogoutReactiveAuthenticationManager}
|
||||
*/
|
||||
OidcBackChannelLogoutReactiveAuthenticationManager() {
|
||||
JwtTypeValidator type = new JwtTypeValidator("JWT", "logout+jwt");
|
||||
type.setAllowEmpty(true);
|
||||
Function<ClientRegistration, OAuth2TokenValidator<Jwt>> jwtValidator = (clientRegistration) -> JwtValidators
|
||||
.createDefaultWithValidators(new OidcBackChannelLogoutTokenValidator(clientRegistration));
|
||||
.createDefaultWithValidators(type, new OidcBackChannelLogoutTokenValidator(clientRegistration));
|
||||
this.logoutTokenDecoderFactory = (clientRegistration) -> {
|
||||
String jwkSetUri = clientRegistration.getProviderDetails().getJwkSetUri();
|
||||
if (!StringUtils.hasText(jwkSetUri)) {
|
||||
|
@ -84,11 +83,7 @@ final class OidcBackChannelLogoutReactiveAuthenticationManager implements Reacti
|
|||
null);
|
||||
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
|
||||
}
|
||||
JOSEObjectTypeVerifier<JWKSecurityContext> typeVerifier = new DefaultJOSEObjectTypeVerifier<>(null,
|
||||
JOSEObjectType.JWT, new JOSEObjectType("logout+jwt"));
|
||||
NimbusReactiveJwtDecoder decoder = NimbusReactiveJwtDecoder.withJwkSetUri(jwkSetUri)
|
||||
.jwtProcessorCustomizer((processor) -> processor.setJWSTypeVerifier(typeVerifier))
|
||||
.build();
|
||||
NimbusReactiveJwtDecoder decoder = NimbusReactiveJwtDecoder.withJwkSetUri(jwkSetUri).build();
|
||||
decoder.setJwtValidator(jwtValidator.apply(clientRegistration));
|
||||
decoder.setClaimSetConverter(
|
||||
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.web.PortMapper;
|
||||
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.server.DefaultServerRedirectStrategy;
|
||||
import org.springframework.security.web.server.DelegatingServerAuthenticationEntryPoint;
|
||||
|
@ -298,6 +298,7 @@ import org.springframework.web.util.pattern.PathPatternParser;
|
|||
* @author Parikshit Dutta
|
||||
* @author Ankur Pathak
|
||||
* @author Alexey Nesterov
|
||||
* @author Yanming Zhou
|
||||
* @since 5.0
|
||||
*/
|
||||
public class ServerHttpSecurity {
|
||||
|
@ -943,8 +944,8 @@ public class ServerHttpSecurity {
|
|||
* }
|
||||
* </pre>
|
||||
*
|
||||
* Note that if extractor is not specified, {@link SubjectDnX509PrincipalExtractor}
|
||||
* will be used. If authenticationManager is not specified,
|
||||
* Note that if extractor is not specified, {@link SubjectX500PrincipalExtractor} will
|
||||
* be used. If authenticationManager is not specified,
|
||||
* {@link ReactivePreAuthenticatedAuthenticationManager} will be used.
|
||||
* @return the {@link X509Spec} to customize
|
||||
* @since 5.2
|
||||
|
@ -978,8 +979,8 @@ public class ServerHttpSecurity {
|
|||
* }
|
||||
* </pre>
|
||||
*
|
||||
* Note that if extractor is not specified, {@link SubjectDnX509PrincipalExtractor}
|
||||
* will be used. If authenticationManager is not specified,
|
||||
* Note that if extractor is not specified, {@link SubjectX500PrincipalExtractor} will
|
||||
* be used. If authenticationManager is not specified,
|
||||
* {@link ReactivePreAuthenticatedAuthenticationManager} will be used.
|
||||
* @param x509Customizer the {@link Customizer} to provide more options for the
|
||||
* {@link X509Spec}
|
||||
|
@ -4180,7 +4181,7 @@ public class ServerHttpSecurity {
|
|||
if (this.principalExtractor != null) {
|
||||
return this.principalExtractor;
|
||||
}
|
||||
return new SubjectDnX509PrincipalExtractor();
|
||||
return new SubjectX500PrincipalExtractor();
|
||||
}
|
||||
|
||||
private ReactiveAuthenticationManager getAuthenticationManager() {
|
||||
|
@ -5443,8 +5444,11 @@ public class ServerHttpSecurity {
|
|||
public OpaqueTokenSpec introspectionUri(String introspectionUri) {
|
||||
Assert.hasText(introspectionUri, "introspectionUri cannot be empty");
|
||||
this.introspectionUri = introspectionUri;
|
||||
this.introspector = () -> new SpringReactiveOpaqueTokenIntrospector(this.introspectionUri,
|
||||
this.clientId, this.clientSecret);
|
||||
this.introspector = () -> SpringReactiveOpaqueTokenIntrospector
|
||||
.withIntrospectionUri(this.introspectionUri)
|
||||
.clientId(this.clientId)
|
||||
.clientSecret(this.clientSecret)
|
||||
.build();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -5459,8 +5463,11 @@ public class ServerHttpSecurity {
|
|||
Assert.notNull(clientSecret, "clientSecret cannot be null");
|
||||
this.clientId = clientId;
|
||||
this.clientSecret = clientSecret;
|
||||
this.introspector = () -> new SpringReactiveOpaqueTokenIntrospector(this.introspectionUri,
|
||||
this.clientId, this.clientSecret);
|
||||
this.introspector = () -> SpringReactiveOpaqueTokenIntrospector
|
||||
.withIntrospectionUri(this.introspectionUri)
|
||||
.clientId(this.clientId)
|
||||
.clientSecret(this.clientSecret)
|
||||
.build();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,9 +16,13 @@
|
|||
|
||||
package org.springframework.security.config.websocket;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
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 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 boolean sameOriginDisabled;
|
||||
|
@ -345,16 +354,7 @@ public final class WebSocketMessageBrokerSecurityBeanDefinitionParser implements
|
|||
}
|
||||
}
|
||||
}
|
||||
else if ("org.springframework.web.socket.server.support.WebSocketHttpRequestHandler"
|
||||
.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)) {
|
||||
else if (CSRF_HANDSHAKE_HANDLER_CLASSES.contains(beanClassName)) {
|
||||
addCsrfTokenHandshakeInterceptor(bd);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,6 +51,7 @@ class X509Dsl {
|
|||
var authenticationDetailsSource: AuthenticationDetailsSource<HttpServletRequest, PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails>? = null
|
||||
var userDetailsService: UserDetailsService? = null
|
||||
var authenticationUserDetailsService: AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken>? = null
|
||||
@Deprecated("Use x509PrincipalExtractor instead")
|
||||
var subjectPrincipalRegex: String? = null
|
||||
|
||||
|
||||
|
|
|
@ -14,7 +14,8 @@
|
|||
# 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.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
|
||||
|
@ -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.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
|
||||
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.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
|
||||
|
|
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.credentials.TestSaml2X509Credentials;
|
||||
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.Saml2AuthenticationException;
|
||||
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.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.TestSaml2Authentications;
|
||||
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(DefaultSaml2AuthenticatedPrincipal.class,
|
||||
(r) -> TestSaml2Authentications.authentication().getPrincipal());
|
||||
generatorByClassName.put(Saml2Authentication.class,
|
||||
(r) -> applyDetails(TestSaml2Authentications.authentication()));
|
||||
Saml2Authentication saml2 = 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,
|
||||
(r) -> TestSaml2PostAuthenticationRequests.create());
|
||||
generatorByClassName.put(Saml2RedirectAuthenticationRequest.class,
|
||||
|
|
|
@ -260,6 +260,12 @@ class SpringSecurityCoreVersionSerializableTests {
|
|||
String version = System.getProperty("springSecurityVersion");
|
||||
String[] parts = version.split("\\.");
|
||||
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";
|
||||
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");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -58,7 +58,7 @@ public class AuthorizationProxyConfigurationTests {
|
|||
@Test
|
||||
public void proxyWhenNotPreAuthorizedThenDenies() {
|
||||
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)
|
||||
.withMessage("Access Denied");
|
||||
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(toaster::extractBread)
|
||||
|
@ -69,7 +69,7 @@ public class AuthorizationProxyConfigurationTests {
|
|||
@Test
|
||||
public void proxyWhenPreAuthorizedThenAllows() {
|
||||
this.spring.register(DefaultsConfig.class).autowire();
|
||||
Toaster toaster = (Toaster) this.proxyFactory.proxy(new Toaster());
|
||||
Toaster toaster = this.proxyFactory.proxy(new Toaster());
|
||||
toaster.makeToast();
|
||||
assertThat(toaster.extractBread()).isEqualTo("yummy");
|
||||
}
|
||||
|
@ -77,7 +77,7 @@ public class AuthorizationProxyConfigurationTests {
|
|||
@Test
|
||||
public void proxyReactiveWhenNotPreAuthorizedThenDenies() {
|
||||
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();
|
||||
StepVerifier
|
||||
.create(toaster.reactiveMakeToast().contextWrite(ReactiveSecurityContextHolder.withAuthentication(user)))
|
||||
|
@ -90,7 +90,7 @@ public class AuthorizationProxyConfigurationTests {
|
|||
@Test
|
||||
public void proxyReactiveWhenPreAuthorizedThenAllows() {
|
||||
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();
|
||||
StepVerifier
|
||||
.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.ObservationTextPublisher;
|
||||
import jakarta.annotation.security.DenyAll;
|
||||
import org.aopalliance.aop.Advice;
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import org.springframework.aop.Advisor;
|
||||
import org.springframework.aop.Pointcut;
|
||||
import org.springframework.aop.config.AopConfigUtils;
|
||||
import org.springframework.aop.support.DefaultPointcutAdvisor;
|
||||
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.core.annotation.AnnotationAwareOrderComparator;
|
||||
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.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.converter.HttpMessageNotWritableException;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.access.PermissionEvaluator;
|
||||
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.PrePostTemplateDefaults;
|
||||
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.observation.SecurityObservationSettings;
|
||||
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.WithMockUser;
|
||||
import org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener;
|
||||
import org.springframework.security.web.util.ThrowableAnalyzer;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.TestExecutionListeners;
|
||||
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.support.AnnotationConfigWebApplicationContext;
|
||||
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.assertThatExceptionOfType;
|
||||
|
@ -127,6 +150,9 @@ import static org.mockito.Mockito.spy;
|
|||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
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}.
|
||||
|
@ -148,6 +174,9 @@ public class PrePostMethodSecurityConfigurationTests {
|
|||
@Autowired(required = false)
|
||||
BusinessService businessService;
|
||||
|
||||
@Autowired(required = false)
|
||||
MockMvc mvc;
|
||||
|
||||
@WithMockUser
|
||||
@Test
|
||||
public void customMethodSecurityPreAuthorizeAdminWhenRoleUserThenAccessDeniedException() {
|
||||
|
@ -733,6 +762,28 @@ public class PrePostMethodSecurityConfigurationTests {
|
|||
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
|
||||
@WithMockUser(authorities = "airplane:read")
|
||||
public void findByIdWhenAuthorizedResponseEntityThenAuthorizes() {
|
||||
|
@ -804,6 +855,46 @@ public class PrePostMethodSecurityConfigurationTests {
|
|||
.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
|
||||
@WithMockUser(authorities = "airplane:read")
|
||||
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() {
|
||||
return (context) -> ((AnnotationConfigWebApplicationContext) context).setAllowBeanDefinitionOverriding(false);
|
||||
}
|
||||
|
@ -1648,14 +1830,6 @@ public class PrePostMethodSecurityConfigurationTests {
|
|||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
@Order(1)
|
||||
static TargetVisitor mock() {
|
||||
return Mockito.mock(TargetVisitor.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
@Order(0)
|
||||
static TargetVisitor skipValueTypes() {
|
||||
return TargetVisitor.defaultsSkipValueTypes();
|
||||
}
|
||||
|
@ -1688,10 +1862,39 @@ public class PrePostMethodSecurityConfigurationTests {
|
|||
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) {
|
||||
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) {
|
||||
this.flights.put(flight.getId(), 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.context.support.AnnotationConfigWebApplicationContext;
|
||||
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 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
|
||||
public void requestRejectedHandlerInvoked() throws ServletException, IOException {
|
||||
loadConfig(DefaultConfig.class);
|
||||
|
@ -132,30 +110,6 @@ public class WebSecurityTests {
|
|||
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) {
|
||||
this.context = new AnnotationConfigWebApplicationContext();
|
||||
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
|
||||
@EnableWebSecurity
|
||||
static class RequestRejectedHandlerConfig {
|
||||
|
|
|
@ -21,7 +21,6 @@ import okhttp3.mockwebserver.Dispatcher;
|
|||
import okhttp3.mockwebserver.MockResponse;
|
||||
import okhttp3.mockwebserver.MockWebServer;
|
||||
import okhttp3.mockwebserver.RecordedRequest;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
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.test.web.servlet.MockMvc;
|
||||
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.RestController;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
@ -197,7 +197,7 @@ public class SecurityReactorContextConfigurationResourceServerTests {
|
|||
public MockResponse dispatch(RecordedRequest request) {
|
||||
MockResponse response = new MockResponse().setResponseCode(200);
|
||||
String header = request.getHeader("Authorization");
|
||||
if (StringUtils.isBlank(header)) {
|
||||
if (!StringUtils.hasText(header)) {
|
||||
return response;
|
||||
}
|
||||
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");
|
||||
* 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.assertThatExceptionOfType;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.doCallRealMethod;
|
||||
|
@ -153,6 +154,7 @@ public class AuthorizeHttpRequestsConfigurerTests {
|
|||
@Test
|
||||
public void configureMvcMatcherAccessAuthorizationManagerWhenNotNullThenVerifyUse() throws Exception {
|
||||
CustomAuthorizationManagerConfig.authorizationManager = mock(AuthorizationManager.class);
|
||||
given(CustomAuthorizationManagerConfig.authorizationManager.authorize(any(), any())).willCallRealMethod();
|
||||
this.spring.register(CustomAuthorizationManagerConfig.class, BasicController.class).autowire();
|
||||
this.mvc.perform(get("/")).andExpect(status().isOk());
|
||||
verify(CustomAuthorizationManagerConfig.authorizationManager).check(any(), any());
|
||||
|
@ -161,6 +163,8 @@ public class AuthorizeHttpRequestsConfigurerTests {
|
|||
@Test
|
||||
public void configureNoParameterMvcMatcherAccessAuthorizationManagerWhenNotNullThenVerifyUse() throws Exception {
|
||||
CustomAuthorizationManagerNoParameterConfig.authorizationManager = mock(AuthorizationManager.class);
|
||||
given(CustomAuthorizationManagerNoParameterConfig.authorizationManager.authorize(any(), any()))
|
||||
.willCallRealMethod();
|
||||
this.spring.register(CustomAuthorizationManagerNoParameterConfig.class, BasicController.class).autowire();
|
||||
this.mvc.perform(get("/")).andExpect(status().isOk());
|
||||
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.context.support.AnnotationConfigWebApplicationContext;
|
||||
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 static org.assertj.core.api.Assertions.assertThat;
|
||||
|
@ -160,67 +158,6 @@ public class AuthorizeRequestsTests {
|
|||
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
|
||||
public void mvcMatcherPathVariables() throws Exception {
|
||||
loadConfig(MvcMatcherPathVariablesConfig.class);
|
||||
|
@ -245,35 +182,6 @@ public class AuthorizeRequestsTests {
|
|||
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) {
|
||||
this.context = new AnnotationConfigWebApplicationContext();
|
||||
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");
|
||||
* 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.request;
|
||||
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.status;
|
||||
|
||||
|
@ -613,6 +614,37 @@ public class CsrfConfigurerTests {
|
|||
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
|
||||
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
|
||||
@EnableWebSecurity
|
||||
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.context.support.AnnotationConfigWebApplicationContext;
|
||||
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 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
|
||||
public void mvcMatcherGetFiltersNoUnsupportedMethodExceptionFromDummyRequest() {
|
||||
loadConfig(MvcMatcherConfig.class);
|
||||
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
|
||||
public void requestMatchersMvcMatcherServletPath() throws Exception {
|
||||
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;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
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.UserDetailsService;
|
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||
import org.springframework.security.web.DefaultSecurityFilterChain;
|
||||
import org.springframework.security.web.FilterChainProxy;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.servlet.MockServletContext;
|
||||
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.RestController;
|
||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
||||
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 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
|
||||
public void securityMatcherWhenMvcMatcherAndGetFiltersNoUnsupportedMethodExceptionFromDummyRequest() {
|
||||
loadConfig(SecurityMatcherMvcConfig.class);
|
||||
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
|
||||
public void securityMatchersMvcMatcherServletPath() throws Exception {
|
||||
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.context.support.AnnotationConfigWebApplicationContext;
|
||||
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 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
|
||||
public void anonymousUrlAuthorization() {
|
||||
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
|
||||
@Configuration
|
||||
@EnableWebMvc
|
||||
|
|
|
@ -43,7 +43,9 @@ import org.springframework.security.core.userdetails.UserDetailsService;
|
|||
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
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.X509TestUtils;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
@ -155,6 +157,28 @@ public class X509ConfigurerTests {
|
|||
// @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) {
|
||||
try (InputStream is = new ClassPathResource(location).getInputStream()) {
|
||||
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.DefaultBearerTokenResolver;
|
||||
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.provisioning.InMemoryUserDetailsManager;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.access.AccessDeniedHandler;
|
||||
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.MvcResult;
|
||||
import org.springframework.test.web.servlet.ResultMatcher;
|
||||
|
@ -760,13 +762,6 @@ public class OAuth2ResourceServerConfigurerTests {
|
|||
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
|
||||
public void requestWhenCustomAuthenticationDetailsSourceThenUsed() throws Exception {
|
||||
this.spring.register(CustomAuthenticationDetailsSource.class, JwtDecoderConfig.class, BasicController.class)
|
||||
|
@ -1416,6 +1411,47 @@ public class OAuth2ResourceServerConfigurerTests {
|
|||
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) {
|
||||
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
|
||||
@EnableWebSecurity
|
||||
static class MultipleIssuersConfig {
|
||||
|
|
|
@ -253,7 +253,7 @@ public class Saml2LoginConfigurerTests {
|
|||
public void authenticationRequestWhenAuthenticationRequestResolverBeanThenUses() throws Exception {
|
||||
this.spring.register(CustomAuthenticationRequestResolverBean.class).autowire();
|
||||
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 decoded = URLDecoder.decode(samlRequest, "UTF-8");
|
||||
String inflated = Saml2Utils.samlInflate(Saml2Utils.samlDecode(decoded));
|
||||
|
@ -264,7 +264,7 @@ public class Saml2LoginConfigurerTests {
|
|||
public void authenticationRequestWhenAuthenticationRequestResolverDslThenUses() throws Exception {
|
||||
this.spring.register(CustomAuthenticationRequestResolverDsl.class).autowire();
|
||||
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 decoded = URLDecoder.decode(samlRequest, "UTF-8");
|
||||
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");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -389,14 +389,14 @@ public class Saml2LogoutConfigurerTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void saml2LogoutRequestWhenInvalidSamlRequestThen401() throws Exception {
|
||||
public void saml2LogoutRequestWhenInvalidSamlRequestThen302Redirect() throws Exception {
|
||||
this.spring.register(Saml2LogoutDefaultsConfig.class).autowire();
|
||||
this.mvc
|
||||
.perform(get("/logout/saml2/slo").param("SAMLRequest", this.apLogoutRequest)
|
||||
.param("RelayState", this.apLogoutRequestRelayState)
|
||||
.param("SigAlg", this.apLogoutRequestSigAlg)
|
||||
.with(authentication(this.user)))
|
||||
.andExpect(status().isUnauthorized());
|
||||
.andExpect(status().isFound());
|
||||
verifyNoInteractions(getBean(LogoutHandler.class));
|
||||
}
|
||||
|
||||
|
|
|
@ -30,12 +30,12 @@ import java.util.TreeMap;
|
|||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.security.config.http.SecurityFiltersAssertions;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
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 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();
|
||||
|
||||
|
@ -86,7 +86,7 @@ public class XsdDocumentedTests {
|
|||
.flatMap(XmlNode::children)
|
||||
.flatMap(XmlNode::children)
|
||||
.map((node) -> node.attribute("value"))
|
||||
.filter(StringUtils::isNotEmpty)
|
||||
.filter(StringUtils::hasText)
|
||||
.collect(Collectors.toList());
|
||||
// @formatter:on
|
||||
SecurityFiltersAssertions.assertEquals(nodes);
|
||||
|
@ -129,7 +129,7 @@ public class XsdDocumentedTests {
|
|||
.flatMap(XmlNode::children)
|
||||
.flatMap(XmlNode::children)
|
||||
.map((node) -> node.attribute("value"))
|
||||
.filter(StringUtils::isNotEmpty)
|
||||
.filter(StringUtils::hasText)
|
||||
.collect(Collectors.toList());
|
||||
// @formatter:on
|
||||
assertThat(nodes).isEqualTo(expected);
|
||||
|
@ -151,8 +151,8 @@ public class XsdDocumentedTests {
|
|||
.list((dir, name) -> name.endsWith(".xsd"));
|
||||
// @formatter:on
|
||||
assertThat(schemas.length)
|
||||
.withFailMessage("the count is equal to 27, if not then schemaDocument needs updating")
|
||||
.isEqualTo(27);
|
||||
.withFailMessage("the count is equal to 28, if not then schemaDocument needs updating")
|
||||
.isEqualTo(28);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -254,8 +254,6 @@ public class InterceptUrlConfigTests {
|
|||
public void requestWhenUsingMvcMatchersThenAuthorizesRequestsAccordingly() throws Exception {
|
||||
this.spring.configLocations(this.xml("MvcMatchers")).autowire();
|
||||
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
|
||||
|
@ -304,10 +302,6 @@ public class InterceptUrlConfigTests {
|
|||
// @formatter:off
|
||||
this.mvc.perform(get("/spring/path").servletPath("/spring"))
|
||||
.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
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ import java.io.IOException;
|
|||
import java.io.OutputStream;
|
||||
import java.security.AccessController;
|
||||
import java.security.Principal;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
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.logout.LogoutFilter;
|
||||
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.DefaultLogoutPageGeneratingFilter;
|
||||
import org.springframework.security.web.authentication.ui.DefaultResourcesFilter;
|
||||
|
@ -398,6 +400,27 @@ public class MiscHttpConfigTests {
|
|||
.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
|
||||
public void getWhenUsingX509AndPropertyPlaceholderThenSubjectPrincipalRegexIsConfigured() throws Exception {
|
||||
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");
|
||||
* 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.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -50,13 +49,11 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
|||
import org.mockito.Mockito;
|
||||
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.FactoryBean;
|
||||
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.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
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.NimbusJwtDecoder;
|
||||
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.introspection.NimbusOpaqueTokenIntrospector;
|
||||
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.web.BearerTokenResolver;
|
||||
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.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
|
@ -462,6 +461,24 @@ public class OAuth2ResourceServerBeanDefinitionParserTests {
|
|||
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
|
||||
public void requestWhenBearerTokenResolverAllowsRequestBodyThenEitherHeaderOrRequestBodyIsAccepted()
|
||||
throws Exception {
|
||||
|
@ -521,14 +538,6 @@ public class OAuth2ResourceServerBeanDefinitionParserTests {
|
|||
// @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
|
||||
public void requestWhenCustomJwtDecoderThenUsed() throws Exception {
|
||||
this.spring.configLocations(xml("MockJwtDecoder"), xml("Jwt")).autowire();
|
||||
|
@ -545,6 +554,12 @@ public class OAuth2ResourceServerBeanDefinitionParserTests {
|
|||
.isThrownBy(() -> this.spring.configLocations(xml("JwtDecoderAndJwkSetUri")).autowire());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureWhenAuthenticationConverterAndJwkSetUriThenException() {
|
||||
assertThatExceptionOfType(BeanDefinitionStoreException.class).isThrownBy(
|
||||
() -> this.spring.configLocations(xml("AuthenticationConverterAndBearerTokenResolver")).autowire());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWhenRealmNameConfiguredThenUsesOnUnauthenticated() throws Exception {
|
||||
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");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -287,15 +287,16 @@ public class Saml2LogoutBeanDefinitionParserTests {
|
|||
.andExpect(status().isBadRequest());
|
||||
}
|
||||
|
||||
// gh-14635
|
||||
@Test
|
||||
public void saml2LogoutRequestWhenInvalidSamlRequestThen401() throws Exception {
|
||||
public void saml2LogoutRequestWhenInvalidSamlRequestThen302Redirect() throws Exception {
|
||||
this.spring.configLocations(this.xml("Default")).autowire();
|
||||
this.mvc
|
||||
.perform(get("/logout/saml2/slo").param("SAMLRequest", this.apLogoutRequest)
|
||||
.param("RelayState", this.apLogoutRequestRelayState)
|
||||
.param("SigAlg", this.apLogoutRequestSigAlg)
|
||||
.with(authentication(this.saml2User)))
|
||||
.andExpect(status().isUnauthorized());
|
||||
.andExpect(status().isFound());
|
||||
}
|
||||
|
||||
@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");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -115,6 +115,24 @@ public class CommonOAuth2ProviderTests {
|
|||
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) {
|
||||
return builder(provider).build();
|
||||
}
|
||||
|
|
|
@ -18,8 +18,6 @@ package org.springframework.security.config.web.server;
|
|||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
@ -114,12 +112,13 @@ public class CorsSpecTests {
|
|||
.exchange()
|
||||
.returnResult(String.class);
|
||||
// @formatter:on
|
||||
Map<String, List<String>> responseHeaders = response.getResponseHeaders();
|
||||
HttpHeaders responseHeaders = response.getResponseHeaders();
|
||||
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()) {
|
||||
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.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
@ -80,14 +78,14 @@ public class HeaderSpecTests {
|
|||
|
||||
@Test
|
||||
public void headersWhenDisableThenNoSecurityHeaders() {
|
||||
new HashSet<>(this.expectedHeaders.keySet()).forEach(this::expectHeaderNamesNotPresent);
|
||||
new HashSet<>(this.expectedHeaders.headerNames()).forEach(this::expectHeaderNamesNotPresent);
|
||||
this.http.headers().disable();
|
||||
assertHeaders();
|
||||
}
|
||||
|
||||
@Test
|
||||
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());
|
||||
assertHeaders();
|
||||
}
|
||||
|
@ -515,12 +513,13 @@ public class HeaderSpecTests {
|
|||
.uri("https://example.com/")
|
||||
.exchange()
|
||||
.returnResult(String.class);
|
||||
Map<String, List<String>> responseHeaders = response.getResponseHeaders();
|
||||
HttpHeaders responseHeaders = response.getResponseHeaders();
|
||||
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()) {
|
||||
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) {
|
||||
MockResponse response = new MockResponse();
|
||||
response.setResponseCode(result.getStatus().value());
|
||||
for (String name : result.getResponseHeaders().keySet()) {
|
||||
for (String name : result.getResponseHeaders().headerNames()) {
|
||||
response.addHeader(name, result.getResponseHeaders().getFirst(name));
|
||||
}
|
||||
String body = result.getResponseBody().blockFirst();
|
||||
|
|
|
@ -85,7 +85,7 @@ final class HtmlUnitWebTestClient {
|
|||
}
|
||||
return request;
|
||||
}
|
||||
return request.body(BodyInserters.fromObject(requestBody));
|
||||
return request.body(BodyInserters.fromProducer(requestBody, String.class));
|
||||
}
|
||||
|
||||
private MultiValueMap<String, String> formData(List<NameValuePair> params) {
|
||||
|
@ -161,7 +161,7 @@ final class HtmlUnitWebTestClient {
|
|||
redirectUrl = scheme + "://" + host + location.toASCIIString();
|
||||
}
|
||||
// @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()))
|
||||
.cookies((cookies) -> cookies.addAll(request.cookies()))
|
||||
.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
|
||||
@EnableWebSecurity
|
||||
@EnableWebMvc
|
||||
|
@ -193,14 +173,6 @@ class AuthorizeHttpRequestsDslTests {
|
|||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
open class LegacyMvcMatchingConfig : WebMvcConfigurer {
|
||||
override fun configurePathMatch(configurer: PathMatchConfigurer) {
|
||||
configurer.setUseSuffixPatternMatch(true)
|
||||
configurer.setUseTrailingSlashMatch(true)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `request when secured by mvc path variables then responds based on path variable value`() {
|
||||
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
|
||||
@EnableWebSecurity
|
||||
@EnableWebMvc
|
||||
|
@ -179,14 +159,6 @@ class AuthorizeRequestsDslTests {
|
|||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
open class LegacyMvcMatchingConfig : WebMvcConfigurer {
|
||||
override fun configurePathMatch(configurer: PathMatchConfigurer) {
|
||||
configurer.setUseSuffixPatternMatch(true)
|
||||
configurer.setUseTrailingSlashMatch(true)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `request when secured by mvc path variables then responds based on path variable value`() {
|
||||
this.spring.register(MvcMatcherPathVariablesConfig::class.java).autowire()
|
||||
|
|
|
@ -127,7 +127,7 @@ class ServerHttpsRedirectDslTests {
|
|||
return http {
|
||||
redirectToHttps {
|
||||
httpsRedirectWhen {
|
||||
it.request.headers.containsKey("X-Requires-Https")
|
||||
it.request.headers.headerNames().contains("X-Requires-Https")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -165,7 +165,7 @@ class ServerHttpsRedirectDslTests {
|
|||
redirectToHttps {
|
||||
httpsRedirectWhen(PathPatternParserServerWebExchangeMatcher("/secure"))
|
||||
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.apache.directory" level="ERROR"/>
|
||||
|
||||
<root level="${root.level:-WARN}">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</root>
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
</http>
|
||||
|
||||
<mvc:annotation-driven>
|
||||
<mvc:path-matching suffix-pattern="true" trailing-slash="true"/>
|
||||
<mvc:path-matching />
|
||||
</mvc:annotation-driven>
|
||||
|
||||
<b:bean name="path" class="org.springframework.security.config.http.InterceptUrlConfigTests.PathController"/>
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
</http>
|
||||
|
||||
<mvc:annotation-driven>
|
||||
<mvc:path-matching suffix-pattern="true"/>
|
||||
<mvc:path-matching />
|
||||
</mvc:annotation-driven>
|
||||
|
||||
<b:bean name="path" class="org.springframework.security.config.http.InterceptUrlConfigTests.PathController"/>
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
</http>
|
||||
|
||||
<mvc:annotation-driven>
|
||||
<mvc:path-matching suffix-pattern="true" trailing-slash="true"/>
|
||||
<mvc:path-matching />
|
||||
</mvc:annotation-driven>
|
||||
|
||||
<b:bean name="path" class="org.springframework.security.config.http.InterceptUrlConfigTests.PathController"/>
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
</http>
|
||||
|
||||
<mvc:annotation-driven>
|
||||
<mvc:path-matching suffix-pattern="true"/>
|
||||
<mvc:path-matching />
|
||||
</mvc:annotation-driven>
|
||||
|
||||
<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