安全测试
本文档描述了如何测试 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
依赖项
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-test-security</artifactId>
<scope>test</scope>
</dependency>
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
的属性的身份运行测试。
该功能仅适用于 |
如果需要在多个测试方法中使用相同的安全设置集,则这特别有用。 |
@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 集成测试。