Issue
I am trying to use an expression-based check for an user ID path variable, so users can only access resources that belong to them. It is pretty clearly described in the rel="nofollow noreferrer">Spring documentation. But I cannot access the bean, with the error that a String
is provided.
This is my security filter chain and the bean:
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.cors()
.and()
.csrf().disable()
.authorizeHttpRequests()
.antMatchers(WHITELIST_URLS).permitAll()
.antMatchers("/api/**").authenticated()
.antMatchers("/api/**/users/{userId}/**").access("@userSecurity.checkUserId(authentication,#userId)")
.and()
.oauth2Login(oauth2login ->
oauth2login.loginPage("/oauth2/authorization/api-client-oidc"))
.oauth2Client(Customizer.withDefaults())
.build();
}
public static class UserSecurityConfig extends WebSecurityConfiguration {
@Bean("userSecurity")
private boolean checkUserId(Authentication authentication, String userId) {
return authentication.getPrincipal().equals(userId);
}
}
Error:
Required type: AuthorizationManager
<org.springframework.security.web.access.intercept.RequestAuthorizationContext>
Provided: String
I also have been trying to use an AuthorizationDecision
(as lambda expression) but could not access the path variable.
Is the spring documentation wrong on this one? Been searching for quiet a while, but mostly found the same thing as in the Spring documentation.
Actually, I would like to manage this globally in the config and not on each mapping in the controllers by using the @PreAuthorize
annotation.
Edit:
I have been unsuccessffuly trying to solve this using something like:
.access((authentication, object) ->
new AuthorizationDecision(object.getRequest().getServletPath().contains(
authentication.get().getName())))
or
.access((authentication, object) ->
new AuthorizationDecision(authentication.get().getPrincipal().equals(
object.getVariables().get("#userId"))))
Solution
I figured it out, the following example works. The more specific matcher has to be called first, otherwise it will not work.
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.cors()
.and()
.csrf()
.disable()
.authorizeHttpRequests()
.antMatchers("/api/v1/users/{userId}/**")
.access((authentication, object) -> new AuthorizationDecision(
object.getRequest().getServletPath().contains(
authentication.get().getName())
))
.antMatchers(WHITELIST_URLS)
.permitAll()
.antMatchers("/api/v1/**")
.authenticated()
.and()
.oauth2Login(oauth2login ->
oauth2login.loginPage("/oauth2/authorization/api-client-oidc"))
.oauth2Client(Customizer.withDefaults())
.build();
}
Answered By - s4timuen
Answer Checked By - Katrina (JavaFixing Volunteer)