spring - 调用 spring 授权服务器 OAuth2 REST 端点

标签 spring kotlin spring-security oauth-2.0 spring-oauth2

尝试使用 Spring Authorization Server 实现 OAuth2 协议(protocol)。使用以下配置创建了一个简单的应用程序。

@SpringBootApplication
class AuthorizationServerApplication

fun main(args: Array<String>) {
    runApplication<AuthorizationServerApplication>(*args)
}
@Configuration
@Import(OAuth2AuthorizationServerConfiguration::class)
class AuthorizationServerConfig {

    @Bean
    fun registeredClientRepository(): RegisteredClientRepository? {
        val registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())
            .clientId("client")
            .clientSecret("{noop}client-secret")
            .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
            .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
            .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
            .authorizationGrantType(AuthorizationGrantType.PASSWORD)
            .redirectUri("http://example-host:9002/test/admin")
            .redirectUri("http://example-host:9002/test")
            .scope(OidcScopes.OPENID)
            .scope("read")
            .build()
        return InMemoryRegisteredClientRepository(registeredClient)
    }

    ...

    @Bean
    fun providerSettings(): ProviderSettings? {
        return ProviderSettings.builder()
            .issuer("http://example-host:9000")
            .build()
    }
}
@EnableWebSecurity
class DefaultSecurityConfig {

    @Bean
    fun defaultSecurityFilterChain(http: HttpSecurity): SecurityFilterChain? {
        http
            .authorizeRequests { authorizeRequests ->
                authorizeRequests
                    .anyRequest().permitAll()
            }
            // these are disabled so that I won't get any additional issue this needs to be changed
            .formLogin().disable()
            .csrf().disable()

        return http.build()
    }

    @Bean
    fun users(): UserDetailsService? {
        val admin: UserDetails = User.withDefaultPasswordEncoder()
            .username("admin")
            .password("password")
            .roles("ADMIN")
            .authorities("read", "write")
            .build()

        val user: UserDetails = User.withDefaultPasswordEncoder()
            .username("user")
            .password("password")
            .roles("USER")
            .authorities("read")
            .build()
        return InMemoryUserDetailsManager(admin, user)
    }
}

当调用以下端点时:GET http://example-host:9000/.well-known/oauth-authorization-server我得到这些:

{
    "issuer": "http://example-host:9000",
    "authorization_endpoint": "http://example-host:9000/oauth2/authorize",
    "token_endpoint": "http://example-host:9000/oauth2/token",
    "token_endpoint_auth_methods_supported": [
        "client_secret_basic",
        "client_secret_post",
        "client_secret_jwt",
        "private_key_jwt"
    ],
    "jwks_uri": "http://example-host:9000/oauth2/jwks",
    "response_types_supported": [
        "code"
    ],
    "grant_types_supported": [
        "authorization_code",
        "client_credentials",
        "refresh_token"
    ],
    "revocation_endpoint": "http://example-host:9000/oauth2/revoke",
    "revocation_endpoint_auth_methods_supported": [
        "client_secret_basic",
        "client_secret_post",
        "client_secret_jwt",
        "private_key_jwt"
    ],
    "introspection_endpoint": "http://example-host:9000/oauth2/introspect",
    "introspection_endpoint_auth_methods_supported": [
        "client_secret_basic",
        "client_secret_post",
        "client_secret_jwt",
        "private_key_jwt"
    ],
    "code_challenge_methods_supported": [
        "S256"
    ]
}

我正在尝试通过身份验证并尝试遵循 this documentation 。我尝试了多个电话,其中之一是:

curl --location --request POST 'example-host:9000/oauth2/token' \
--header 'Authorization: Basic YWRtaW46cGFzc3dvcmQ=' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=client' \
--data-urlencode 'client_secret=client-secret'

大多数情况下我都会收到 401 消息,其中包含不同的消息。无法真正弄清楚在哪里可以找到一些带有示例的文档,因为我找到的示例对我的用例并没有真正的帮助。我不完全了解在客户端是前端应用程序的情况下如何能够验证和使用资源服务器的端点。也许我误解了什么?

编辑: 请求 token 端点时从授权服务器添加跟踪日志:

curl --location --request POST 'example-host:9000/oauth2/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=client' \
--data-urlencode 'client_secret=client-secret' \
--data-urlencode 'grant_type=password' \
--data-urlencode 'username=user' \
--data-urlencode 'password=password'
2022-06-14 16:41:47.154 TRACE 34744 --- [nio-9000-exec-2] o.s.security.web.FilterChainProxy        : Trying to match request against DefaultSecurityFilterChain [RequestMatcher=org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization.OAuth2AuthorizationServerConfigurer$$Lambda$746/0x000000080105b608@7a764446, Filters=[org.springframework.security.web.session.DisableEncodeUrlFilter@841f2ce, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@38eb32b, org.springframework.security.web.context.SecurityContextPersistenceFilter@4232bd1e, org.springframework.security.oauth2.server.authorization.web.ProviderContextFilter@1c463b0b, org.springframework.security.web.header.HeaderWriterFilter@6e87e57e, org.springframework.security.web.csrf.CsrfFilter@3657ca3e, org.springframework.security.web.authentication.logout.LogoutFilter@36c9161d, org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationEndpointFilter@c85053, org.springframework.security.oauth2.server.authorization.oidc.web.OidcProviderConfigurationEndpointFilter@827dabb, org.springframework.security.oauth2.server.authorization.web.NimbusJwkSetEndpointFilter@3fce33c2, org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationServerMetadataEndpointFilter@5f5e39a5, org.springframework.security.oauth2.server.authorization.web.OAuth2ClientAuthenticationFilter@6f53bec6, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@1be28be5, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@12322dee, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@4aba4a37, org.springframework.security.web.session.SessionManagementFilter@3c29c1e5, org.springframework.security.web.access.ExceptionTranslationFilter@5e60456e, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@5771f1b4, org.springframework.security.oauth2.server.authorization.web.OAuth2TokenEndpointFilter@1ec08e27, org.springframework.security.oauth2.server.authorization.web.OAuth2TokenIntrospectionEndpointFilter@548d0a8d, org.springframework.security.oauth2.server.authorization.web.OAuth2TokenRevocationEndpointFilter@6c15398a, org.springframework.security.oauth2.server.authorization.oidc.web.OidcUserInfoEndpointFilter@6bcb4915]] (1/2)
2022-06-14 16:41:47.154 DEBUG 34744 --- [nio-9000-exec-2] o.s.security.web.FilterChainProxy        : Securing POST /oauth2/token
2022-06-14 16:41:47.154 TRACE 34744 --- [nio-9000-exec-2] o.s.security.web.FilterChainProxy        : Invoking DisableEncodeUrlFilter (1/22)
2022-06-14 16:41:47.154 TRACE 34744 --- [nio-9000-exec-2] o.s.security.web.FilterChainProxy        : Invoking WebAsyncManagerIntegrationFilter (2/22)
2022-06-14 16:41:47.154 TRACE 34744 --- [nio-9000-exec-2] o.s.security.web.FilterChainProxy        : Invoking SecurityContextPersistenceFilter (3/22)
2022-06-14 16:41:47.154 TRACE 34744 --- [nio-9000-exec-2] w.c.HttpSessionSecurityContextRepository : No HttpSession currently exists
2022-06-14 16:41:47.154 TRACE 34744 --- [nio-9000-exec-2] w.c.HttpSessionSecurityContextRepository : Created SecurityContextImpl [Null authentication]
2022-06-14 16:41:47.154 DEBUG 34744 --- [nio-9000-exec-2] s.s.w.c.SecurityContextPersistenceFilter : Set SecurityContextHolder to empty SecurityContext
2022-06-14 16:41:47.154 TRACE 34744 --- [nio-9000-exec-2] o.s.security.web.FilterChainProxy        : Invoking ProviderContextFilter (4/22)
2022-06-14 16:41:47.154 TRACE 34744 --- [nio-9000-exec-2] o.s.security.web.FilterChainProxy        : Invoking HeaderWriterFilter (5/22)
2022-06-14 16:41:47.154 TRACE 34744 --- [nio-9000-exec-2] o.s.security.web.FilterChainProxy        : Invoking CsrfFilter (6/22)
2022-06-14 16:41:47.154 TRACE 34744 --- [nio-9000-exec-2] o.s.security.web.csrf.CsrfFilter         : Did not protect against CSRF since request did not match And [CsrfNotRequired [TRACE, HEAD, GET, OPTIONS], Not [Or [org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization.OAuth2AuthorizationServerConfigurer$$Lambda$746/0x000000080105b608@7a764446]]]
2022-06-14 16:41:47.154 TRACE 34744 --- [nio-9000-exec-2] o.s.security.web.FilterChainProxy        : Invoking LogoutFilter (7/22)
2022-06-14 16:41:47.154 TRACE 34744 --- [nio-9000-exec-2] o.s.s.w.a.logout.LogoutFilter            : Did not match request to Ant [pattern='/logout', POST]
2022-06-14 16:41:47.154 TRACE 34744 --- [nio-9000-exec-2] o.s.security.web.FilterChainProxy        : Invoking OAuth2AuthorizationEndpointFilter (8/22)
2022-06-14 16:41:47.154 TRACE 34744 --- [nio-9000-exec-2] o.s.security.web.FilterChainProxy        : Invoking OidcProviderConfigurationEndpointFilter (9/22)
2022-06-14 16:41:47.154 TRACE 34744 --- [nio-9000-exec-2] o.s.security.web.FilterChainProxy        : Invoking NimbusJwkSetEndpointFilter (10/22)
2022-06-14 16:41:47.155 TRACE 34744 --- [nio-9000-exec-2] o.s.security.web.FilterChainProxy        : Invoking OAuth2AuthorizationServerMetadataEndpointFilter (11/22)
2022-06-14 16:41:47.155 TRACE 34744 --- [nio-9000-exec-2] o.s.security.web.FilterChainProxy        : Invoking OAuth2ClientAuthenticationFilter (12/22)
2022-06-14 16:41:47.155 TRACE 34744 --- [nio-9000-exec-2] o.s.s.authentication.ProviderManager     : Authenticating request with JwtClientAssertionAuthenticationProvider (1/11)
2022-06-14 16:41:47.155 TRACE 34744 --- [nio-9000-exec-2] o.s.s.authentication.ProviderManager     : Authenticating request with ClientSecretAuthenticationProvider (2/11)
2022-06-14 16:41:47.155 TRACE 34744 --- [nio-9000-exec-2] o.s.s.authentication.ProviderManager     : Authenticating request with PublicClientAuthenticationProvider (3/11)
2022-06-14 16:41:47.155 TRACE 34744 --- [nio-9000-exec-2] o.s.s.w.header.writers.HstsHeaderWriter  : Not injecting HSTS header since it did not match request to [Is Secure]
2022-06-14 16:41:47.155 DEBUG 34744 --- [nio-9000-exec-2] w.c.HttpSessionSecurityContextRepository : Did not store empty SecurityContext
2022-06-14 16:41:47.156 DEBUG 34744 --- [nio-9000-exec-2] w.c.HttpSessionSecurityContextRepository : Did not store empty SecurityContext
2022-06-14 16:41:47.156 DEBUG 34744 --- [nio-9000-exec-2] s.s.w.c.SecurityContextPersistenceFilter : Cleared SecurityContextHolder to complete request

最佳答案

首先,在您的情况下,您在 token 请求中不需要 Authorization header ,因为您明确允许所有请求通过 authorizeRequests.anyRequest().permitAll() 传递.

其次,在您的 curl 请求中,您没有至少指定所需的授权类型及其参数。

例如,对于password 授权类型,请求可能如下所示:

curl -L -X POST 'example-host:9000/oauth2/token' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=client' \
--data-urlencode 'client_secret=client-secret' \ 
--data-urlencode 'grant_type=password' \ 
--data-urlencode 'username=user' \ 
--data-urlencode 'password=password'

更新:

  1. Spring 授权服务器 0.3.0 不支持 password 授权类型,正如 .well 的 grant_types_supported 部分所示。 -known/oauth-authorization-server 端点输出。 org.springframework.security.oauth2.server.authorization.authentication 包中没有这样的身份验证提供程序。

  2. 要至少使 client_credentials token 请求正常工作,请添加

.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)

并且(如果您想在 POST 正文中传递 client_idclient_secret)

.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST)

registeredClientRepository 中的 RegisteredClient。 然后可以用以下方法进行测试

curl -L -X POST 'http://example-host:9000/oauth2/token' -H 'Content-Type: application/x-www-form-urlencoded' -d 'grant_type=client_credentials&client_id=client&client_secret=client-secret'

(请务必传递 RegisteredClient 的实际 client_idclient_secret)

此外,如果您导入 OAuth2AuthorizationServerConfiguration,则会创建身份验证服务器端点的默认 SecurityFilterChain,无需手动定义它。另一方面,您的应用程序身份验证可能仍然需要 SecurityFilterChain

  • 要调试 OAuth 身份验证过程并查看确切的异常(如果有),请在 org.springframework.security.authentication.ProviderManager#authenticate() 方法中设置一些断点
  • 关于spring - 调用 spring 授权服务器 OAuth2 REST 端点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72615662/

    相关文章:

    java - tomcat 8.5 上的 Apache cxf 回车问题

    java - Spring MVC : @Value annotation to get int value defined in *. 属性文件

    javascript - 如何在 Kotlin 中定义一个全局 js 函数?

    android - 如何以字节形式获取 okhttp 存储的缓存大小?

    java - Spring AccessDeniedException 中的自定义消息

    java - 解密 Saml token 时出错

    spring - 为什么我们需要 jackson 数据绑定(bind)?

    spring - SimpleMessageListenerContainer Amazon SQS 轮询间隔

    kotlin - 在Kotlin中替换String.format(…Locale)

    java - Spring 安全 : session-config with COOKIE not working