简体   繁体   English

KeycloakSecurityContext 在 SpringBoot 中返回 null

[英]KeycloakSecurityContext is returning null in SpringBoot

I am creating an SPA with Keycloak 5, Spring Boot 2 and Angular 7.我正在使用 Keycloak 5、Spring Boot 2 和 Angular 7 创建一个 SPA。

Everything was fine, even keycloack configuration in application.properties and roles securing.一切都很好,甚至application.properties和角色保护中的 keycloack 配置。 But when I tried to create a Bean to get the User Token Data, I am receiving a null bean of it.但是当我尝试创建一个 Bean 来获取用户令牌数据时,我收到了它的一个空 bean。 Can't understand why, it is just like the code in Keycloak documentation...不明白为什么,就像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());
    }
}

Boot Config:启动配置:

    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/*

I did something similar in my application using Keycloak 4.8.1.Final where I could access the AccessToken and expose it as a bean for injection into other classes:我在我的应用程序中使用 Keycloak 4.8.1.Final做了类似的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();

 }

After upgrading Keycloak to 5 (and above all the way to 15.0.1) the code above no longer works.将 Keycloak 升级到 5(最重要的是升级到 15.0.1)后,上面的代码不再有效。 The only way I managed to get a reference to the AccessToken was by using Spring Security as described (mostly) here我设法获得对 AccessToken 的引用的唯一方法是使用 Spring Security,如(主要) here

Basically adding spring security deps to gradle基本上将 spring security deps 添加到 gradle

implementation('org.springframework.boot:spring-boot-starter-security')

Adding this config class...添加此配置类...

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

}

And then updating my method in my other config class:然后在我的其他配置类中更新我的方法:

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
    }
}

The above codes works for me with Springboot versions 2.1.2.RELEASE and 2.5.3以上代码适用于 Springboot 版本2.1.2.RELEASE2.5.3

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM