通过Spring Boot(Security)和Keycloak启用角色身份验证?

jpg*_*z18 12 java spring spring-boot keycloak

我正在尝试做一件简单的事情。

想要向单个端点发出请求并发送承载令牌(来自客户端),我希望验证该令牌,并取决于端点上keycloak接受/拒绝请求上分配的角色。

我遵循了许多教程甚至书籍,但是其中大多数我根本不理解。

遵循此设置我的密钥斗篷信息(领域,角色,用户) https://medium.com/@bcarunmail/securing-rest-api-using-keycloak-and-spring-oauth2-6ddf3a1efcc2

所以,

我基本上是通过客户端,具有特定角色“用户”的用户来设置我的密钥斗篷,并像这样进行配置:

@Configuration
@KeycloakConfiguration
//@ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
public class SecurityConf extends KeycloakWebSecurityConfigurerAdapter
{
    /**
     * Registers the KeycloakAuthenticationProvider with the authentication manager.
     */
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(keycloakAuthenticationProvider());
    }

    /**
     * Defines the session authentication strategy.
     */
    @Bean
    @Override
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
    }

    @Bean
    public KeycloakSpringBootConfigResolver KeycloakConfigResolver() {
        return new KeycloakSpringBootConfigResolver();
    }

    @Bean
    public FilterRegistrationBean keycloakAuthenticationProcessingFilterRegistrationBean(
            KeycloakAuthenticationProcessingFilter filter) {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
        registrationBean.setEnabled(false);
        return registrationBean;
    }

    @Bean
    public FilterRegistrationBean keycloakPreAuthActionsFilterRegistrationBean(
            KeycloakPreAuthActionsFilter filter) {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
        registrationBean.setEnabled(false);
        return registrationBean;
    }


    @Override
    protected void configure(HttpSecurity http) throws Exception
    {
        super.configure(http);
        http
                .authorizeRequests()
                .antMatchers("/user/*").hasRole("admin")
                .antMatchers("/admin*").hasRole("user")

    }
}
Run Code Online (Sandbox Code Playgroud)

我不明白为什么在很多教程中我都看到这一点(作为最后一条规则):

.anyRequest().permitAll();
Run Code Online (Sandbox Code Playgroud)

基本上,当我设置为不具有安全性时,可以不使用承载令牌来调用端点。

但是当我将其添加为最后一条规则时

 .anyRequest().denyAll();
Run Code Online (Sandbox Code Playgroud)

我总是得到403。

放行我发现了这一点:

请求是要进行身份验证

f.KeycloakAuthenticationProcessingFilter : Attempting Keycloak authentication
o.k.a.BearerTokenRequestAuthenticator    : Found [1] values in authorization header, selecting the first value for Bearer.
o.k.a.BearerTokenRequestAuthenticator    : Verifying access_token
o.k.a.BearerTokenRequestAuthenticator    : successful authorized
a.s.a.SpringSecurityRequestAuthenticator : Completing bearer authentication. Bearer roles: [] 
o.k.adapters.RequestAuthenticator        : User 'testuser' invoking 'http://localhost:9090/api/user/123' on client 'users'
o.k.adapters.RequestAuthenticator        : Bearer AUTHENTICATED
f.KeycloakAuthenticationProcessingFilter : Auth outcome: AUTHENTICATED
o.s.s.authentication.ProviderManager     : Authentication attempt using org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider
o.s.s.core.session.SessionRegistryImpl   : Registering session 5B871A0E2AF55B70DC8E3B7436D79333, for principal testuser
f.KeycloakAuthenticationProcessingFilter : Authentication success using bearer token/basic authentication. Updating SecurityContextHolder to contain: org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken@355f68d6: Principal: testuser; Credentials: [PROTECTED]; Authenticated: true; Details: org.keycloak.adapters.springsecurity.account.SimpleKeycloakAccount@5d7a32a9; Not granted any authorities
[nio-9090-exec-3] o.s.security.web.FilterChainProxy        : /api/user/123 at position 8 of 15 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
nio-9090-exec-3] o.s.s.w.s.DefaultSavedRequest            : pathInfo: both null (property equals)
[nio-9090-exec-3] o.s.s.w.s.DefaultSavedRequest            : queryString: both null (property equals)
Run Code Online (Sandbox Code Playgroud)

好像我没有担当角色...

我的依赖:

        <dependency>
            <groupId>org.keycloak</groupId>
            <artifactId>keycloak-spring-boot-starter</artifactId>
            <version>6.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.keycloak</groupId>
            <artifactId>keycloak-spring-security-adapter</artifactId>
            <version>6.0.1</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
Run Code Online (Sandbox Code Playgroud)

我的问题?

我请求发送访问令牌:

client_id -> my client from keycloak
username -> my user from keycloak
password -> my password from keycloak
grant_type -> password
client_secret -> from keycloak
Run Code Online (Sandbox Code Playgroud)

我得到一个令牌,然后用来向我的应用程序endint请求。无论我使用哪个端点(具有角色用户或具有角色admin的端点),我的请求始终有效。

在我的住处,我有这样的东西:

keycloak:
  auth-server-url: http://localhost:8080/auth/
  resource: users-api
  credentials:
    secret : my-secret
  use-resource-role-mappings : true
  realm: my-realm
  realmKey:  my-key
  public-client: true
  principal-attribute: preferred_username
  bearer-only: true
