Security Testing
本文档介绍了如何测试 Quarkus Security。
This document describes how to test Quarkus Security.
Configuring User Information
您可以使用 quarkus-elytron-security-properties-file 来测试安全。这同时支持在 application.properties
和独立属性文件中嵌入用户信息。
You can use quarkus-elytron-security-properties-file for testing security. This supports both embedding user info in application.properties
and standalone properties files.
例如,以下配置将允许在生产中配置用户(在生产中需要 OAuth2)和使用 Configuration Profiles 的开发模式中的用户。
For example, the following configuration will allow for configuring the users in both the production where OAuth2 is required and development modes using Configuration Profiles.
# 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
Test Security Extension
Quarkus 为使用不同用户以及在禁用安全子系统的情况下进行测试提供了显式支持。要使用此功能,您必须包含 quarkus-test-security
依赖项:
Quarkus provides explicit support for testing with different users, and with the security subsystem disabled. To use
this you must include the quarkus-test-security
dependency:
<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
注释,该注释可以应用于测试方法和测试类以控制测试运行的安全上下文。这允许您执行两件事,您可以禁用授权,以便测试可以访问安全端点而无需进行身份验证,并且您可以指定希望测试在哪个标识下运行。
This artifact provides the io.quarkus.test.security.TestSecurity
annotation, that can be applied to test methods and
test classes to control the security context that the test is run with. This allows you to do two things, you can disable
authorization so tests can access secured endpoints without needing to be authenticated, and you can specify the identity
that you want the tests to run under.
在禁用授权的情况下运行的测试可以将 enabled 属性设置为 false:
A test that runs with authorization disabled can just set the enabled property to false:
@Test
@TestSecurity(authorizationEnabled = false)
void someTestMethod() {
...
}
这将禁用所有访问检查,这允许测试访问安全端点而无需进行身份验证。
This will disable all access checks, which allows the test to access secured endpoints without needing to authenticate.
您还可以使用它来配置测试将作为其运行的当前用户:
You can also use this to configure the current user that the test will run as:
@Test
@TestSecurity(user = "testUser", roles = {"admin", "user"})
void someTestMethod() {
...
}
这将使用具有给定用户名和角色的标识运行测试。请注意,这些可以组合起来,因此如果您在运行测试的同时禁用授权,这将很有用(如果端点需要存在标识,这可能很有用)。
This will run the test with an identity with the given username and roles. Note that these can be combined, so you can disable authorization while also providing an identity to run the test under, which can be useful if the endpoint expects an identity to be present.
有关测试依赖于注入的 JsonWebToken
的端点代码的更多详细信息,请参见 OpenID Connect Bearer Token Integration testing、OpenID Connect Authorization Code Flow Integration testing 和 SmallRye JWT Integration testing。
See OpenID Connect Bearer Token Integration testing, OpenID Connect Authorization Code Flow Integration testing and SmallRye JWT Integration testing for more details about testing the endpoint code which depends on the injected JsonWebToken
.
此外,您可以为标识指定属性,可能是使用标识增强添加的自定义项:
Additionally, you can specify attributes for the identity, perhaps custom items that were added with identity augmentation:
@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
的标识下运行测试。
This will run the test with an identity with an attribute of type Long
named answer
.
此功能仅适用于 @QuarkusTest
并且将在 @QuarkusIntegrationTest
上*not* 工作。
The feature is only available for @QuarkusTest
and will not work on a @QuarkusIntegrationTest
.
如果需要在多个测试方法中使用同一组安全设置,这会尤其有用。 This is particularly useful if the same set of security settings needs to be used in multiple test methods. |
Mixing security tests
如果使用 @TestSecurity
和基本的 Auth(这是在未定义任何内容时,成为后备的 authmechanism)测试安全功能变得有必要,则需要显式启用基本的 Auth,例如通过设置 quarkus.http.auth.basic=true
或 %test.quarkus.http.auth.basic=true
。
If it becomes necessary to test security features using both @TestSecurity
and Basic Auth (which is the fallback auth
mechanism when none is defined), then Basic Auth needs to be enabled explicitly,
for example by setting quarkus.http.auth.basic=true
or %test.quarkus.http.auth.basic=true
.
同样,如果在使用 @TestSecurity
和 Bearer 令牌验证两者的情况下,需要测试安全功能,可以像下面的示例中那样同时利用两者:
Similarly, if it becomes necessary to test security features using both @TestSecurity
and Bearer token authentication,
you can leverage both like in the example below:
@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 token authentication mechanism is used, because a Bearer access token is sent with the HTTP request. |
2 | No authorization header is present, therefore the Test Security Extension creates user Bob . |
Path-based authentication
可以在启用基于路径的验证时,使用 authentication mechanisms must be combined,如示例所示,说明如何选择验证机制。
@TestSecurity
can also be used when authentication mechanisms must be combined.
Example below shows how to select authentication mechanism when path-based authentication is enabled.
@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 | The 'authMechanism' attribute selects Basic authentication. |
2 | The 'authMechanism' attribute selects Form-based authentication. |
在 Quarkus 应用程序中,可以使用注释为各个 Jakarta REST 端点选择一种特定的验证机制:
In your Quarkus application, it is possible to use annotations to select an authentication mechanism specific to each Jakarta REST endpoint:
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 | All HTTP requests to the /basic-only path from the basicTestMethod test are authenticated successfully. |
2 | Same HTTP requests will fail when invoked from the formTestMethod test as Basic authentication is required. |
反之,可以使用 HTTP 安全策略来选择特定路径的验证机制:
Alternatively, it is possible to select path-specific authentication mechanism with HTTP Security Policy:
# 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
Use Wiremock for Integration Testing
还可以使用 Wiremock 来模拟授权 OAuth2 和 OIDC 服务:有关更详细信息,请参阅 OAuth2 Integration testing、OpenID Connect Bearer Token Integration testing、OpenID Connect Authorization Code Flow Integration testing 和 SmallRye JWT Integration testing。
You can also use Wiremock to mock the authorization OAuth2 and OIDC services: See OAuth2 Integration testing, OpenID Connect Bearer Token Integration testing, OpenID Connect Authorization Code Flow Integration testing and SmallRye JWT Integration testing for more details.