【问题标题】:Use keycloak for only authentication and use custom filter for authorization (Spring Boot)仅使用 keycloak 进行身份验证并使用自定义过滤器进行授权(Spring Boot)
【发布时间】:2021-09-19 19:01:24
【问题描述】:

我正在尝试仅使用 keycloak 进行身份验证,并使用我自己的自定义过滤器进行授权。所以理想的流程是:首先 Keycloak 过滤器对请求进行身份验证并在上下文中设置身份验证对象。然后我的自定义过滤器应该运行,它应该获取现有的身份验证对象,在该身份验证对象中添加权限并将其设置回上下文中。

我的 securityConfig 正在像这样扩展 KeycloakWebSecurityConfigurerAdapter

@Configuration
@EnableWebSecurity
class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter
{
    @Override
    protected void configure(HttpSecurity http) throws Exception
   {
      super.configure(http);
        http
        .cors()
        .and()
        .csrf().ignoringAntMatchers("/","/auth","/auth/logout").csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()).and()
        .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
        .authorizeRequests()
        .antMatchers(
                "/",
                "/auth",
                "/password/**",
              "/register/**",
              "/v2/api-docs",
              "/actuator/**",
              "/configuration/ui",
              "/swagger-resources",
              "/configuration/security",
              "/swagger-ui.html",
              "/webjars/**",
              "/swagger-resources/configuration/ui",
              "/swagger-resources/configuration/security",
              "/browser/index.html#",
              "/browser/**").permitAll()
        .antMatchers(HttpMethod.POST, REGISTER).permitAll()
        .antMatchers(HttpMethod.POST, CONFIRM).permitAll()
        .anyRequest().authenticated()
        .and()
        .addFilter(new JWTAuthorizationFilter(authenticationManager(),context))
//      .addFilterAfter(new JWTAuthorizationFilter(authenticationManager(),context), KeycloakAuthenticationProcessingFilter.class)
        .headers()
        .contentSecurityPolicy("script-src 'self'");
}

它首先运行 KeycloakAuthenticationProcessingFilter,然后运行我的自定义过滤器(JWTAuthorizationFilter),但随后再次调用 KeycloakAuthenticationProcessingFilter,因为再次设置了身份验证对象并清除了权限。 (我尝试了几件事。当前代码加上注释行等等)

首先,在快速启动应用程序中使用 keycloak 是否是正确的方法,如果是这样,那么我怎样才能使我的过滤器在过滤器链中最后运行?

【问题讨论】:

  • 我不熟悉KeycloakAuthenticationProcessingFilter,但请确保它没有注册两次。有关类似建议,请参阅 this comment
  • 实际上 KeycloakAuthenticationProcessingFilter 不在我的应用程序中,我正在使用库。是否有任何解决方案,无论它运行多少次,我都可以将我的过滤器放在链的末尾?
  • 我不熟悉 keycloak 库,但目前看来它可能没有得到很好的维护。我刚刚使用内置的.oauth2Client(withDefaults()) 配置成功地针对 keycloak 测试了 Spring Security 5.5,它运行良好!所以我想.oauth2Login(withDefaults()) 也可以。

标签: spring spring-boot spring-security keycloak openid-connect


【解决方案1】:

您似乎将两种范式混合在一起,1) 使用 keycloak 作为授权服务器,以及 2) 尝试将 JWT 用于 auth(entication|orization) 中的一个或两个。

请参阅 Spring Security 的 Josh Cummings 撰写的 this recently minted video,了解何时使用 JWT,以及如何使用 Spring Security 5。此外,请查看 JWT sample,它完全取代了您的 JWTAuthenticationFilter,而无需使用 keycloak-spring-security-adapter。您还可以查看OAuth2 Login sample,获取有关使用 Spring Security 进行社交登录的指南。

就具体使用keycloak而言,我推荐使用Spring Security 5的内置功能,它与Keycloak的授权服务器完全集成,应该同时实现你的两个目标。

操作方法

我正在使用 docker 使用以下命令运行 keycloak:

docker run --name keycloak -p 9000:8080 -e KEYCLOAK_USER=admin -e KEYCLOAK_PASSWORD=admin -e DB_VENDOR=h2 -d jboss/keycloak

我还在我的主机文件(例如/etc/hosts)中添加了以下内容以进行测试:

127.0.0.1       auth-server

