I'm trying to upgrade to spring boot 3 and spring security 6.
I'm running into an issue where previously, mvcMatchers
would match against these URLs:
String[] companiesEndpoints = {"/companies", "/companies/*"};
String[] ideationEndpoint = {"/ideation", "/ideation/*"};
String[] assessmentsEndpoints = {"/assessment", "/assessment/*", "/assessment/*/value-rating", "/assessment/*/viability-rating", "/assessment/*/customer-rating"};
String[] teamsEndpoints = {"/teams", "/teams/*"};
String[] userEndpoints = {"/users", "/users/*"};
String[] projectEndpoints = {"/project", "/project/*", "/project-and-assessment"};
String[] workspaceEndpoints = {"/workspace", "/workspace/*"};
String[] tagEndpoints = {"/tag", "/tag/*"};
As part of the move to spring security 6, mvcMatchers
are now replaced with requestMatchers
- I thought I could drop in the new matches and things would keep working, but now the listed URLs before only allow some of the requests. Here are some examples:
.mvcMatchers(assessmentsEndpoints).authenticated()
use to match
"/assessment/2900b695-d344-4bec-b25d-524f6b22a93a/customer-rating"
.
.requestMatchers(assessmentsEndpoints).authenticated()
does not match so the API returns a 403.
This makes me think that requestMatcher
is not a drop in replacement for mvcMatcher
, but I'm not sure how I should be structuring this to make requestMatcher
allow these requests.
I have many requests with path parameters like "/assessment/{assessmentId}/value-rating*"
. How should I structure my String[]
endpoints to allow such URLs?
For reference, here is the full SecurityConfig
class that contains the relevant code.
@Configuration
public class SecurityConfig {
@Value(value = "${auth0.audience}")
private String apiAudience;
@Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}")
private String issuer;
@Bean
ForwardedHeaderFilter forwardedHeaderFilter() {
return new ForwardedHeaderFilter();
}
@Bean
JwtDecoder jwtDecoder() {
NimbusJwtDecoder jwtDecoder = JwtDecoders.fromOidcIssuerLocation(issuer);
OAuth2TokenValidator<Jwt> audienceValidator = new AudienceValidator(apiAudience);
OAuth2TokenValidator<Jwt> withIssuer = JwtValidators.createDefaultWithIssuer(issuer);
OAuth2TokenValidator<Jwt> withAudience = new DelegatingOAuth2TokenValidator<>(withIssuer, audienceValidator);
jwtDecoder.setJwtValidator(withAudience);
return jwtDecoder;
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList(
"http://localhost:4200"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "PATCH", "DELETE"));
configuration.setAllowCredentials(true);
configuration.setAllowedHeaders(Arrays.asList(
"x-requested-with",
"content-type",
"Accept",
"Authorization",
"sentry-trace",
"baggage"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
String[] companiesEndpoints = {"/companies", "/companies/*"};
String[] ideationEndpoint = {"/ideation", "/ideation/*"};
String[] assessmentsEndpoints = {"/assessment", "/assessment/*", "/assessment/*/value-rating", "/assessment/*/viability-rating", "/assessment/*/customer-rating"};
String[] teamsEndpoints = {"/teams", "/teams/*"};
String[] userEndpoints = {"/users", "/users/*"};
String[] projectEndpoints = {"/project", "/project/*", "/project-and-assessment"};
String[] workspaceEndpoints = {"/workspace", "/workspace/*"};
String[] tagEndpoints = {"/tag", "/tag/*"};
http.authorizeHttpRequests((authorize) -> {
try {
authorize
.requestMatchers(companiesEndpoints).authenticated()
.requestMatchers(ideationEndpoint).authenticated()
.requestMatchers(assessmentsEndpoints).authenticated()
.requestMatchers(teamsEndpoints).authenticated()
.requestMatchers(userEndpoints).authenticated()
.requestMatchers(projectEndpoints).authenticated()
.requestMatchers(workspaceEndpoints).authenticated()
.requestMatchers(tagEndpoints).authenticated()
.requestMatchers(EndpointRequest.to("info")).hasAuthority("SCOPE_read:status")
.requestMatchers(EndpointRequest.to("health")).permitAll()
.and()
.oauth2ResourceServer((oauth2ResourceServer) ->
oauth2ResourceServer.jwt(jwt -> jwt.decoder(jwtDecoder())));
} catch (Exception e) {
throw new RuntimeException(e);
}
});
// Disable X-Frames on same origin to enable access to H2 in memory db console
// https://stackoverflow.com/questions/26220083/h2-database-console-spring-boot-load-denied-by-x-frame-options
http.headers().frameOptions().sameOrigin();
return http.build();
}
}