Keycloak + spring Boot 角色身份验证不起作用

2mbi3lxu  于 12个月前  发布在  Spring
关注(0)|答案(2)|浏览(101)

我正在开发一个使用keycloak认证服务器的spring后端。我正在努力获得基于角色的授权。我在我的领域中创建了一个用户“dev_admin”,并将领域角色“admin”赋予了他。我有一个端点“debuf/admin”,它只对具有管理员角色的用户可访问。我的安全配置看起来像这样:

@Configuration
@EnableWebSecurity
public class SecurityConfig {

private final KeycloakLogoutHandler keycloakLogoutHandler;
SecurityConfig(KeycloakLogoutHandler keycloakLogoutHandler) {
    this.keycloakLogoutHandler = keycloakLogoutHandler;
}

@Bean
public SecurityFilterChain clientFilterChain(HttpSecurity http) throws Exception {
    return http
        .authorizeHttpRequests(auth -> {
            auth
                .requestMatchers(new AntPathRequestMatcher("/debug/public")).permitAll()
                .requestMatchers(new AntPathRequestMatcher("/debug/authenticated")).authenticated()
                .requestMatchers(new AntPathRequestMatcher("/debug/admin")).hasAnyRole("admin")
                .anyRequest().authenticated();
        })
        .oauth2Login(Customizer.withDefaults())
        .logout((conf) -> {
            conf.addLogoutHandler(keycloakLogoutHandler);
            conf.logoutSuccessUrl("/index");
        })
        .build();
}
}

但是,在我以dev_admin用户身份登录并尝试访问admin端点后,我返回错误403。这是来自spring控制台的:

