Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 612909ac4f | |||
| 28f98b3351 | |||
| ed96e2cddf | |||
| 7200f76ac1 | |||
| 094b71b329 | |||
| 491f0f647c | |||
| 1db8734101 | |||
| 4b44a2d924 | |||
| e8b9a35494 | |||
| 82e5f62079 | |||
| 30bc2634d7 | |||
| 13ca7ac4d4 | |||
| c4f061c63d | |||
| 2c6d053ceb | |||
| ff4745a461 | |||
| 38a7ffcc9b | |||
| 6f55fc60bb | |||
| d0491c07e9 | |||
| 486c5118d2 | |||
| d02ab50577 | |||
| 17d8293be1 |
+11
-4
@@ -34,6 +34,14 @@ class RepositoryConventionPlugin implements Plugin<Project> {
|
||||
if (forceMavenRepositories?.contains('local')) {
|
||||
mavenLocal()
|
||||
}
|
||||
maven {
|
||||
name = 'shibboleth'
|
||||
url = 'https://build.shibboleth.net/nexus/content/repositories/releases/'
|
||||
content {
|
||||
includeGroupByRegex('org\\.opensaml.*')
|
||||
includeGroupByRegex('net\\.shibboleth.*')
|
||||
}
|
||||
}
|
||||
mavenCentral()
|
||||
if (isSnapshot) {
|
||||
maven {
|
||||
@@ -67,12 +75,11 @@ class RepositoryConventionPlugin implements Plugin<Project> {
|
||||
password project.artifactoryPassword
|
||||
}
|
||||
}
|
||||
content {
|
||||
excludeGroup('net.minidev')
|
||||
}
|
||||
url = 'https://repo.spring.io/release/'
|
||||
}
|
||||
maven {
|
||||
name = 'shibboleth'
|
||||
url = 'https://build.shibboleth.net/nexus/content/repositories/releases/'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+7
-7
@@ -125,27 +125,27 @@ public class RepositoryConventionPluginTests {
|
||||
|
||||
private void assertSnapshotRepository(RepositoryHandler repositories) {
|
||||
assertThat(repositories).extracting(ArtifactRepository::getName).hasSize(5);
|
||||
assertThat(((MavenArtifactRepository) repositories.get(0)).getUrl().toString())
|
||||
.isEqualTo("https://repo.maven.apache.org/maven2/");
|
||||
assertThat(((MavenArtifactRepository) repositories.get(1)).getUrl().toString())
|
||||
.isEqualTo("https://repo.spring.io/snapshot/");
|
||||
.isEqualTo("https://repo.maven.apache.org/maven2/");
|
||||
assertThat(((MavenArtifactRepository) repositories.get(2)).getUrl().toString())
|
||||
.isEqualTo("https://repo.spring.io/snapshot/");
|
||||
assertThat(((MavenArtifactRepository) repositories.get(3)).getUrl().toString())
|
||||
.isEqualTo("https://repo.spring.io/milestone/");
|
||||
}
|
||||
|
||||
private void assertMilestoneRepository(RepositoryHandler repositories) {
|
||||
assertThat(repositories).extracting(ArtifactRepository::getName).hasSize(4);
|
||||
assertThat(((MavenArtifactRepository) repositories.get(0)).getUrl().toString())
|
||||
.isEqualTo("https://repo.maven.apache.org/maven2/");
|
||||
assertThat(((MavenArtifactRepository) repositories.get(1)).getUrl().toString())
|
||||
.isEqualTo("https://repo.maven.apache.org/maven2/");
|
||||
assertThat(((MavenArtifactRepository) repositories.get(2)).getUrl().toString())
|
||||
.isEqualTo("https://repo.spring.io/milestone/");
|
||||
}
|
||||
|
||||
private void assertReleaseRepository(RepositoryHandler repositories) {
|
||||
assertThat(repositories).extracting(ArtifactRepository::getName).hasSize(3);
|
||||
assertThat(((MavenArtifactRepository) repositories.get(0)).getUrl().toString())
|
||||
.isEqualTo("https://repo.maven.apache.org/maven2/");
|
||||
assertThat(((MavenArtifactRepository) repositories.get(1)).getUrl().toString())
|
||||
.isEqualTo("https://repo.maven.apache.org/maven2/");
|
||||
assertThat(((MavenArtifactRepository) repositories.get(2)).getUrl().toString())
|
||||
.isEqualTo("https://repo.spring.io/release/");
|
||||
}
|
||||
|
||||
|
||||
@@ -4,10 +4,6 @@ apply plugin: 'io.spring.convention.spring-module'
|
||||
apply plugin: 'trang'
|
||||
apply plugin: 'kotlin'
|
||||
|
||||
repositories {
|
||||
maven { url "https://build.shibboleth.net/nexus/content/repositories/releases/" }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
management platform(project(":spring-security-dependencies"))
|
||||
// NB: Don't add other compile time dependencies to the config module as this breaks tooling
|
||||
|
||||
+4
@@ -36,6 +36,10 @@ class MethodSecurityAdvisorRegistrar implements ImportBeanDefinitionRegistrar {
|
||||
}
|
||||
|
||||
private void registerAsAdvisor(String prefix, BeanDefinitionRegistry registry) {
|
||||
String advisorName = prefix + "Advisor";
|
||||
if (registry.containsBeanDefinition(advisorName)) {
|
||||
return;
|
||||
}
|
||||
String interceptorName = prefix + "MethodInterceptor";
|
||||
if (!registry.containsBeanDefinition(interceptorName)) {
|
||||
return;
|
||||
|
||||
+32
-4
@@ -18,6 +18,8 @@ package org.springframework.security.config.annotation.web;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -312,18 +314,31 @@ public abstract class AbstractRequestMatcherRegistry<C> {
|
||||
if (servletContext == null) {
|
||||
return requestMatchers(RequestMatchers.antMatchersAsArray(method, patterns));
|
||||
}
|
||||
Map<String, ? extends ServletRegistration> registrations = servletContext.getServletRegistrations();
|
||||
if (registrations == null) {
|
||||
Map<String, ? extends ServletRegistration> registrations = mappableServletRegistrations(servletContext);
|
||||
if (registrations.isEmpty()) {
|
||||
return requestMatchers(RequestMatchers.antMatchersAsArray(method, patterns));
|
||||
}
|
||||
if (!hasDispatcherServlet(registrations)) {
|
||||
return requestMatchers(RequestMatchers.antMatchersAsArray(method, patterns));
|
||||
}
|
||||
Assert.isTrue(registrations.size() == 1,
|
||||
"This method cannot decide whether these patterns are Spring MVC patterns or not. If this endpoint is a Spring MVC endpoint, please use requestMatchers(MvcRequestMatcher); otherwise, please use requestMatchers(AntPathRequestMatcher).");
|
||||
if (registrations.size() > 1) {
|
||||
String errorMessage = computeErrorMessage(registrations.values());
|
||||
throw new IllegalArgumentException(errorMessage);
|
||||
}
|
||||
return requestMatchers(createMvcMatchers(method, patterns).toArray(new RequestMatcher[0]));
|
||||
}
|
||||
|
||||
private Map<String, ? extends ServletRegistration> mappableServletRegistrations(ServletContext servletContext) {
|
||||
Map<String, ServletRegistration> mappable = new LinkedHashMap<>();
|
||||
for (Map.Entry<String, ? extends ServletRegistration> entry : servletContext.getServletRegistrations()
|
||||
.entrySet()) {
|
||||
if (!entry.getValue().getMappings().isEmpty()) {
|
||||
mappable.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
return mappable;
|
||||
}
|
||||
|
||||
private boolean hasDispatcherServlet(Map<String, ? extends ServletRegistration> registrations) {
|
||||
if (registrations == null) {
|
||||
return false;
|
||||
@@ -344,6 +359,19 @@ public abstract class AbstractRequestMatcherRegistry<C> {
|
||||
return false;
|
||||
}
|
||||
|
||||
private String computeErrorMessage(Collection<? extends ServletRegistration> registrations) {
|
||||
String template = "This method cannot decide whether these patterns are Spring MVC patterns or not. "
|
||||
+ "If this endpoint is a Spring MVC endpoint, please use requestMatchers(MvcRequestMatcher); "
|
||||
+ "otherwise, please use requestMatchers(AntPathRequestMatcher).\n\n"
|
||||
+ "This is because there is more than one mappable servlet in your servlet context: %s.\n\n"
|
||||
+ "For each MvcRequestMatcher, call MvcRequestMatcher#setServletPath to indicate the servlet path.";
|
||||
Map<String, Collection<String>> mappings = new LinkedHashMap<>();
|
||||
for (ServletRegistration registration : registrations) {
|
||||
mappings.put(registration.getClassName(), registration.getMappings());
|
||||
}
|
||||
return String.format(template, mappings);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* If the {@link HandlerMappingIntrospector} is available in the classpath, maps to an
|
||||
|
||||
@@ -16,8 +16,10 @@
|
||||
|
||||
package org.springframework.security.config;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -35,7 +37,7 @@ public class MockServletContext extends org.springframework.mock.web.MockServlet
|
||||
|
||||
public static MockServletContext mvc() {
|
||||
MockServletContext servletContext = new MockServletContext();
|
||||
servletContext.addServlet("dispatcherServlet", DispatcherServlet.class);
|
||||
servletContext.addServlet("dispatcherServlet", DispatcherServlet.class).addMapping("/");
|
||||
return servletContext;
|
||||
}
|
||||
|
||||
@@ -59,6 +61,8 @@ public class MockServletContext extends org.springframework.mock.web.MockServlet
|
||||
|
||||
private final Class<?> clazz;
|
||||
|
||||
private final Set<String> mappings = new LinkedHashSet<>();
|
||||
|
||||
MockServletRegistration(String name, Class<?> clazz) {
|
||||
this.name = name;
|
||||
this.clazz = clazz;
|
||||
@@ -91,12 +95,13 @@ public class MockServletContext extends org.springframework.mock.web.MockServlet
|
||||
|
||||
@Override
|
||||
public Set<String> addMapping(String... urlPatterns) {
|
||||
return null;
|
||||
this.mappings.addAll(Arrays.asList(urlPatterns));
|
||||
return this.mappings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getMappings() {
|
||||
return null;
|
||||
return this.mappings;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+15
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -20,6 +20,7 @@ import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
@@ -64,6 +65,8 @@ import org.springframework.security.test.context.support.WithSecurityContextTest
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.TestExecutionListeners;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
import org.springframework.web.context.ConfigurableWebApplicationContext;
|
||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
@@ -418,6 +421,17 @@ public class PrePostMethodSecurityConfigurationTests {
|
||||
assertThat(this.spring.getContext().containsBean("annotationSecurityAspect$0")).isFalse();
|
||||
}
|
||||
|
||||
// gh-13572
|
||||
@Test
|
||||
public void configureWhenBeanOverridingDisallowedThenWorks() {
|
||||
this.spring.register(MethodSecurityServiceConfig.class, BusinessServiceConfig.class)
|
||||
.postProcessor(disallowBeanOverriding()).autowire();
|
||||
}
|
||||
|
||||
private static Consumer<ConfigurableWebApplicationContext> disallowBeanOverriding() {
|
||||
return (context) -> ((AnnotationConfigWebApplicationContext) context).setAllowBeanDefinitionOverriding(false);
|
||||
}
|
||||
|
||||
@EnableMethodSecurity
|
||||
static class MethodSecurityServiceConfig {
|
||||
|
||||
|
||||
+16
-3
@@ -63,12 +63,13 @@ public class AbstractRequestMatcherRegistryTests {
|
||||
private WebApplicationContext context;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
public void setUp() throws Exception {
|
||||
this.matcherRegistry = new TestRequestMatcherRegistry();
|
||||
this.context = mock(WebApplicationContext.class);
|
||||
given(this.context.getBean(ObjectPostProcessor.class)).willReturn(NO_OP_OBJECT_POST_PROCESSOR);
|
||||
given(this.context.getServletContext()).willReturn(MockServletContext.mvc());
|
||||
this.matcherRegistry.setApplicationContext(this.context);
|
||||
mockMvcPresentClasspath(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -211,12 +212,24 @@ public class AbstractRequestMatcherRegistryTests {
|
||||
public void requestMatchersWhenAmbiguousServletsThenException() {
|
||||
MockServletContext servletContext = new MockServletContext();
|
||||
given(this.context.getServletContext()).willReturn(servletContext);
|
||||
servletContext.addServlet("dispatcherServlet", DispatcherServlet.class);
|
||||
servletContext.addServlet("servletTwo", Servlet.class);
|
||||
servletContext.addServlet("dispatcherServlet", DispatcherServlet.class).addMapping("/");
|
||||
servletContext.addServlet("servletTwo", Servlet.class).addMapping("/servlet/**");
|
||||
assertThatExceptionOfType(IllegalArgumentException.class)
|
||||
.isThrownBy(() -> this.matcherRegistry.requestMatchers("/**"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestMatchersWhenUnmappableServletsThenSkips() {
|
||||
mockMvcIntrospector(true);
|
||||
MockServletContext servletContext = new MockServletContext();
|
||||
given(this.context.getServletContext()).willReturn(servletContext);
|
||||
servletContext.addServlet("dispatcherServlet", DispatcherServlet.class).addMapping("/");
|
||||
servletContext.addServlet("servletTwo", Servlet.class);
|
||||
List<RequestMatcher> requestMatchers = this.matcherRegistry.requestMatchers("/**");
|
||||
assertThat(requestMatchers).hasSize(1);
|
||||
assertThat(requestMatchers.get(0)).isInstanceOf(MvcRequestMatcher.class);
|
||||
}
|
||||
|
||||
private void mockMvcIntrospector(boolean isPresent) {
|
||||
ApplicationContext context = this.matcherRegistry.getApplicationContext();
|
||||
given(context.containsBean("mvcHandlerMappingIntrospector")).willReturn(isPresent);
|
||||
|
||||
+2
-2
@@ -8,11 +8,11 @@ javaPlatform {
|
||||
|
||||
dependencies {
|
||||
api platform("org.springframework:spring-framework-bom:$springFrameworkVersion")
|
||||
api platform("io.projectreactor:reactor-bom:2020.0.34")
|
||||
api platform("io.projectreactor:reactor-bom:2020.0.35")
|
||||
api platform("io.rsocket:rsocket-bom:1.1.4")
|
||||
api platform("org.junit:junit-bom:5.9.3")
|
||||
api platform("org.mockito:mockito-bom:4.8.1")
|
||||
api platform("org.springframework.data:spring-data-bom:2021.2.14")
|
||||
api platform("org.springframework.data:spring-data-bom:2021.2.15")
|
||||
api platform("org.jetbrains.kotlin:kotlin-bom:$kotlinVersion")
|
||||
api platform("org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.6.4")
|
||||
api platform("com.fasterxml.jackson:jackson-bom:2.13.5")
|
||||
|
||||
@@ -692,7 +692,7 @@ open class SecurityConfiguration {
|
||||
|
||||
=== Publish an `AuthenticationManager` Bean
|
||||
|
||||
As part of `WebSecurityConfigurerAdapeter` removal, `configure(AuthenticationManagerBuilder)` is also removed.
|
||||
As part of `WebSecurityConfigurerAdapter` removal, `configure(AuthenticationManagerBuilder)` is also removed.
|
||||
Preparing for its removal will differ based on your reason for using it.
|
||||
|
||||
==== LDAP Authentication
|
||||
|
||||
@@ -410,8 +410,8 @@ fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
|
||||
[[webflux-headers-referrer]]
|
||||
== Referrer Policy
|
||||
|
||||
Spring Security does not add xref:features/exploits/headers.adoc#headers-referrer[Referrer Policy] headers by default.
|
||||
You can enable the Referrer Policy header using configuration as shown below:
|
||||
Spring Security adds the xref:features/exploits/headers.adoc#headers-referrer[Referrer Policy] header by default with the directive `no-referrer`.
|
||||
You can change the Referrer Policy header using configuration as shown below:
|
||||
|
||||
.Referrer Policy Configuration
|
||||
[tabs]
|
||||
|
||||
@@ -159,7 +159,39 @@ XML::
|
||||
|
||||
The following beans are required in an application context to enable remember-me services:
|
||||
|
||||
[source,xml]
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
@Bean
|
||||
RememberMeAuthenticationFilter rememberMeFilter() {
|
||||
RememberMeAuthenticationFilter rememberMeFilter = new RememberMeAuthenticationFilter();
|
||||
rememberMeFilter.setRememberMeServices(rememberMeServices());
|
||||
rememberMeFilter.setAuthenticationManager(theAuthenticationManager);
|
||||
return rememberMeFilter;
|
||||
}
|
||||
|
||||
@Bean
|
||||
TokenBasedRememberMeServices rememberMeServices() {
|
||||
TokenBasedRememberMeServices rememberMeServices = new TokenBasedRememberMeServices();
|
||||
rememberMeServices.setUserDetailsService(myUserDetailsService);
|
||||
rememberMeServices.setKey("springRocks");
|
||||
return rememberMeServices;
|
||||
}
|
||||
|
||||
@Bean
|
||||
RememberMeAuthenticationProvider rememberMeAuthenticationProvider() {
|
||||
RememberMeAuthenticationProvider rememberMeAuthenticationProvider = new RememberMeAuthenticationProvider();
|
||||
rememberMeAuthenticationProvider.setKey("springRocks");
|
||||
return rememberMeAuthenticationProvider;
|
||||
}
|
||||
----
|
||||
|
||||
XML::
|
||||
+
|
||||
[source,xml,role="secondary"]
|
||||
----
|
||||
<bean id="rememberMeFilter" class=
|
||||
"org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter">
|
||||
@@ -178,6 +210,7 @@ The following beans are required in an application context to enable remember-me
|
||||
<property name="key" value="springRocks"/>
|
||||
</bean>
|
||||
----
|
||||
======
|
||||
|
||||
Don't forget to add your `RememberMeServices` implementation to your `UsernamePasswordAuthenticationFilter.setRememberMeServices()` property, include the `RememberMeAuthenticationProvider` in your `AuthenticationManager.setProviders()` list, and add `RememberMeAuthenticationFilter` into your `FilterChainProxy` (typically immediately after your `UsernamePasswordAuthenticationFilter`).
|
||||
|
||||
|
||||
@@ -241,16 +241,15 @@ fun rest(): RestTemplate {
|
||||
val rest = RestTemplate()
|
||||
rest.interceptors.add(ClientHttpRequestInterceptor { request, body, execution ->
|
||||
val authentication: Authentication? = SecurityContextHolder.getContext().authentication
|
||||
if (authentication != null) {
|
||||
execution.execute(request, body)
|
||||
if (authentication == null) {
|
||||
return execution.execute(request, body)
|
||||
}
|
||||
|
||||
if (authentication!!.credentials !is AbstractOAuth2Token) {
|
||||
execution.execute(request, body)
|
||||
if (authentication.credentials !is AbstractOAuth2Token) {
|
||||
return execution.execute(request, body)
|
||||
}
|
||||
|
||||
val token: AbstractOAuth2Token = authentication.credentials as AbstractOAuth2Token
|
||||
request.headers.setBearerAuth(token.tokenValue)
|
||||
request.headers.setBearerAuth(authentication.credentials.tokenValue)
|
||||
execution.execute(request, body)
|
||||
})
|
||||
return rest
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
plugins {
|
||||
id 'org.antora' version '1.0.0'
|
||||
id 'io.spring.antora.generate-antora-yml' version '0.0.1'
|
||||
id 'io.spring.convention.repository'
|
||||
}
|
||||
|
||||
apply plugin: 'io.spring.convention.docs'
|
||||
@@ -59,10 +60,3 @@ def resolvedVersions(Configuration configuration) {
|
||||
.resolvedArtifacts
|
||||
.collectEntries { [(it.name + '-version'): it.moduleVersion.id.version] }
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven { url 'https://repo.spring.io/release' }
|
||||
maven { url 'https://repo.spring.io/milestone' }
|
||||
maven { url 'https://repo.spring.io/snapshot' }
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
aspectjVersion=1.9.19
|
||||
aspectjVersion=1.9.20
|
||||
springJavaformatVersion=0.0.39
|
||||
springBootVersion=2.7.12
|
||||
springFrameworkVersion=5.3.29
|
||||
openSamlVersion=3.4.6
|
||||
version=5.8.5-SNAPSHOT
|
||||
version=5.8.6
|
||||
kotlinVersion=1.7.22
|
||||
samplesBranch=5.8.x
|
||||
org.gradle.jvmargs=-Xmx3g -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError
|
||||
|
||||
Reference in New Issue
Block a user