Spring Boot 和 Keycloak - GrantedAuthority 总是空着回来

Posted

技术标签:

【中文标题】Spring Boot 和 Keycloak - GrantedAuthority 总是空着回来【英文标题】:Spring Boot and Keycloak - GrantedAuthority always coming back empty 【发布时间】:2021-12-02 07:44:22 【问题描述】:

我已经设置了一个 REST API,我正在尝试使用 Keycloak 进行保护,并在本地 Docker 组合中运行所有内容。 REST API 在主机/服务rest-api 上运行,Keycloak 在auth-service 上运行,并且两者前面都有一个代理容器(因此以 /auth 开头的任何内容都进入 Keycloak,其他任何内容都进入 API)。

安全配置 - 我已经尝试了一些东西,但目前它是:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(jsr250Enabled = true)
public class SecurityConfiguration
        extends KeycloakWebSecurityConfigurerAdapter 
    @Override
    protected void configure(final HttpSecurity http) throws Exception 
        super.configure(http);
        http.authorizeRequests()
                .anyRequest()
                .permitAll();
        http.csrf().disable();
        http.cors();
    

    @Bean
    CorsConfigurationSource corsConfigurationSource() 
        UrlBasedCorsConfigurationSource source =
                new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration(
                "/**",
                new CorsConfiguration().applyPermitDefaultValues()
        );
        return source;
    

    /**
     * Global configuration.
     *
     * @param auth Authentication Manager Builder
     * @throws Exception If security can't be configured
     */
    @Autowired
    public void configureGlobal(final AuthenticationManagerBuilder auth)
            throws Exception 
        SimpleAuthorityMapper grantedAuthorityMapper =
                new SimpleAuthorityMapper();
        grantedAuthorityMapper.setPrefix("ROLE_");

        KeycloakAuthenticationProvider keycloakAuthenticationProvider =
                keycloakAuthenticationProvider();
        keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(
                grantedAuthorityMapper
        );
        auth.authenticationProvider(keycloakAuthenticationProvider);
    

    @Bean
    @Override
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() 
        return new RegisterSessionAuthenticationStrategy(
                new SessionRegistryImpl()
        );
    

然后是一个简单的控制器:

@RestController
@CrossOrigin("*")
@RequestMapping("/api/v3/auth")
public class AuthController 
    /**
     * Logger.
     */
    private static final Logger LOGGER =
            LoggerFactory.getLogger(AuthController.class);

    /**
     * Default constructor.
     */
    AuthController() 
        LOGGER.info("AuthController Constructor.");
    
...
    /**
     * Checks/initiates a user login.
     *
     * @return Information about the current user's auth status
     */
    @RolesAllowed("ROLE_user")
    @GetMapping(
            value = "/login1",
            produces = MediaType.APPLICATION_JSON_VALUE
    )
    @ResponseBody
    public Map<String, String> login1() 
        final Map<String, String> response = new HashMap<String, String>();
        response.put("status", "OK");

        final Authentication authentication =
                SecurityContextHolder.getContext().getAuthentication();
        final List<String> roles = new ArrayList<String>();
        for (GrantedAuthority authority : authentication.getAuthorities()) 
            roles.add(authority.getAuthority());
        
        response.put(
                "Current user roles",
                "[" + String.join(",", roles) + "]"
        );

        return response;
    

@RolesAllowed 注释确实启动了 Keycloak 集成。我被带到登录页面,我被允许登录,然后我被传回位于 /sso/login 的 REST 应用程序,并且该响应设置了我期望的 JWT:

Set-Cookie: KEYCLOAK_ADAPTER_STATE=eyJhbGciOi...

使用https://jwt.io/ 进行调试,该令牌对我来说看起来不错,具体来说:

  "realm_access": 
    "roles": [
      "offline_access",
      "uma_authorization",
      "user"
    ]
  ,

但是,当我被引导回到登录页面时,我收到了 403 禁止。所以我在同一个控制器中设计了第二种方法:

    @GetMapping(
            value = "",
            produces = MediaType.APPLICATION_JSON_VALUE
    )
    @ResponseBody
    public Map<String, String> read() 
        final Map<String, String> response = new HashMap<String, String>();
        response.put("status", "OK");

        final Authentication authentication =
                SecurityContextHolder.getContext().getAuthentication();

        response.put(
                "AUTHENTICATION NAME",
                authentication.getName()
        );
        response.put(
                "AUTHENTICATION PRINCIPAL",
                authentication.getPrincipal().toString()
        );
        response.put(
                "AUTHENTICATION AUTHORITIES",
                authentication.getAuthorities().toString()
        );

        int i = 0;
        for (GrantedAuthority authority : authentication.getAuthorities()) 
            response.put(
                    "AUTHORITY-" + i++,
                    authority.getAuthority()
            );
        

        return response;
    

输出如下:


  "AUTHENTICATION PRINCIPAL": "<GUID I expect>",
  "AUTHENTICATION NAME": "<GUID I expect>",
  "status": "OK",
  "AUTHENTICATION AUTHORITIES": "[]"

所以...我显然从 Keycloak 获得了身份验证 JWT,看起来 Spring 正在尝试用它做正确的事情。但是 GrantedAuthority 列表总是空的,所以我总是得到 403。

对这里出了什么问题有什么建议吗?

【问题讨论】:

【参考方案1】:

当然,这一切都归结为一个配置行:

keycloak.use-resource-role-mappings = true

将其更改为 false 从领域映射而不是资源映射中提取,我很好。

【讨论】:

以上是关于Spring Boot 和 Keycloak - GrantedAuthority 总是空着回来的主要内容,如果未能解决你的问题,请参考以下文章

dockerized 环境中的 Keycloak 和 Spring Boot Web 应用程序

Docker(Spring Boot 或 Thorntail)和 Keycloak

Spring Boot 我无法切换 keycloak 和基本身份验证

403 spring-boot-2-keycloak-适配器

Spring Boot 和 Keycloak - 仅适用于 get 方法

Keycloak Spring Boot 适配器和匿名资源