编辑此页面

安全测试

本文档描述了如何测试 Quarkus Security。

配置用户信息

您可以使用 quarkus-elytron-security-properties-file 进行安全测试。 它支持将用户信息嵌入到 application.properties 和独立的属性文件中。

例如,以下配置将允许在需要 OAuth2 的生产模式和使用 配置Profile 的开发模式中配置用户。

# Configure embedded authentication
%dev.quarkus.security.users.embedded.enabled=true
%dev.quarkus.security.users.embedded.plain-text=true
%dev.quarkus.security.users.embedded.users.scott=reader
%dev.quarkus.security.users.embedded.users.stuart=writer
%dev.quarkus.security.users.embedded.roles.scott=READER
%dev.quarkus.security.users.embedded.roles.stuart=READER,WRITER

# Configure OAuth2
quarkus.oauth2.enabled=true
%dev.quarkus.oauth2.enabled=false
quarkus.oauth2.client-id=client-id
quarkus.oauth2.client-secret=client-secret
quarkus.oauth2.introspection-url=http://host:port/introspect

测试安全扩展

Quarkus 提供了对使用不同用户进行测试以及禁用安全子系统的显式支持。 要使用此功能,您必须包含 quarkus-test-security 依赖项

pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-test-security</artifactId>
    <scope>test</scope>
</dependency>
build.gradle
testImplementation("io.quarkus:quarkus-test-security")

此构件提供了 io.quarkus.test.security.TestSecurity 注解,该注解可以应用于测试方法和测试类,以控制运行测试的安全上下文。 这允许您做两件事,您可以禁用授权,以便测试可以在无需身份验证的情况下访问受保护的端点,并且您可以指定您希望测试在哪个身份下运行。

运行禁用授权的测试只需将 enabled 属性设置为 false

@Test
@TestSecurity(authorizationEnabled = false)
void someTestMethod() {
...
}

这将禁用所有访问检查,允许测试在无需身份验证的情况下访问受保护的端点。

您也可以使用它来配置测试将以哪个当前用户身份运行

@Test
@TestSecurity(user = "testUser", roles = {"admin", "user"})
void someTestMethod() {
...
}

这将使用具有给定用户名和角色的身份运行测试。 请注意,这些可以组合使用,因此您可以禁用授权,同时提供一个身份来运行测试,如果端点期望存在身份,这可能很有用。

有关测试依赖于注入的 JsonWebToken 的端点代码的更多详细信息,请参阅 OpenID Connect Bearer Token 集成测试OpenID Connect 授权码流集成测试SmallRye JWT 集成测试

此外,您可以为身份指定属性,可能是通过身份增强添加的自定义项目

@Inject
SecurityIdentity identity;

@Test
@TestSecurity(
    user = "testUser",
    roles = {"admin", "user"},
    attributes = { @SecurityAttribute(key = "answer", value = "42", type = AttributeType.LONG) })
void someTestMethod() {
    Long answer = identity.<Long>getAttribute("answer");
...
}

这将使用具有类型为 Long 且名为 answer 的属性的身份运行测试。

该功能仅适用于 @QuarkusTest,并且适用于 @QuarkusIntegrationTest

@TestSecurity 也可以在元注解中使用,例如这样

    @Retention(RetentionPolicy.RUNTIME)
    @Target({ ElementType.METHOD })
    @TestSecurity(user = "testUser", roles = {"admin", "user"})
    public @interface TestSecurityMetaAnnotation {

    }

如果需要在多个测试方法中使用相同的安全设置集,则这特别有用。

@TestSecurity 注解也适用于 @PermissionsAllowed 安全注解。 考虑以下示例

@Test
@TestSecurity(user = "testUser", permissions = "see:detail")
void someTestMethod() {
    ...
}

这将使用具有权限 see 和操作 detail 的身份运行测试。 因此,对以下示例中声明的 getDetail 方法的调用将成功

@PermissionsAllowed("see:detail")
public String getDetail() {
    return "detail";
}

也可以设置自定义权限,如下例所示

@PermissionsAllowed("see", permission = CustomPermission.class)
public String getDetail() {
    return "detail";
}

需要使用 SecurityIdentityAugmentor CDI Bean 将 CustomPermission 授予由 @TestSecurity 注解创建的 SecurityIdentity

