I want to give access based on the realm roles. I am using Keycloak and Spring Boot 3 (Spring Security 6).
I tried hasRole()/hasAnyRole/hasAuthority/hasAnyAuthority with the desired role name. The letter case is the same.
By default use-resource-role-mappings
is false("If false, it will look at the realm level for user role mappings").
I'd like to set this in java configuration file, as I would find it very time consuming to add on each controller @PreAuthorize
annotation.
I keep getting 403 as a response. What am I doing wrong?
The method convert(Jwt source)
from the KeycloakGrantedAuthoritiesConverter
is not even called. I put a breakpoint and it never gets there.
Here is my code:
@Configuration
@EnableWebSecurity
@Order(Ordered.HIGHEST_PRECEDENCE)
public class SecurityConfig {
@Value("${portal.required.role.name}")
private String portalRoleName;
@Value("${server.servlet.context-path}")
private String servletContextPath;
@Bean
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http, KeycloakLogoutHandler keycloakLogoutHandler) throws Exception {
CookieCsrfTokenRepository tokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse();
XorCsrfTokenRequestAttributeHandler delegate = new XorCsrfTokenRequestAttributeHandler();
delegate.setCsrfRequestAttributeName("_csrf");
CsrfTokenRequestHandler requestHandler = delegate::handle;
http.authorizeHttpRequests(auth -> {
auth.requestMatchers("/fristpath/**", "/secondpath/**").permitAll();
auth.requestMatchers("/thirdpath/**").hasAnyRole(portalRoleName);//obw
auth.anyRequest().authenticated();
});
http.oauth2ResourceServer(oauth2 -> oauth2.jwt());
http.oauth2ResourceServer()
.jwt()
.jwtAuthenticationConverter(jwtAuthenticationConverter());
http.oauth2Login()
.and()
.logout()
.addLogoutHandler(keycloakLogoutHandler)
.logoutSuccessUrl("/");
http.csrf(csrf -> csrf
.csrfTokenRepository(tokenRepository)
.csrfTokenRequestHandler(requestHandler));
return http.build();
}
private JwtAuthenticationConverter jwtAuthenticationConverter() {
JwtAuthenticationConverter jwtConverter = new JwtAuthenticationConverter();
jwtConverter.setJwtGrantedAuthoritiesConverter(new KeycloakGrantedAuthoritiesConverter());
return jwtConverter;
}
}
public class KeycloakGrantedAuthoritiesConverter implements Converter<Jwt, Collection<GrantedAuthority>> {
@Override
public Collection<GrantedAuthority> convert(Jwt source) {
Map<String, Object> realmAccess = source.getClaimAsMap("realm_access");
List<String> roles = (List<String>) realmAccess.get("roles");
return roles.stream()
.map(rn -> new SimpleGrantedAuthority("ROLE_" rn))
.collect(Collectors.toList());
}
}
The same with ROLE_obw...
And this is part of the access token:
CodePudding user response:
By default there is only scope based mapping provided by Spring security for handling access via JWT token. For Authorities/Roles need a custom mapping solution to extract from JWT token and put into security context.
There is a nice article on same which explains this.
CodePudding user response:
Login (and logout) are handled by OAuth2 clients, not resource-servers (REST APIs). Requests to resource-server protected resources should have an access-token.
Remove login and logout conf from your resource-server security filter-chain (you might also make it session-less and disable CSRF) and send requests with Bearer
access-token in Authorization
header.
If your app also serves server-side rendered UI, define a second SecurityFilterChain
bean for client configuration (with login, logout, sessions and CSRF protection enabled).
Details in my answer to this other question: Use Keycloak Spring Adapter with Spring Boot 3