2023-09-10T19:44:54.653+02:00 TRACE 1400 --- [nio-8080-exec-7] estMatcherDelegatingAuthorizationManager : Authorizing SecurityContextHolderAwareRequestWrapper[ org.springframework.security.web.header.HeaderWriterFilter$HeaderWriterRequest@5f29c29e]
2023-09-10T19:44:54.653+02:00 TRACE 1400 --- [nio-8080-exec-7] estMatcherDelegatingAuthorizationManager : Checking authorization on SecurityContextHolderAwareRequestWrapper[ org.springframework.security.web.header.HeaderWriterFilter$HeaderWriterRequest@5f29c29e] using AuthorityAuthorizationManager[authorities=[ROLE_admin]]
2023-09-10T19:44:54.653+02:00 TRACE 1400 --- [nio-8080-exec-7] w.c.HttpSessionSecurityContextRepository : Retrieved SecurityContextImpl [Authentication=OAuth2AuthenticationToken [Principal=Name: [dev_admin], Granted Authorities: [[OIDC_USER, SCOPE_email, SCOPE_openid, SCOPE_profile]], User Attributes: [{at_hash=1blJDsXc37Pz3uyqygg43A, sub=413591c7-3213-40a4-be2d-82dc69fe1264, email_verified=true, iss=http://localhost:8081/realms/users, typ=ID, preferred_username=dev_admin, given_name=dev, nonce=f_D-DARLucc5Y0Uau8QLhgv2srTM0gK3MDA1zpaDXDM, sid=fab4e120-b249-4e76-bffb-c5a4820fa536, aud=[core-server], acr=1, azp=core-server, auth_time=2023-09-10T17:44:50Z, name=dev admin, exp=2023-09-10T17:49:50Z, session_state=fab4e120-b249-4e76-bffb-c5a4820fa536, family_name=admin, iat=2023-09-10T17:44:50Z, [email protected], jti=a7e9ff04-2526-4ff8-82c1-1216ee6d673a}], Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=95CD2FEF619B032D2A30C89CFAD0740C], Granted Authorities=[OIDC_USER, SCOPE_email, SCOPE_openid, SCOPE_profile]]] from SPRING_SECURITY_CONTEXT
2023-09-10T19:44:54.653+02:00 TRACE 1400 --- [nio-8080-exec-7] o.s.s.w.a.AnonymousAuthenticationFilter  : Did not set SecurityContextHolder since already authenticated OAuth2AuthenticationToken [Principal=Name: [dev_admin], Granted Authorities: [[OIDC_USER, SCOPE_email, SCOPE_openid, SCOPE_profile]], User Attributes: [{at_hash=1blJDsXc37Pz3uyqygg43A, sub=413591c7-3213-40a4-be2d-82dc69fe1264, email_verified=true, iss=http://localhost:8081/realms/users, typ=ID, preferred_username=dev_admin, given_name=dev, nonce=f_D-DARLucc5Y0Uau8QLhgv2srTM0gK3MDA1zpaDXDM, sid=fab4e120-b249-4e76-bffb-c5a4820fa536, aud=[core-server], acr=1, azp=core-server, auth_time=2023-09-10T17:44:50Z, name=dev admin, exp=2023-09-10T17:49:50Z, session_state=fab4e120-b249-4e76-bffb-c5a4820fa536, family_name=admin, iat=2023-09-10T17:44:50Z, [email protected], jti=a7e9ff04-2526-4ff8-82c1-1216ee6d673a}], Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=95CD2FEF619B032D2A30C89CFAD0740C], Granted Authorities=[OIDC_USER, SCOPE_email, SCOPE_openid, SCOPE_profile]]
2023-09-10T19:44:54.653+02:00 TRACE 1400 --- [nio-8080-exec-7] o.s.s.w.a.ExceptionTranslationFilter     : Sending OAuth2AuthenticationToken [Principal=Name: [dev_admin], Granted Authorities: [[OIDC_USER, SCOPE_email, SCOPE_openid, SCOPE_profile]], User Attributes: [{at_hash=1blJDsXc37Pz3uyqygg43A, sub=413591c7-3213-40a4-be2d-82dc69fe1264, email_verified=true, iss=http://localhost:8081/realms/users, typ=ID, preferred_username=dev_admin, given_name=dev, nonce=f_D-DARLucc5Y0Uau8QLhgv2srTM0gK3MDA1zpaDXDM, sid=fab4e120-b249-4e76-bffb-c5a4820fa536, aud=[core-server], acr=1, azp=core-server, auth_time=2023-09-10T17:44:50Z, name=dev admin, exp=2023-09-10T17:49:50Z, session_state=fab4e120-b249-4e76-bffb-c5a4820fa536, family_name=admin, iat=2023-09-10T17:44:50Z, [email protected], jti=a7e9ff04-2526-4ff8-82c1-1216ee6d673a}], Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=95CD2FEF619B032D2A30C89CFAD0740C], Granted Authorities=[OIDC_USER, SCOPE_email, SCOPE_openid, SCOPE_profile]] to access denied handler since access is denied

我还使用“impersonate”特性检查了dev_adminJWT令牌,该角色包含在令牌中。我还注意到,在我的浏览器中,保存为cookie的令牌要短得多,并且不包含角色。这些都是我找到的线索,但我仍然不知道为什么它不工作。如果有人知道,这将是非常有帮助的。
编辑:顺便说一下,我没有使用keycloak适配器。它看起来是这样的,因为“keycloaklogouthandler”向keycloaks logout端点发送请求
我还应该提到,在我的例子中,客户端和资源服务器是同一个服务器。我认为问题与安全配置有关。下面是我的应用程序.yml:

security:
oauth2:
  resourceserver.jwt:
      issuer-uri: ${keycloak.baseUrl}/realms/${keycloak.userRealm}
      jwk-set-uri: ${keycloak.baseUrl}/auth/realms/${keycloak.userRealm}/protocol/openid-connect/certs

  client:
    registration.keycloak:
      client-id: myclient
      authorization-grant-type: authorization_code
      scope: openid

      admin_user: ???
      admin_password: ???

    provider.keycloak:
      issuer-uri: ${keycloak.baseUrl}/realms/${keycloak.userRealm}
      user-name-attribute: preferred_username
r7xajy2e

r7xajy2e1#

根据手册,您应该提供GrantedAuthoritiesMapper bean或重写OAuth2UserService

不要将Keycloak适配器用于Spring。它很久以前就被弃用了,并且不适用于Spring Security 6 / Boot 3。
不从Keycloak中获取用户角色。这样效率太低了。相反,从标记中读取它。当你写一个OAuth2客户端(它读取ID令牌,而不是访问令牌)时,你可能需要激活Map器:Client scopes-> roles-> Mappers-> realm roles并启用Add to ID tokenAdd to userinfo(如果您使用客户端角色,您可以对它执行相同的操作)。

另一个答案包含了从Keycloak角色到资源服务器和客户端中的Spring权限的示例转换。

zf9nrax1

zf9nrax12#

您在SecurityConfig中指定角色的方式似乎有问题。要对Keycloak使用基于角色的授权,您需要使用Keycloak特定的角色前缀来指定角色。默认情况下,Keycloak对角色使用ROLE_前缀。
以下是如何修改SecurityConfig以使用正确的角色前缀:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(keycloakAuthenticationProvider());
    }

    @Bean
    @Override
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new NullAuthenticatedSessionStrategy();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
        http
            .authorizeRequests()
                .antMatchers("/debug/public").permitAll()
                .antMatchers("/debug/authenticated").authenticated()
                .antMatchers("/debug/admin").hasRole("admin") // Use "hasRole" with the role name without "ROLE_" prefix
                .anyRequest().authenticated()
            .and()
            .oauth2Login()
                .defaultSuccessURL("/success")
            .and()
            .logout()
                .logoutUrl("/logout")
                .logoutSuccessUrl("/index");
    }
}

在此修改后的配置中:
我们扩展了KeycloakWebSecurityConfigurerAdapter以利用Keycloak特有的特性。
我们将AuthenticationManagerBuilder配置为使用Keycloak提供的keycloakAuthenticationProvider
请确保您的Keycloak领域配置与此设置匹配,特别是关于角色名称。Keycloak中的角色应该命名为“admin”,而不带ROLE_前缀。
希望我能帮上忙。
此外,确保Sping Boot 应用程序的Keycloak客户端配置包括正确的角色Map。
完成这些修改后,再次尝试访问/debug/admin端点。如果在Keycloak中正确分配了角色,并且您的Keycloak客户端配置是准确的,则基于角色的授权应按预期工作。

相关问题