Merge pull request #45163 from ngocnhan-tran1996
Build and Deploy Snapshot / Build and Deploy Snapshot (push) Has been cancelled Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[early-access:true toolchain:true version:24], map[id:${{ vars.UBUNTU_MEDIUM || 'ubuntu-latest' }} name:Linux]) (push) Has been cancelled Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[early-access:true toolchain:true version:24], map[id:windows-latest name:Windows]) (push) Has been cancelled Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:false version:17], map[id:${{ vars.UBUNTU_MEDIUM || 'ubuntu-latest' }} name:Linux]) (push) Has been cancelled Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:false version:17], map[id:windows-latest name:Windows]) (push) Has been cancelled Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:false version:21], map[id:${{ vars.UBUNTU_MEDIUM || 'ubuntu-latest' }} name:Linux]) (push) Has been cancelled Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:false version:21], map[id:windows-latest name:Windows]) (push) Has been cancelled Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:false version:22], map[id:${{ vars.UBUNTU_MEDIUM || 'ubuntu-latest' }} name:Linux]) (push) Has been cancelled Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:false version:22], map[id:windows-latest name:Windows]) (push) Has been cancelled Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:true version:23], map[id:${{ vars.UBUNTU_MEDIUM || 'ubuntu-latest' }} name:Linux]) (push) Has been cancelled Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:true version:23], map[id:windows-latest name:Windows]) (push) Has been cancelled Details
Run System Tests / Java ${{ matrix.java.version}} (map[toolchain:false version:17]) (push) Has been cancelled Details
Run System Tests / Java ${{ matrix.java.version}} (map[toolchain:true version:21]) (push) Has been cancelled Details
Build and Deploy Snapshot / Trigger Docs Build (push) Has been cancelled Details
Build and Deploy Snapshot / Verify (push) Has been cancelled Details

* pr/45163:
  Polish 'Migrate from AntPathRequestMatcher to PathPatternRequestMatcher'
  Migrate from AntPathRequestMatcher to PathPatternRequestMatcher

Closes gh-45163
This commit is contained in:
Phillip Webb 2025-04-11 18:47:22 -07:00
commit 86940bd111
14 changed files with 73 additions and 67 deletions

View File

@ -62,6 +62,7 @@ import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.web.cors.CorsConfiguration;
@ -183,16 +184,16 @@ public class CloudFoundryActuatorAutoConfiguration {
}
@Override
@SuppressWarnings("removal")
public void customize(WebSecurity web) {
List<RequestMatcher> requestMatchers = new ArrayList<>();
this.pathMappedEndpoints.getAllPaths()
.forEach((path) -> requestMatchers
.add(new org.springframework.security.web.util.matcher.AntPathRequestMatcher(path + "/**")));
requestMatchers.add(new org.springframework.security.web.util.matcher.AntPathRequestMatcher(BASE_PATH));
requestMatchers
.add(new org.springframework.security.web.util.matcher.AntPathRequestMatcher(BASE_PATH + "/"));
web.ignoring().requestMatchers(new OrRequestMatcher(requestMatchers));
List<RequestMatcher> matchers = new ArrayList<>();
this.pathMappedEndpoints.getAllPaths().forEach((path) -> matchers.add(pathMatcher(path + "/**")));
matchers.add(pathMatcher(BASE_PATH));
matchers.add(pathMatcher(BASE_PATH + "/"));
web.ignoring().requestMatchers(new OrRequestMatcher(matchers));
}
private PathPatternRequestMatcher pathMatcher(String path) {
return PathPatternRequestMatcher.withDefaults().matcher(path);
}
}

View File

