【问题标题】:KeycloakSecurityContext is returning null in SpringBootKeycloakSecurityContext 在 SpringBoot 中返回 null
【发布时间】:2019-03-27 19:13:41
【问题描述】:

我正在使用 Keycloak 5、Spring Boot 2 和 Angular 7 创建一个 SPA。

一切都很好,甚至application.properties 中的 keycloack 配置和角色保护。但是当我尝试创建一个 Bean 来获取用户令牌数据时,我收到了一个空 bean。不明白为什么,和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/*

【问题讨论】:

  • 你能链接到keycloak docs部分,它解释了这样做吗?您基本上是在告诉您在当前 HTTP 请求中需要一个名为 org.keycloak.KeycloakSecurityContext 的属性,我认为它不存在。您应该从会话 IMO 中检索它。
  • 也许这种方式不再有效,因为它是不同的版本。
  • @Please_Dont_Bully_Me_SO_Lords 我遇到了同样的问题。如果您找到任何解决方案,请告诉我。另外你能告诉我你在哪里找到了 keycloak-spring-boot-starter 的文档吗?

标签: java spring-boot keycloak


【解决方案1】:

我在我的应用程序中使用 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 描述(主要)here

基本上将spring security deps添加到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.RELEASE2.5.3

【讨论】:

    猜你喜欢
    • 2020-01-23
    • 2017-05-24
    • 1970-01-01
    • 2021-05-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-07
    • 2011-12-30
    相关资源
    最近更新 更多