@ApplicationScoped
public class CustomSecurityIdentityAugmentor implements SecurityIdentityAugmentor {
    @Override
    public Uni<SecurityIdentity> augment(SecurityIdentity securityIdentity,
            AuthenticationRequestContext authenticationRequestContext) {
        final SecurityIdentity augmentedIdentity;
        if (shouldGrantCustomPermission(securityIdentity) {
            augmentedIdentity = QuarkusSecurityIdentity.builder(securityIdentity)
                                   .addPermission(new CustomPermission("see")).build();
        } else {
            augmentedIdentity = securityIdentity;
        }
        return Uni.createFrom().item(augmentedIdentity);
    }
}

如果您将 @TestSecurity#augmentors 注解属性设置为 CustomSecurityIdentityAugmentor.class,Quarkus 将仅使用 @TestSecurity 注解创建的 SecurityIdentity 来增强身份,如下所示

@Test
@TestSecurity(user = "testUser", permissions = "see:detail", augmentors = CustomSecurityIdentityAugmentor.class)
void someTestMethod() {
    ...
}

混合安全测试

如果需要同时使用 @TestSecurity 和基本身份验证(当未定义任何身份验证机制时的回退身份验证机制)来测试安全功能,则需要显式启用基本身份验证,例如,通过设置 quarkus.http.auth.basic=true%test.quarkus.http.auth.basic=true

同样,如果需要同时使用 @TestSecurity 和 Bearer 令牌身份验证来测试安全功能,您可以像下面的示例一样同时利用这两种方法

@Test
@TestSecurity(user = "Bob")
public void testSecurityMetaAnnotation {
    RestAssured.given()
            .auth().oauth2(getTokenForUser("Alice")) (1)
            .get("hello")
            .then()
            .statusCode(200)
            .body(Matchers.is("Hello Alice"));
    RestAssured.given()
            .get("hello")
            .then()
            .statusCode(200)
            .body(Matchers.is("Hello Bob")); (2)
}

@Path("hello")
public static class HelloResource {

  @Inject
  SecurityIdentity identity;

  @Authenticated
  @GET
  public String sayHello() {
       return "Hello " + identity.getPrincipal().getName();
  }
}
1 由于 Bearer 访问令牌随 HTTP 请求一起发送,因此使用 Bearer 令牌身份验证机制。
2 不存在授权标头,因此测试安全扩展创建用户 Bob

基于路径的身份验证

必须组合身份验证机制时,也可以使用 @TestSecurity。 以下示例显示了在启用基于路径的身份验证时如何选择身份验证机制。

@Test
@TestSecurity(user = "testUser", roles = {"admin", "user"}, authMechanism = "basic") (1)
void basicTestMethod() {
    ...
}

@Test
@TestSecurity(user = "testUser", roles = {"admin", "user"}, authMechanism = "form") (2)
void formTestMethod() {
    ...
}
1 “authMechanism”属性选择基本身份验证。
2 “authMechanism”属性选择基于表单的身份验证。

在您的 Quarkus 应用程序中,可以使用注解来选择特定于每个 Jakarta REST 端点的身份验证机制

package org.acme.security.testing;

import io.quarkus.vertx.http.runtime.security.annotation.BasicAuthentication;
import io.quarkus.vertx.http.runtime.security.annotation.FormAuthentication;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;

@Path("/")
public class TestSecurityResource {

    @BasicAuthentication (1)
    @GET
    @Path("basic-only")
    public String basicOnly() {
        return "basic-only";
    }

    @FormAuthentication (2)
    @GET
    @Path("form-only")
    public String formOnly() {
        return "form-only";
    }
}
1 来自 basicTestMethod 测试的所有到 /basic-only 路径的 HTTP 请求都经过成功身份验证。
2 当从 formTestMethod 测试调用时,相同的 HTTP 请求将失败,因为需要基本身份验证。

或者,可以使用 HTTP 安全策略选择特定于路径的身份验证机制

# require basic authentication for the '/basic-only' path
quarkus.http.auth.permission.basic.paths=/basic-only
quarkus.http.auth.permission.basic.policy=authenticated
quarkus.http.auth.permission.basic.auth-mechanism=basic

# require form-based authentication for the '/form-only' path
quarkus.http.auth.permission.form.paths=/form-only
quarkus.http.auth.permission.form.policy=authenticated
quarkus.http.auth.permission.form.auth-mechanism=form

使用 Wiremock 进行集成测试

您还可以使用 Wiremock 来模拟授权 OAuth2 和 OIDC 服务:有关更多详细信息,请参阅 OAuth2 集成测试OpenID Connect Bearer Token 集成测试OpenID Connect 授权码流集成测试SmallRye JWT 集成测试

相关内容