使用admin:admin 登录keycloak 后,我设置了一个名为myrealm 的领域和一个名为test-client 的客户端,其有效重定向URI 为http://localhost:8080/*,附加范围为resource:read,生成了一个客户端密码,最后创建了一个有凭据的用户。

TLDR: GitHub example

要开始使用,请确保类路径上有以下依赖项:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-oauth2-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

有两种方法可以配置应用使用 keycloak 作为授权服务器(提供者):

server:
  port: 8080

keycloak-server:
  uri: http://auth-server:9000/auth/realms/myrealm
  openid-uri: ${keycloak-server.uri}/protocol/openid-connect

spring:
  security:
    oauth2:
      client:
        registration:
          test-client:
            provider: keycloak
            client-id: test-client
            client-secret: ${TEST_CLIENT_SECRET:your-client-secret}
            client-authentication-method: client_secret_basic
            authorization-grant-type: authorization_code
            redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
            scope: resource:read
        provider:
          keycloak:
            # Configure the provider with keycloak
            authorization-uri: ${keycloak-server.openid-uri}/auth
            token-uri: ${keycloak-server.openid-uri}/token
            user-info-uri: ${keycloak-server.openid-uri}/userinfo
            user-info-authentication-method: client_secret_basic
            jwk-set-uri: ${keycloak-server.openid-uri}/certs
            # Alternatively, set issuer-uri (replaces above settings) to use ${keycloak-server.uri}/.well-known/openid-configuration
            # to auto-configure OpenID Connect on startup.
            # issuer-uri: ${keycloak-server.uri}
            # This is required in either case to inform Spring Security about keycloak's username
            user-name-attribute: preferred_username

logging:
  level:
    org.springframework.security: trace

最简单的方法是设置上面的issuer-uri 属性,但这需要在应用启动时运行 Keycloak 服务器。上例中显式配置未注释。

这是一个示例控制器来证明它的工作原理:

@RestController
public class ExampleController {
    @GetMapping("/")
    public Map<String, String> home(@AuthenticationPrincipal DefaultOAuth2User user) {
        return Map.of("message", "You are logged in, " + user.getName() + "!");
    }

    @GetMapping("/token")
    public OAuth2AccessToken token(@RegisteredOAuth2AuthorizedClient("test-client") OAuth2AuthorizedClient testClient) {
        return testClient.getAccessToken();
    }
}

这通常足以使用 keycloak 进行身份验证。如果你想对 Spring Security 进行更细粒度的控制,请使用以下配置开始:

@EnableWebSecurity
public class SecurityConfiguration {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        // @formatter:off
        http
            .authorizeRequests(authorizeRequests -> authorizeRequests
                .antMatchers("/token").hasAuthority("SCOPE_resource:read")
                .anyRequest().authenticated()
            )
            .oauth2Client(withDefaults())
            .oauth2Login(withDefaults());
        // @formatter:on

        return http.build();
    }

}

总结

此示例使用 Spring Security 5 对 OAuth 2.0 和 OpenID Connect 1.0 的开箱即用支持以及 Keycloak。

注意您不需要自定义过滤器来从 keycloak 获取权限,我们只是要求resource:read 范围可用,Spring Security 与 Keycloak 合作以确保我们对此拥有权利权威。我们也使用 JWT 进行身份验证,而是使用标准会话管理,推荐

【讨论】:

  • 这句话对我有帮助:The easiest way is to set the issuer-uri property above 谢谢。
【解决方案2】:

Keycloak 是一个基于标准的解决方案,具有一些良好的令牌发行能力。然后,您可以在 API 中使用您喜欢的任何库。 Keycloak 适配器可能不是最佳选择。

您可能想要的主要内容是:

  • 以标准方式验证 JWT
  • 控制授权和声明
  • 有据可查的解决方案

不久前,我整理了一些代码来展示如何在需要的情况下更密切地控制 Spring Boot OAuth 处理 - 以防它为您自己的解决方案提供一些想法:

我的代码使用 Nimbus SDK 作为具有出色文档的 OAuth 库的示例。

【讨论】:

    猜你喜欢
    • 2018-03-12
    • 2021-06-05
    • 2012-02-27
    • 2022-01-20
    • 2017-05-10
    • 2018-12-11
    • 2018-07-16
    • 2012-12-12
    • 2021-03-19
    相关资源
    最近更新 更多