我正在开发一个使用密钥罩身份验证服务器的Spring后端.我正在努力让基于角色的授权发挥作用.我已经在我的领域中创建了一个用户"dev_admin",并为他分配了领域角色"admin".我有一个端点"Debuf/admin",只有具有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用户身份登录并try 访问admin端点之后,我返回错误403.这是来自弹簧控制台的信息:

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=dev@admin.com, 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=dev@admin.com, 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=dev@admin.com, 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

我还使用"imperassate"功能判断了dev_admin JWT内标识,该角色包含在该内标识中.我还注意到,在我的浏览器中,保存为Cookie的令牌要短得多,并且不包含该角色.这些都是我找到的线索,但我仍然不知道为什么它不起作用.如果有人知道发生了什么,那将是非常有帮助的.

编辑:顺便说一句,我没有使用 keys 斗篷适配器.它看起来就像这样,因为"keyloaklogouthandler"将请求发送到密钥伪装注销端点

我还应该提到,在我的例子中,客户端和资源服务器是同一服务器.我认为这个问题与安全配置有关.以下是我的申请表.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

推荐答案

根据the manual,您应该提供一个GrantedAuthoritiesMapper Bean或覆盖OAuth2UserService.

Do not use Keycloak adapters for Spring.它很久以前就被弃用了,不能与Spring Security6/Boot3一起使用.

Do not fetch user roles from Keycloak.这太低效了.取而代之的是,从 token 上阅读它.当您编写OAuth2客户端(它读取ID令牌,而不是访问令牌)时,您可能必须激活映射器:Client scopes->;roles->;Mappers->;realm roles并启用Add to ID tokenAdd to userinfo(如果您使用客户端角色,可能会执行相同的操作).

other answer个代码包含了在资源服务器和客户端中从Keyshaak角色到Spring授权的转换示例.

Java相关问答推荐

javafx getHostServices(). showDocument()调出Chrome而不是默认浏览器(Linux)

使用java访问具体子类特定方法的最佳方法是什么?

为什么Java的代码工作(if condition内部的实例)

Com.example.service.QuestionService中的构造函数的参数0需要找不到的类型为';com.example.Dao.QuestionDao;的Bean

我可以在MacOS上使用什么Java函数来在适当的设备上以适当的音量播放适当的alert 声音?

当构造函数创建一个新实例时,Java为什么需要&new";

删除打印语句会影响功能...腐败在起作用?

深度优先搜索实现:算法只向右搜索

组合连接以从两个表返回数据

为了安全起见,有必要复制一份 list 吗?

本机方法(JNI)总是编译的吗?

持续时间--为什么在秒为负数的情况下还要做额外的工作?

在Java中比较同一多维数组的两个不同的字符串元素

当我将JTextField的getText函数与相等的String进行比较时;t返回true

rest api服务 spring 启动中出现IllegalFormatConversionException

谷歌应用引擎本地服务器赢得';t在eclipse上运行

原始和参数化之间的差异调用orElseGet时可选(供应商)

JavaFX中ListView中的问题

Spring Boot应用程序中的自定义constraintvalidator不会被调用

单例模式中热切初始化和惰性初始化的区别