在集成测试中使用 oauth/openid 进行身份验证

Posted

技术标签:

【中文标题】在集成测试中使用 oauth/openid 进行身份验证【英文标题】:Authentication with oauth/openid in integration tests 【发布时间】:2020-08-04 02:11:38 【问题描述】:

我在本地有以下情况:

使用 spring security 和 openid 保护的 spring boot 应用程序 应用程序配置为使用授权代码流。 我可以对其进行身份验证的 keycloak 服务器

现在我想写一些集成测试,我有点受阻。

默认情况下,我的安全性配置为有状态(因此,如果我导航到我的应用程序而不是访问令牌,我会得到一个会话 id cookie),这不是问题,因为我有一种 API 网关。 我有以下内容:

protected void configure(HttpSecurity http) throws Exception  // @formatter:off
    http.authorizeRequests(authorizeRequests -> authorizeRequests
            .anyRequest()
            .authenticated())
            .oauth2Login(AbstractAuthenticationFilterConfigurer::permitAll)
            .addFilterAfter(new CustomAuthenticationFilter(benutzerRepository), UsernamePasswordAuthenticationFilter.class)
            .logout(logout -> logout.logoutSuccessHandler(oidcLogoutSuccessHandler()));
  // @formatter:on

而我的 application.properties 类似于:

spring.security.oauth2.client.registration.keycloak.client-id=application_name
spring.security.oauth2.client.registration.keycloak.client-secret=36a2740b-e7b9-4e66-9e2f-242a19d7815f
spring.security.oauth2.client.registration.keycloak.client-name=Keycloak
spring.security.oauth2.client.registration.keycloak.scope=openid, roles
spring.security.oauth2.client.registration.keycloak.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.keycloak.client-authentication-method=basic
spring.security.oauth2.client.registration.keycloak.redirect-uri=baseUrl/login/oauth2/code/registrationId
spring.security.oauth2.client.provider.keycloak.authorization-uri=http://localhost:8180/auth/realms/application_name/protocol/openid-connect/auth
spring.security.oauth2.client.provider.keycloak.token-uri=http://localhost:8180/auth/realms/application_name/protocol/openid-connect/token
spring.security.oauth2.client.provider.keycloak.user-info-uri=http://localhost:8180/auth/realms/application_name/protocol/openid-connect/userinfo
spring.security.oauth2.client.provider.keycloak.jwk-set-uri=http://localhost:8180/auth/realms/application_name/protocol/openid-connect/certs
spring.security.oauth2.client.provider.keycloak.user-name-attribute=preferred_username

这可行,但现在,我想编写一些集成测试。所以基本上在测试中从我的spring boot应用程序调用API来触发一些操作,我的API当然是受保护的。

所以我所做的就是让 WebClient 尝试获取访问令牌,只是为了我的集成测试,重复通常在后台“神奇地完成”的请求。

我的想法是: - 使用 webclient 调用授权 url 以获取代码 - 将此代码与客户端 ID 和客户端密码一起发送以获取令牌

听起来很简单,但我只是被阻止了。我不断从 KEYcloak 收到 error=invalid_request,我不知道为什么。

我只是尝试执行以下操作以获取代码(这可能是错误的......):

URIBuilder authorizationURI = new URIBuilder("http://localhost:8180/auth/realms/application_name/protocol/openid-connect/auth");
        authorizationURI.addParameter("client_id", "application_name");
        authorizationURI.addParameter("scope", "openid");
        authorizationURI.addParameter("state", "123456");
        authorizationURI.addParameter("redirect_uri", "http://localhost:8080/login/oauth2/code/keycloak");
        authorizationURI.addParameter("response_type", "code");
        WebClient webclient = WebClient.builder()
                .baseUrl(authorizationURI.toString())
                .build();
        var test = webclient.post()
                .contentType(MediaType.APPLICATION_FORM_URLENCODED)
                .body(
                        BodyInserters.fromFormData("username", "user")
                                .with("password", "password"))
                .exchange()
                .block()
                .bodyToMono(String.class)
                .block();

【问题讨论】:

【参考方案1】:

如果有人遇到同样的问题,我找到了解决方法。

因此必须在 Keycloak 中为其客户端设置机密,然后启用服务客户端。那么就可以这样做:

URIBuilder authorizationURI = new URIBuilder("http://localhost:8180/auth/realms/application_name/protocol/openid-connect/token");
        WebClient webclient = WebClient.builder().build();
        MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
        formData.put("grant_type", Collections.singletonList("client_credentials"));
        formData.put("client_id", Collections.singletonList("application_name"));
        formData.put("client_secret", Collections.singletonList("tralala-5d0e-4bea-938b-884c1ce4c981"));

tadaaaa,您将获得一个用于测试的访问令牌。

【讨论】:

以上是关于在集成测试中使用 oauth/openid 进行身份验证的主要内容,如果未能解决你的问题,请参考以下文章

使用 OAuth2/OpenId Connect 和微服务进行身份验证和授权

OAuth/OpenID - 我应该使用哪个?

OAuth & OpenID & SAML 工作流程梳理对比

我实施 OAuth/OpenId 有多差?

使用 Oauth2/OpenID 连接构建 Web-API

Oauth2/Openid 连接。如何撤销未知的访问/刷新令牌