带有 Spring Boot KeycloakSecurityContext 的 Keycloak 始终为空

Posted

技术标签:

【中文标题】带有 Spring Boot KeycloakSecurityContext 的 Keycloak 始终为空【英文标题】:Keycloak with Spring Boot KeycloakSecurityContext always null 【发布时间】:2021-01-18 04:19:36 【问题描述】:

我尝试将 Keycloak 与 Spring Boot 一起使用,但我遇到了问题。身份验证适用于适配器,但不适用于授权。

这是我的配置:

keycloak.realm = master
keycloak.auth-server-url = http://127.0.0.1:8080/auth
keycloak.ssl-required = none
keycloak.resource = pactng
keycloak.credentials.secret = **************************
keycloak.use-resource-role-mappings = true
keycloak.principal-attribute=preferred_username
keycloak.bearer-only = true

keycloak.policy-enforcer-config.userManagedAccess=org.keycloak.representations.adapters.config.PolicyEnforcerConfig.UmaProtocolConfig
keycloak.policy-enforcer-config.lazyLoadPaths=true

keycloak.policy-enforcer-config.paths[0].path=/*
keycloak.policy-enforcer-config.paths[0].methods[0].method=GET
keycloak.policy-enforcer-config.paths[0].methods[0].scopes[0]=urn:pactng:scopes:read
keycloak.policy-enforcer-config.paths[0].methods[1].method=POST
keycloak.policy-enforcer-config.paths[0].methods[1].scopes[0]=urn:pactng:scopes:create
keycloak.policy-enforcer-config.paths[0].methods[2].method=PUT
keycloak.policy-enforcer-config.paths[0].methods[2].scopes[0]=urn:pactng:scopes:update
keycloak.policy-enforcer-config.paths[0].methods[3].method=PATCH
keycloak.policy-enforcer-config.paths[0].methods[3].scopes[0]=urn:pactng:scopes:update
keycloak.policy-enforcer-config.paths[0].methods[4].method=DELETE
keycloak.policy-enforcer-config.paths[0].methods[4].scopes[0]=urn:pactng:scopes:delete

我没有在这里定义角色,我希望适配器根据访问令牌中包含的请求和权限动态计算授权。

这是我用于 Spring Security 的 KeycloakConfig:

@KeycloakConfiguration
public class KeycloakConfig extends KeycloakWebSecurityConfigurerAdapter 

    /**
     * Map keycloak role to spring ROLE_<ROLE>
     * @param auth
     */
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) 
        KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
        keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
        auth.authenticationProvider(keycloakAuthenticationProvider);
    

    /**
     * Defined the strategy use by keycloak for user session. We need authorization system so we use a session for a confidential client.
     * Can be NullAuthenticatedSessionStrategy for bearerClient
     * @return
     */
    @Bean
    @Override
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() 
        return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
    

    /**
     * Use spring boot application.properties instead of keycloak.json to retrieve
     * connexion informations
     */
    @Bean
    public KeycloakSpringBootConfigResolver KeycloakConfigResolver() 
        return new KeycloakSpringBootConfigResolver();
    


    @Override
    protected void configure(HttpSecurity http) throws Exception 
        super.configure(http);
        http.csrf().disable()
                .authorizeRequests()
                .antMatchers("/api/*").authenticated()
                .anyRequest().permitAll();
    

我通过以下请求检索令牌进行测试。 UMA 2 已激活,因为如果没有此适配器,似乎无法启用授权。

# Get Access Token for a user.
POST http://127.0.0.1:8080/auth/realms/master/protocol/openid-connect/token

    client_id   "<CLIENT_ID>"
    client_secret   "<CLIENT_SECRET>"
    username    "<USERNAME>"
    password    "<USER_PASSWORD>"
    grant_type  "password"

这给了我一个访问令牌,我在以下请求中使用它。

# Try to get access to the resource endpoint
GET http://localhost:8081/api/v1/<resources>
Headers: authorization: Bearer <ACCESS_TOKEN>

这给了我一个 401 WWW-Authenticate 遵循 UMA2 规范和一张票。然后我联系 keycloak 以获得 RPT:

# Get RPT from Keycloak
POST http://127.0.0.1:8080/auth/realms/master/protocol/openid-connect/token
Headers: authorization: Bearer <ACCESS_TOKEN>

    grant_type: "urn:ietf:params:oauth:grant-type:uma-ticket"
    ticket: <TICKET>

Keycloak 给我一个 RPT,它是一个访问令牌,其中包含权限。然后我重试获取资源,但使用此 RPT

# Try to get access to the resource endpoint
GET http://localhost:8081/api/v1/<resources>
Headers: authorization: Bearer <RPT>

但是适配器没有访问资源,而是再次给我一个 401 WWW-Authenticate。

我深入研究了代码,发现 KeycloakSecurityContext 在 AbstractPolicyEnforcer 中始终为空:

   public AuthorizationContext authorize(OIDCHttpFacade httpFacade) 
        EnforcementMode enforcementMode = getEnforcerConfig().getEnforcementMode();
        // Always return null
        KeycloakSecurityContext securityContext = httpFacade.getSecurityContext();

这使得代码每次都被困在这部分代码中:

if (securityContext == null) 
     if (!isDefaultAccessDeniedUri(request)) 
          if (pathConfig != null) 
               if (EnforcementMode.DISABLED.equals(pathConfig.getEnforcementMode())) 
                   return createEmptyAuthorizationContext(true);
                else 
                   challenge(pathConfig, getRequiredScopes(pathConfig, request), httpFacade);
               
            else 
                    handleAccessDenied(httpFacade);
           
      
      return createEmptyAuthorizationContext(false);
 

所以我的问题是,经过这么长的解释,为什么这个 securityContext 没有被填充?我错过了什么吗?

【问题讨论】:

【参考方案1】:

好的,我找到了解决方案。似乎这个适配器想要一个角色上的 securityContraints 来完成它的工作。所以我添加它以使其工作:

keycloak.securityConstraints[0].authRoles[0]=*
keycloak.securityConstraints[0].securityCollections[0].patterns[0]=/*

这基本上告诉它处理所有路径上的所有角色。

P.S 继续使用 JAVA 11,因为它与最新的 JDK 版本不兼容。

【讨论】:

以上是关于带有 Spring Boot KeycloakSecurityContext 的 Keycloak 始终为空的主要内容,如果未能解决你的问题,请参考以下文章

带有 spring-boot 和 spring-security 的 JWT

带有 MVC 的 Spring Boot SOAP Web 服务

带有 Spring Boot 的 Spring Restful 服务 - NoSuchBeanDefinitionException

带有spring-boot的自定义sql中的Liquibase参数

java 带有Spring Boot 1.5的JPA

带有 Spring Boot 中所需的显式 basePackages 的 IntegrationComponentScan?