Currently there is not way enforce the Principle of least privilege with respect to Handler Mappings and method security, e.g. PreAuthorize.
Some frameworks built on top of Spring Security accomplish this by providing a AnnotationFilterInvocationDefinition to the FilterSecurityInterceptor that checks both the request mappings and method level annotations.
This approach, however, will no longer be possible in Spring Security 7 due to the fact that the FilterSecurityInterceptor is set for removal in favor of the AuthorizationFilter.
While it is possible to create an instance of the AuthorizationFilter with its own AuthorizationManager, it is impossible to utilize the convenient creation of a RequestMatcherDelegatingAuthorizationManager.
A very simple solution for achieving the Principle of least privilege while honoring method level overrides, would be to instantiate a AuthorizationFilter with a wrapped RequestMatcherDelegatingAuthorizationManager that first checks the authorization level of request mapping and then does the method level check annotation check as an override. This is not currently possible because RequestMatcherDelegatingAuthorizationManager can not be built independently to the AuthorizationFilter, nor can it be customized to perform further checks.
Making it extensible or allowing it to be built outside of the AuthorizationFilter would enable the new approach to be accomplished and promote code reuse. The goal here is to utilize the RequestMatcherDelegatingAuthorizationManager when creating a custom approach to securing requests that involves method level security.
Comment From: codeconsole
https://github.com/spring-projects/spring-security/issues/14371
Comment From: jzheaux
Related to #13057
Comment From: aslary
@jzheaux @codeconsole I asked a question yesterday on StackOverflow. How can I implement your solution in my SecurityConfig? I am using following security config for self signed JWTs (I got this from Dan Vega's tutorial to prevent having to write my own OncePerRequestFilter):
@Configuration
@EnableWebSecurity
@EnableMethodSecurity(
securedEnabled = true,
prePostEnabled = true,
jsr250Enabled = true,
) // This enables `Secured`, `PreAuthorize`, `PostAuthorize`, `RolesAllowed`, `PermitAll` and similar annotations.
class SecurityConfig {
/**
* To get a deeper understanding on how Spring Security automatically validates the token
* (e.g. signature, expiry, roles, etc.), have a look at [BearerTokenAuthenticationFilter][org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter].
* If Spring Security throws an error (authentication or authorization), [BearerTokenAuthenticationEntryPoint] and [BearerTokenAccessDeniedHandler]
* get called respectively by the security chain.
*/
@Bean
fun securityFilterChain(http: HttpSecurity): SecurityFilterChain =
http
.csrf { it.disable() }
.authorizeHttpRequests {
it
.requestMatchers(HttpMethod.POST, "/auth/register", "/auth/login")
.permitAll()
.anyRequest()
.denyAll() // Deny per default, override in controllers... NOT WORKING :'(
}.sessionManagement { it.sessionCreationPolicy(SessionCreationPolicy.STATELESS) }
.oauth2ResourceServer { it.jwt { jwt -> jwt.decoder(jwtDecoder()) } }
.exceptionHandling {
it
.authenticationEntryPoint(BearerTokenAuthenticationEntryPoint())
.accessDeniedHandler(BearerTokenAccessDeniedHandler())
}.build()
@Bean
fun jwtDecoder(): JwtDecoder {
val bytes = jwtKey.toByteArray()
val originalKey = SecretKeySpec(bytes, 0, bytes.size, "RSA")
return NimbusJwtDecoder.withSecretKey(originalKey).macAlgorithm(MacAlgorithm.HS512).build()
}
}
Currently, I cannot override the default denyAll behaviour in my controllers. None of the Security annotations, such as Secured, RolesAllowed, PermitAll or Pre/PostAuthorize work in this case.
How does this commit help with this?