Run Code Online (Sandbox Code Playgroud)

知道如何在这种情况下实际启用角色吗?

我是否必须配置客户端以使用JWT?有任何想法吗?

我还在端点上添加了注释

@Secured("admin")
@PreAuthorize("hasAnyAuthority('admin')")
Run Code Online (Sandbox Code Playgroud)

但似乎他们什么也没做...

-编辑-

固定网址以匹配资源后,我仍然得到403。

"realm_access": {
    "roles": [
      "offline_access",
      "admin",
      "uma_authorization"
    ]
  },
  "resource_access": {
    "account": {
      "roles": [
        "manage-account",
        "manage-account-links",
        "view-profile"
      ]
    }
  },
Run Code Online (Sandbox Code Playgroud)

它以某种方式将resource_access与我的问题相关联吗?

ch4*_*4mp 11

2022年更新

Spring 的 Keycloak 适配器已被弃用。不要使用它。spring-boot-starter-oauth2-resource-server代替使用。

简单的解决方案

在 之上有一组非常方便的spring-boot-starter-oauth2-resource-server,配置可以非常简单:

@EnableMethodSecurity
@Configuration
public static class SecurityConfig {
    @Bean
    ExpressionInterceptUrlRegistryPostProcessor expressionInterceptUrlRegistryPostProcessor() {
        return (ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry) -> registry
            .antMatchers("/api/user/**").hasAuthority("USER")
            .antMatchers("/api/admin/**").hasAuthority("ADMIN")
            .anyRequest().authenticated();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)
com.c4-soft.springaddons.security.issuers[0].location=https://localhost:8443/realms/master
com.c4-soft.springaddons.security.issuers[0].authorities.claims=realm_access.roles,resource_access.employee-service.roles,resource_access.other-client.roles
com.c4-soft.springaddons.security.cors[0].path=/api/**
com.c4-soft.springaddons.security.permit-all=/actuator/health/readiness,/actuator/health/liveness,/v3/api-docs/**
Run Code Online (Sandbox Code Playgroud)

仅弹簧解决方案

要仅使用相同的方法spring-boot-starter-oauth2-resource-server,需要编写相当多的 Java conf:

com.c4-soft.springaddons.security.issuers[0].location=https://localhost:8443/realms/master
com.c4-soft.springaddons.security.issuers[0].authorities.claims=realm_access.roles,resource_access.employee-service.roles,resource_access.other-client.roles
com.c4-soft.springaddons.security.cors[0].path=/api/**
com.c4-soft.springaddons.security.permit-all=/actuator/health/readiness,/actuator/health/liveness,/v3/api-docs/**
Run Code Online (Sandbox Code Playgroud)
spring.security.oauth2.resourceserver.jwt.issuer-uri=https://localhost:8443/realms/master
Run Code Online (Sandbox Code Playgroud)

重要笔记

上面的两个配置都没有转换为 keycloak 角色(大小写不变,没有ROLE_前缀),使用hasAuthority(...)而不是hasRole(...).

此外,仅考虑在以下级别定义的角色:

  • “领域”
  • “员工服务”客户端(已在问题中引用的教程中定义
  • “other-client”(只是为了演示可以使用任何其他任意客户端)


sta*_*ker 7

在调试堆栈中:我看到您正在调用/api/user/123并且在您的安全配置中您正在保护/user/*这是不一样的,将您的安全更改为:

.antMatchers("/api/user/*").hasRole("user")
                .antMatchers("/api/admin*").hasRole("admin")
Run Code Online (Sandbox Code Playgroud)

PS:你不需要注册KeycloakAuthenticationProcessingFilterKeycloakPreAuthActionsFilter


emr*_*kgn 6

我知道这是一篇旧文章,但我写这篇文章只是为了将来参考,以防其他人遇到同样的问题。

如果您查看日志,Keycloak 已成功验证访问令牌,但没有任何授予的权限。这就是 Spring 不授权该请求并且您得到HTTP 403 Forbidden 的原因:

f.KeycloakAuthenticationProcessingFilter : Authentication success using bearer token/basic authentication. Updating SecurityContextHolder to contain: org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken@355f68d6: Principal: testuser; Credentials: [PROTECTED]; Authenticated: true; Details: org.keycloak.adapters.springsecurity.account.SimpleKeycloakAccount@5d7a32a9; Not granted any authorities
Run Code Online (Sandbox Code Playgroud)

这是因为 Keycloak 适配器被配置为使用资源(即客户端级别)角色映射而不是领域级别角色映射:

use-resource-role-mappings:如果设置为 true,适配器将在令牌内查找用户的应用程序级角色映射。如果为 false,它将查看领域级别的用户角色映射。这是可选的。默认值为 false。

是有关适配器配置的链接。

因此,如果您想通过领域角色获得授权,属性应该如下所示:

f.KeycloakAuthenticationProcessingFilter : Authentication success using bearer token/basic authentication. Updating SecurityContextHolder to contain: org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken@355f68d6: Principal: testuser; Credentials: [PROTECTED]; Authenticated: true; Details: org.keycloak.adapters.springsecurity.account.SimpleKeycloakAccount@5d7a32a9; Not granted any authorities
Run Code Online (Sandbox Code Playgroud)

注意:如果您想同时使用领域级和客户端级角色映射,那么您应该重写 KeycloakAuthenticationProvider.authenticate() 方法,通过自己组合来提供必要的角色。