@ -42,6 +42,7 @@ import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.MergedAnnotation;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.http.HttpMethod;
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
@ -231,14 +232,12 @@ public final class EndpointRequest {
return linksMatchers;
}
@SuppressWarnings("removal")
protected RequestMatcherProvider getRequestMatcherProvider(WebApplicationContext context) {
try {
return getRequestMatcherProviderBean(context);
}
catch (NoSuchBeanDefinitionException ex) {
return (pattern, method) -> new org.springframework.security.web.util.matcher.AntPathRequestMatcher(
pattern, (method != null) ? method.name() : null);
return (pattern, method) -> PathPatternRequestMatcher.withDefaults().matcher(method, pattern);
}
}

View File

@ -19,29 +19,26 @@ package org.springframework.boot.actuate.autoconfigure.security.servlet;
import java.util.function.Function;
import org.springframework.http.HttpMethod;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
/**
* {@link RequestMatcherProvider} that provides an {@link AntPathRequestMatcher}.
* {@link RequestMatcherProvider} that provides an {@link PathPatternRequestMatcher}.
*
* @author Madhura Bhave
* @author Chris Bono
*/
class AntPathRequestMatcherProvider implements RequestMatcherProvider {
class PathPatternRequestMatcherProvider implements RequestMatcherProvider {
private final Function<String, String> pathFactory;
AntPathRequestMatcherProvider(Function<String, String> pathFactory) {
PathPatternRequestMatcherProvider(Function<String, String> pathFactory) {
this.pathFactory = pathFactory;
}
@Override
@SuppressWarnings("removal")
public RequestMatcher getRequestMatcher(String pattern, HttpMethod httpMethod) {
String path = this.pathFactory.apply(pattern);
return new org.springframework.security.web.util.matcher.AntPathRequestMatcher(path,
(httpMethod != null) ? httpMethod.name() : null);
return PathPatternRequestMatcher.withDefaults().matcher(httpMethod, this.pathFactory.apply(pattern));
}
}

View File

@ -52,7 +52,7 @@ public class SecurityRequestMatchersManagementContextConfiguration {
@ConditionalOnMissingBean
@ConditionalOnClass(DispatcherServlet.class)
public RequestMatcherProvider requestMatcherProvider(DispatcherServletPath servletPath) {
return new AntPathRequestMatcherProvider(servletPath::getRelativePath);
return new PathPatternRequestMatcherProvider(servletPath::getRelativePath);
}
}
@ -65,7 +65,7 @@ public class SecurityRequestMatchersManagementContextConfiguration {
@Bean
public RequestMatcherProvider requestMatcherProvider(JerseyApplicationPath applicationPath) {
return new AntPathRequestMatcherProvider(applicationPath::getRelativePath);
return new PathPatternRequestMatcherProvider(applicationPath::getRelativePath);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2024 the original author or authors.
* Copyright 2012-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.
@ -182,8 +182,10 @@ class CloudFoundryActuatorAutoConfigurationTests {
testCloudFoundrySecurity(request, BASE_PATH + "/test", chain);
testCloudFoundrySecurity(request, BASE_PATH + "/test/a", chain);
request.setServletPath(BASE_PATH + "/other-path");
request.setRequestURI(BASE_PATH + "/other-path");
assertThat(chain.matches(request)).isFalse();
request.setServletPath("/some-other-path");
request.setRequestURI("/some-other-path");
assertThat(chain.matches(request)).isFalse();
});
}
@ -209,9 +211,9 @@ class CloudFoundryActuatorAutoConfigurationTests {
throw new IllegalStateException("No FilterChainProxy found");
}
private static void testCloudFoundrySecurity(MockHttpServletRequest request, String servletPath,
private static void testCloudFoundrySecurity(MockHttpServletRequest request, String requestUri,
SecurityFilterChain chain) {
request.setServletPath(servletPath);
request.setRequestURI(requestUri);
assertThat(chain.matches(request)).isTrue();
}

View File

@ -413,24 +413,24 @@ class EndpointRequestTests {
assertThat(this.matcher.matches(request)).as("Matches " + getRequestPath(request)).isTrue();
}
void doesNotMatch(String servletPath) {
doesNotMatch(mockRequest(null, servletPath));
void doesNotMatch(String requestUri) {
doesNotMatch(mockRequest(null, requestUri));
}
void doesNotMatch(HttpMethod httpMethod, String servletPath) {
doesNotMatch(mockRequest(httpMethod, servletPath));
void doesNotMatch(HttpMethod httpMethod, String requestUri) {
doesNotMatch(mockRequest(httpMethod, requestUri));
}
private void doesNotMatch(HttpServletRequest request) {
assertThat(this.matcher.matches(request)).as("Does not match " + getRequestPath(request)).isFalse();
}
private MockHttpServletRequest mockRequest(HttpMethod httpMethod, String servletPath) {
private MockHttpServletRequest mockRequest(HttpMethod httpMethod, String requestUri) {
MockServletContext servletContext = new MockServletContext();
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
MockHttpServletRequest request = new MockHttpServletRequest(servletContext);
if (servletPath != null) {
request.setServletPath(servletPath);
if (requestUri != null) {
request.setRequestURI(requestUri);
}
if (httpMethod != null) {
request.setMethod(httpMethod.name());

View File

@ -52,7 +52,7 @@ import org.springframework.mock.web.MockServletContext;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
import org.springframework.web.context.ConfigurableWebApplicationContext;
import org.springframework.web.context.WebApplicationContext;
@ -206,7 +206,7 @@ class ManagementWebSecurityAutoConfigurationTests {
MockHttpServletResponse response = new MockHttpServletResponse();
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, context);
MockHttpServletRequest request = new MockHttpServletRequest(servletContext);
request.setServletPath(path);
request.setRequestURI(path);
request.setMethod("GET");
filterChainProxy.doFilter(request, response, new MockFilterChain());
return HttpStatus.valueOf(response.getStatus());
@ -216,10 +216,9 @@ class ManagementWebSecurityAutoConfigurationTests {
static class CustomSecurityConfiguration {
@Bean
@SuppressWarnings("removal")
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests((requests) -> {
requests.requestMatchers(new AntPathRequestMatcher("/foo")).permitAll();
requests.requestMatchers(PathPatternRequestMatcher.withDefaults().matcher("/foo")).permitAll();
requests.anyRequest().authenticated();
});
http.formLogin(withDefaults());
@ -246,9 +245,8 @@ class ManagementWebSecurityAutoConfigurationTests {
@Bean
@Order(SecurityProperties.BASIC_AUTH_ORDER - 1)
@SuppressWarnings("removal")
SecurityFilterChain testRemoteDevToolsSecurityFilterChain(HttpSecurity http) throws Exception {
http.securityMatcher(new AntPathRequestMatcher("/**"));
http.securityMatcher(PathPatternRequestMatcher.withDefaults().matcher("/**"));
http.authorizeHttpRequests((requests) -> requests.anyRequest().anonymous());
http.csrf((csrf) -> csrf.disable());
return http.build();

View File

@ -27,6 +27,7 @@ import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.web.util.pattern.PathPatternParser;
import static org.assertj.core.api.Assertions.assertThat;
@ -58,9 +59,11 @@ class SecurityRequestMatchersManagementContextConfigurationTests {
@Test
void registersRequestMatcherProviderIfMvcPresent() {
this.contextRunner.withUserConfiguration(TestMvcConfiguration.class).run((context) -> {
AntPathRequestMatcherProvider matcherProvider = context.getBean(AntPathRequestMatcherProvider.class);
PathPatternRequestMatcherProvider matcherProvider = context
.getBean(PathPatternRequestMatcherProvider.class);
RequestMatcher requestMatcher = matcherProvider.getRequestMatcher("/example", null);
assertThat(requestMatcher).extracting("pattern").isEqualTo("/custom/example");
assertThat(requestMatcher).extracting("pattern")
.isEqualTo(PathPatternParser.defaultInstance.parse("/custom/example"));
});
}
@ -69,29 +72,31 @@ class SecurityRequestMatchersManagementContextConfigurationTests {
this.contextRunner.withClassLoader(new FilteredClassLoader("org.springframework.web.servlet.DispatcherServlet"))
.withUserConfiguration(TestJerseyConfiguration.class)
.run((context) -> {
AntPathRequestMatcherProvider matcherProvider = context.getBean(AntPathRequestMatcherProvider.class);
PathPatternRequestMatcherProvider matcherProvider = context
.getBean(PathPatternRequestMatcherProvider.class);
RequestMatcher requestMatcher = matcherProvider.getRequestMatcher("/example", null);
assertThat(requestMatcher).extracting("pattern").isEqualTo("/admin/example");
assertThat(requestMatcher).extracting("pattern")
.isEqualTo(PathPatternParser.defaultInstance.parse("/admin/example"));
});
}
@Test
void mvcRequestMatcherProviderConditionalOnDispatcherServletClass() {
this.contextRunner.withClassLoader(new FilteredClassLoader("org.springframework.web.servlet.DispatcherServlet"))
.run((context) -> assertThat(context).doesNotHaveBean(AntPathRequestMatcherProvider.class));
.run((context) -> assertThat(context).doesNotHaveBean(PathPatternRequestMatcherProvider.class));
}
@Test
void mvcRequestMatcherProviderConditionalOnDispatcherServletPathBean() {
new WebApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(SecurityRequestMatchersManagementContextConfiguration.class))
.run((context) -> assertThat(context).doesNotHaveBean(AntPathRequestMatcherProvider.class));
.run((context) -> assertThat(context).doesNotHaveBean(PathPatternRequestMatcherProvider.class));
}
@Test
void jerseyRequestMatcherProviderConditionalOnResourceConfigClass() {
this.contextRunner.withClassLoader(new FilteredClassLoader("org.glassfish.jersey.server.ResourceConfig"))
.run((context) -> assertThat(context).doesNotHaveBean(AntPathRequestMatcherProvider.class));
.run((context) -> assertThat(context).doesNotHaveBean(PathPatternRequestMatcherProvider.class));
}
@Test
@ -99,7 +104,7 @@ class SecurityRequestMatchersManagementContextConfigurationTests {
new WebApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(SecurityRequestMatchersManagementContextConfiguration.class))
.withClassLoader(new FilteredClassLoader("org.springframework.web.servlet.DispatcherServlet"))
.run((context) -> assertThat(context).doesNotHaveBean(AntPathRequestMatcherProvider.class));
.run((context) -> assertThat(context).doesNotHaveBean(PathPatternRequestMatcherProvider.class));
}
@Configuration(proxyBeanMethods = false)

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2022 the original author or authors.
* Copyright 2012-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.
@ -24,6 +24,7 @@ import org.springframework.boot.autoconfigure.h2.H2ConsoleProperties;
import org.springframework.boot.autoconfigure.security.StaticResourceLocation;
import org.springframework.boot.security.servlet.ApplicationContextRequestMatcher;
import org.springframework.boot.web.context.WebServerApplicationContext;
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.web.context.WebApplicationContext;
@ -76,10 +77,9 @@ public final class PathRequest {
}
@Override
@SuppressWarnings("removal")
protected void initialized(Supplier<H2ConsoleProperties> h2ConsoleProperties) {
this.delegate = new org.springframework.security.web.util.matcher.AntPathRequestMatcher(
h2ConsoleProperties.get().getPath() + "/**");
this.delegate = PathPatternRequestMatcher.withDefaults()
.matcher(h2ConsoleProperties.get().getPath() + "/**");
}
@Override

View File

@ -28,6 +28,7 @@ import org.springframework.boot.autoconfigure.security.StaticResourceLocation;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath;
import org.springframework.boot.security.servlet.ApplicationContextRequestMatcher;
import org.springframework.boot.web.context.WebServerApplicationContext;
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
@ -134,10 +135,8 @@ public final class StaticResourceRequest {
this.delegate = new OrRequestMatcher(getDelegateMatchers(dispatcherServletPath.get()).toList());
}
@SuppressWarnings("removal")
private Stream<RequestMatcher> getDelegateMatchers(DispatcherServletPath dispatcherServletPath) {
return getPatterns(dispatcherServletPath)
.map(org.springframework.security.web.util.matcher.AntPathRequestMatcher::new);
return getPatterns(dispatcherServletPath).map(PathPatternRequestMatcher.withDefaults()::matcher);
}
private Stream<String> getPatterns(DispatcherServletPath dispatcherServletPath) {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2022 the original author or authors.
* Copyright 2012-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,6 +25,7 @@ import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockServletContext;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.context.WebApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
@ -99,14 +100,14 @@ class PathRequestTests {
MockServletContext servletContext = new MockServletContext();
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
MockHttpServletRequest request = new MockHttpServletRequest(servletContext);
request.setPathInfo(path);
request.setRequestURI(path);
return request;
}
private String getRequestPath(HttpServletRequest request) {
String url = request.getServletPath();
if (request.getPathInfo() != null) {
url += request.getPathInfo();
if (StringUtils.hasText(request.getRequestURI())) {
url += request.getRequestURI();
}
return url;
}

View File

@ -25,6 +25,7 @@ import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockServletContext;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.context.WebApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
@ -156,15 +157,18 @@ class StaticResourceRequestTests {
MockHttpServletRequest request = new MockHttpServletRequest(servletContext);
if (servletPath != null) {
request.setServletPath(servletPath);
request.setRequestURI(servletPath + path);
}
else {
request.setRequestURI(path);
}
request.setPathInfo(path);
return request;
}
private String getRequestPath(HttpServletRequest request) {
String url = request.getServletPath();
if (request.getPathInfo() != null) {
url += request.getPathInfo();
if (StringUtils.hasText(request.getRequestURI())) {
url += request.getRequestURI();
}
return url;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2024 the original author or authors.
* Copyright 2012-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,6 +25,7 @@ import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
/**
* Spring Security configuration that allows anonymous access to the remote devtools
@ -45,10 +46,9 @@ class RemoteDevtoolsSecurityConfiguration {
}
@Bean
@SuppressWarnings("removal")
@Order(SecurityProperties.BASIC_AUTH_ORDER - 1)
SecurityFilterChain devtoolsSecurityFilterChain(HttpSecurity http) throws Exception {
http.securityMatcher(new org.springframework.security.web.util.matcher.AntPathRequestMatcher(this.url));
http.securityMatcher(PathPatternRequestMatcher.withDefaults().matcher(this.url));
http.authorizeHttpRequests((requests) -> requests.anyRequest().anonymous());
http.csrf(CsrfConfigurer::disable);
return http.build();

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-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.
@ -31,7 +31,7 @@ import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
import static org.springframework.security.config.Customizer.withDefaults;
@ -68,9 +68,9 @@ public class SecurityConfiguration {
requests.requestMatchers(EndpointRequest.toAnyEndpoint().excluding(MappingsEndpoint.class))
.hasRole("ACTUATOR");
requests.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll();
requests.requestMatchers(new AntPathRequestMatcher("/foo")).permitAll();
requests.requestMatchers(PathPatternRequestMatcher.withDefaults().matcher("/foo")).permitAll();
requests.requestMatchers(new MvcRequestMatcher(handlerMappingIntrospector, "/error")).permitAll();
requests.requestMatchers(new AntPathRequestMatcher("/**")).hasRole("USER");
requests.requestMatchers(PathPatternRequestMatcher.withDefaults().matcher("/**")).hasRole("USER");
});
http.cors(withDefaults());
http.httpBasic(withDefaults());