com.epam.eco.kafkamanager.ui.config.KeycloakBaseWebSecurityConfig Maven / Gradle / Ivy
/*******************************************************************************
* Copyright 2023 EPAM Systems
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*******************************************************************************/
package com.epam.eco.kafkamanager.ui.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
import org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority;
import org.springframework.security.oauth2.core.user.OAuth2UserAuthority;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
import java.util.*;
import java.util.stream.Collectors;
import static java.util.Objects.isNull;
import static java.util.Objects.nonNull;
/**
* @author Mikhail_Vershkov
*/
@Profile(WebSecurityProfiles.KEYCLOAK)
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
@Order(2)
public class KeycloakBaseWebSecurityConfig extends BaseWebSecurityConfig {
private static final String RESOURCE_ACCESS_CLAIM = "resource_access";
private static final String ROLES_CLAIM = "roles";
@Value("${spring.security.oauth2.client.provider.keycloak.logout-url}")
private String logoutUrl;
@Autowired
private OAuth2ClientProperties oAuth2ClientProperties;
@Autowired
private AuthenticationLogFilter authenticationLogFilter;
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return commonHttpConfig(http)
.oauth2Login(login -> login.userInfoEndpoint(config -> config.userAuthoritiesMapper(userAuthoritiesMapperForKeycloak())))
.addFilterAfter(authenticationLogFilter, AnonymousAuthenticationFilter.class)
.logout(logout -> logout
.logoutUrl("/logout")
.logoutSuccessUrl(logoutUrl)
.permitAll())
.build();
}
@Bean
@SuppressWarnings("unchecked")
public GrantedAuthoritiesMapper userAuthoritiesMapperForKeycloak() {
return authorities -> {
Set mappedAuthorities = new HashSet<>();
var authority = authorities.iterator().next();
boolean isOidc = authority instanceof OidcUserAuthority;
if (isOidc) {
var oidcUserAuthority = (OidcUserAuthority) authority;
if (nonNull(oidcUserAuthority.getUserInfo()) &&
nonNull(oidcUserAuthority.getUserInfo().getClaims()) &&
oidcUserAuthority.getUserInfo().getClaims().containsKey(RESOURCE_ACCESS_CLAIM)) {
Map resourceAccess = (Map) oidcUserAuthority.getUserInfo().getClaims().get(RESOURCE_ACCESS_CLAIM);
addExtractedRoles(resourceAccess, mappedAuthorities);
} else if (oidcUserAuthority.getAttributes().containsKey(RESOURCE_ACCESS_CLAIM)) {
Map resourceAccess = (Map) oidcUserAuthority.getAttributes().get(RESOURCE_ACCESS_CLAIM);
addExtractedRoles(resourceAccess, mappedAuthorities);
}
} else {
var oauth2UserAuthority = (OAuth2UserAuthority) authority;
Map userAttributes = oauth2UserAuthority.getAttributes();
if (userAttributes.containsKey(RESOURCE_ACCESS_CLAIM)) {
var realmAccess = (Map) userAttributes.get(RESOURCE_ACCESS_CLAIM);
var roles = (Collection) realmAccess.get(ROLES_CLAIM);
mappedAuthorities.addAll(generateAuthoritiesFromClaim(roles));
}
}
return mappedAuthorities;
};
}
private void addExtractedRoles(Map resourceAccess,
Set mappedAuthorities) {
Optional clientId = oAuth2ClientProperties.getRegistration().values().stream()
.map(OAuth2ClientProperties.Registration::getClientId)
.findFirst();
if(clientId.isEmpty()) {
return;
}
Map clientResources = (Map)resourceAccess.get(clientId.get());
if(isNull(clientResources) || clientResources.isEmpty() || !clientResources.containsKey(ROLES_CLAIM)) {
return;
}
Collection roles = (Collection)clientResources.get(ROLES_CLAIM);
if(isNull(roles) || roles.isEmpty()) {
return;
}
mappedAuthorities.addAll(generateAuthoritiesFromClaim(roles));
}
Collection generateAuthoritiesFromClaim(Collection roles) {
return roles.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());
}
}