Home > Back-end >  Access based on REALM ROLES not working, Spring Security 6 & Keycloak
Access based on REALM ROLES not working, Spring Security 6 & Keycloak

Time:01-18

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());
    }
}

enter image description here

The logged in user's roles The logged in user's roles

The same with ROLE_obw...

And this is part of the access token: enter image description here

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.

Map Authorities from JWT

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

  • Related