Я создаю SPA с Keycloak 5, Spring Boot 2 и Angular 7.
Все было хорошо, даже настройка keycloack в application.properties и защита ролей. Но когда я попытался создать компонент для получения данных токена пользователя, я получил нулевой компонент. Не могу понять почему, это похоже на код в документации Keycloak...
import javax.servlet.http.HttpServletRequest;
import org.keycloak.KeycloakSecurityContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
@Configuration
public class KeycloakConfig {
/**
* Retorna o contexto de segurança do Keycloak.
*
* @return
*/
@Bean
@Scope(scopeName = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public KeycloakSecurityContext accessToken() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
return (KeycloakSecurityContext) request.getAttribute(KeycloakSecurityContext.class.getName());
}
}
Конфигурация загрузки:
keycloak.enabled = true
keycloak.auth-server-url = http://acesso.tre-pa.jus.br/auth
keycloak.realm = TRE-PA
keycloak.resource = acesso-sistemas-service
keycloak.credentials.secret = ca70294a-af51-4abb-81f9-234566de2c7c
keycloak.ssl-required = external
keycloak.use-resource-role-mappings = false
keycloak.bearer-only = true
keycloak.autodetect-bearer-only = true
keycloak.principal-attribute = preferred_username
logging.level.org.keycloak = DEBUG
spring.main.allow-bean-definition-overriding = true
# spring.autoconfigure.exclude = org.keycloak.adapters.springboot.KeycloakSpringBootConfiguration
keycloak.securityConstraints[0].securityCollections[0].name = secured-area
keycloak.securityConstraints[0].securityCollections[0].patterns[0] = /secured/*
keycloak.securityConstraints[1].securityCollections[0].patterns[1] = /admin/*
keycloak.securityConstraints[1].authRoles[0] = DEVELOPER
keycloak.securityConstraints[1].securityCollections[0].name = service-area
keycloak.securityConstraints[1].securityCollections[0].patterns[0] = /service/*
Возможно, этот способ больше не действует, так как это другая версия.
@Please_Dont_Bully_Me_SO_Lords У меня такая же проблема. Если вы нашли какое-либо решение, пожалуйста, сообщите мне. Также не могли бы вы сказать мне, где вы нашли документацию по keycloak-spring-boot-starter?




Я сделал что-то подобное в своем приложении, используя Keycloak 4.8.1.Final, где я мог получить доступ к AccessToken и выставить его как bean-компонент для внедрения в другие классы:
@Bean
@Scope(scopeName = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public AccessToken getAccessToken() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
KeycloakPrincipal<RefreshableKeycloakSecurityContext> keycloakPrincipal = (KeycloakPrincipal) request.getUserPrincipal();
if (keycloakPrincipal == null) {
throw new NotAuthenticatedException("KeycloakPrincipal not set");
}
KeycloakSecurityContext keycloakSecurityContext = keycloakPrincipal.getKeycloakSecurityContext();
return keycloakSecurityContext.getToken();
}
После обновления Keycloak до 5 (и, прежде всего, до 15.0.1) приведенный выше код больше не работает. Единственный способ, которым мне удалось получить ссылку на AccessToken, - это использовать Spring Security, как описано (в основном) здесь
В основном добавление весенних депов безопасности в gradle
implementation('org.springframework.boot:spring-boot-starter-security')
Добавление этого класса конфигурации...
import org.keycloak.adapters.KeycloakConfigResolver
import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver
import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper
import org.springframework.security.core.session.SessionRegistryImpl
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy
import javax.inject.Inject
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(jsr250Enabled = true)
class KeycloakConfig : KeycloakWebSecurityConfigurerAdapter() {
override fun configure(http: HttpSecurity) {
super.configure(http)
http.authorizeRequests()
.anyRequest()
.permitAll()
http.csrf().disable()
}
@Inject
fun configureGlobal(authManager: AuthenticationManagerBuilder) {
val authProvider = keycloakAuthenticationProvider()
authProvider.setGrantedAuthoritiesMapper(SimpleAuthorityMapper())
authManager.authenticationProvider(authProvider)
}
@Bean
override fun sessionAuthenticationStrategy(): SessionAuthenticationStrategy {
return RegisterSessionAuthenticationStrategy(SessionRegistryImpl())
}
}
А затем обновить мой метод в моем другом классе конфигурации:
import org.springframework.amqp.rabbit.annotation.EnableRabbit
import org.springframework.cache.annotation.EnableCaching
import org.springframework.context.annotation.EnableAspectJAutoProxy
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder
import com.fasterxml.jackson.module.kotlin.KotlinModule
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.ObjectMapper
import org.springframework.web.context.WebApplicationContext
import org.springframework.context.annotation.ScopedProxyMode
import org.keycloak.representations.AccessToken
import javax.servlet.http.HttpServletRequest
import org.springframework.web.context.request.RequestContextHolder
import org.springframework.web.context.request.ServletRequestAttributes
import org.keycloak.KeycloakPrincipal
import org.keycloak.adapters.RefreshableKeycloakSecurityContext
import org.keycloak.KeycloakSecurityContext
import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Scope
import javax.inject.Inject
import javax.ws.rs.NotAuthorizedException
import javax.ws.rs.core.Context
import javax.ws.rs.core.SecurityContext
@Configuration
class ApplicationConfig {
//needed for keycloak adaptor 7.0.1+
// added in this here rather than in KeycloakConfig above due to issue here:
// https://stackoverflow.com/questions/57957006/unable-to-build-spring-based-project-for-authentication-using-keycloak
@Bean
fun keycloakConfigResolver(): KeycloakSpringBootConfigResolver {
return KeycloakSpringBootConfigResolver()
}
@Bean
@Scope(scopeName = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
fun accessToken(): AccessToken {
val request = (RequestContextHolder.currentRequestAttributes() as ServletRequestAttributes).request
val token = request.userPrincipal as KeycloakAuthenticationToken? ?: throw NotAuthorizedException("KeycloakPrincipal not set")
val principal = token.principal as KeycloakPrincipal<*>
val keycloakSecurityContext = principal.keycloakSecurityContext
return keycloakSecurityContext.token
}
}
Приведенные выше коды работают для меня с версиями Springboot 2.1.2.RELEASE и 2.5.3
Можете ли вы дать ссылку на часть документации keycloak, в которой объясняется, как это сделать? По сути, вы говорите, что вам нужен атрибут в текущем HTTP-запросе с именем
org.keycloak.KeycloakSecurityContext, который, я думаю, не существует. Вы должны получить это из сеанса IMO..