Kimlik Doğrulama, Proxy ve TLS
1.1.0 ile geldi. Yük testi istekleri Basic, Bearer veya OAuth2 client credentials kimlik doğrulaması taşıyabilir; bu, kodda LoadAuth ile ayarlanır. Ağ ayarları, yani bir HTTP proxy ile TLS trust ve key store'ları, testfly.yml içinde loadtest.proxy ve loadtest.tls altında ayarlanır. Bu sayfadaki her şey iki motorda da çalışır; farklar sonda listelenmiştir. Tasarım, depodaki docs/adr/0008-load-test-auth-proxy-tls.md (ADR 0008) dosyasında kayıtlıdır.
Parolaları, token'ları ve client secret'ları ortamdan okuyun: Java'da System.getenv("NAME"), testfly.yml içinde ${NAME} yer tutucuları. Bu sayfadaki örneklerin hepsi böyle yapar.
1. Kimlik doğrulama
LoadAuth değiştirilemez (immutable) bir değerdir. Bir senaryoda auth(..) ile ayarlanınca her adımın varsayılanı olur; bir adımda ayarlanınca senaryonunkini ezer.
| Fabrika metodu | Gönderilen | Nerede |
|---|---|---|
LoadAuth.basic(user, password) | Authorization: Basic base64(user:password) | senaryo veya adım |
LoadAuth.bearer(token) | Authorization: Bearer <token> | senaryo veya adım |
LoadAuth.oauth2ClientCredentials(tokenUrl, clientId, clientSecret) | koşu başına tek token ile Authorization: Bearer <token> (bkz. OAuth2) | yalnızca senaryo |
LoadAuth.fromConfig(name) | testfly.yml içindeki api.auth.<name> girdisi (basic, bearer, oauth2 tipleri) | senaryo veya adım (adımda oauth2 girdisi olmaz) |
LoadAuth.none() | kimlik doğrulama yok | adım (noAuth()) |
Kısayollar: LoadScenario.basicAuth(user, password), LoadScenario.oauth2ClientCredentials(tokenUrl, clientId, clientSecret, scope) (null scope hiç scope göndermez), LoadStep.basicAuth(user, password) ve LoadStep.noAuth(). Argümansız auth() ayarlanmış değeri veya null döndürür.
Bir LoadAuth nesnesinin toString() çıktısı türü, kullanıcı adını veya client id'yi ve sorgusu çıkarılmış token URL'ini gösterir; her parola, token ve secret için *** yazar.
Basic ve Bearer
TestFly, Basic başlığını iki motorda da kendisi, user:password metninin UTF-8 baytlarından oluşturur (RFC 7617); böylece iki motor da aynı baytları gönderir. Kullanıcı adı, parola veya token içindeki ${var} yer tutucuları her istekte feeder sütunlarından veya çıkarılan (extract) değişkenlerden çözümlenir; böylece her sanal kullanıcı kendi hesabıyla giriş yapabilir:
import io.testfly.loadtest.BaseLoadTest;
import io.testfly.loadtest.LoadAuth;
import io.testfly.loadtest.LoadTestFeeder;
import org.testng.annotations.Test;
public class OrdersAuthLoadTest extends BaseLoadTest {
@Test
public void ordersPerUser() {
loadScenario("Orders")
.feed(LoadTestFeeder.csv("testdata/users.csv")) // sütunlar: user,password
.basicAuth("${user}", "${password}") // her adımın varsayılanı
.step("List orders").get("/orders").and()
.step("Health").get("/health").noAuth().and() // kimlik bilgisi olmadan gönderilir
.step("Admin stats").get("/admin/stats")
.auth(LoadAuth.bearer(System.getenv("ADMIN_TOKEN"))).and()
.run().assertErrorRateBelow(0.01);
}
}
İstek oluşturulurken bir kimlik bilgisinde hâlâ çözümlenmemiş bir ${var} varsa (örneğin bir CSV satırında sütun eksikse) istek gönderilmez ve -1 durumuyla KO sayılır. Koşu devam eder. Çözümlendikten sonra : içeren bir kullanıcı adı veya kontrol karakteri (sekme dışında 0x20 altındaki herhangi bir karakter ya da DEL) içeren çözümlenmiş bir değer de aynı şekilde KO -1 olur. Bu, başlık enjeksiyonunu engeller ve değer hiçbir mesajda görünmez.
Öncelik
En özel ayar kazanır:
| Adımda | İstek şunu gönderir |
|---|---|
auth(..) veya basicAuth(..) | adımın kimlik doğrulaması |
noAuth() | kimlik doğrulama yok |
header("Authorization", ..) var, adım auth'u yok | o başlık; senaryonun auth'u eklenmez |
auth(..) ve header("Authorization", ..) | koşudan önce reddedilir (IllegalArgumentException) |
| hiçbiri | varsa senaryonun auth(..) ayarı |
OAuth2 client credentials
import io.testfly.loadtest.BaseLoadTest;
import io.testfly.loadtest.LoadAuth;
import org.testng.annotations.Test;
public class CatalogOAuth2LoadTest extends BaseLoadTest {
@Test
public void searchWithServiceToken() {
loadScenario("Catalog search")
.auth(LoadAuth.oauth2ClientCredentials(
"https://auth.example.com/oauth2/token",
System.getenv("CATALOG_CLIENT_ID"),
System.getenv("CATALOG_CLIENT_SECRET"))
.scope("catalog.read")
.clientSecretBasic()) // isteğe bağlı; varsayılan client_secret_post
.step("Search").get("/catalog?q=shoes").and()
.run().assertErrorRateBelow(0.01);
}
}
- Koşu başına tek token. Tüm sanal kullanıcılar onu paylaşır. Bir simülasyonda, token URL'i, client id'si, scope'u ve client kimlik doğrulaması aynı olan senaryolar tek token'ı paylaşır.
- Yükten önce alınır. İlk token doğrulamadan sonra ve herhangi bir yük isteğinden önce istenir. Gatling'de bu, test JVM'inde, koşu slotundan, koşu dizininden ve alt JVM'den önce olur. Başarısız olursa
run()exception fırlatır ve hiçbir yük isteği gönderilmez. Mesaj, sorgusu ve kullanıcı bilgisi çıkarılmış token URL'ini, HTTP durumunu ve OAuth2errorkodunu içerir; örneğin[LoadTest] OAuth2 token request to https://auth.example.com/oauth2/token failed: HTTP 401 (error=invalid_client). Yanıt gövdesini,error_descriptiondeğerini, client id'yi, secret'ı veya bir token'ı asla içermez. - Arka planda yenilenir. Token, alındıktan
expires_in − min(60 sn, expires_in / 5)sonra, en erken 1 sn sonra yenilenir.expires_inyoksa veya 0 ya da negatifse 3600 sn sayılır. İstekler yenilemeyi hiç beklemez. Başarısız bir yenileme 250 ms'den 2 sn'ye kadar artan aralıklarla yeniden denenir ve eski token gerçekten sona erene kadar kullanılmaya devam eder. - Yenileme başarısız olursa koşu iptal edilir. Gerçek sona erme anından sonra geçerli token kalmazsa koşu, iki motorda da
IllegalStateException(… aborted: OAuth2 token refresh failed for <tokenUrl>: HTTP <code>) ile iptal edilir. Bu durum KO istekler olarak raporlanmaz. - İstek. Token URL'ine
POST; form kodlugrant_type=client_credentials, ayarlandıysascope. Client,client_idveclient_secretdeğerlerini formda gönderir (client_secret_post) veya.clientSecretBasic()sonrasında birAuthorization: Basicbaşlığıyla kimliğini doğrular. Yalnızca boş olmayan biraccess_tokeniçeren HTTP 200 kabul edilir;bearerdışında birtoken_type(büyük/küçük harf fark etmez) reddedilir;refresh_tokenyok sayılır. İstekloadtest.proxy,loadtest.tlsverequestTimeoutSecondsayarlarını kullanır. - Değişken değildir. Token
${access_token}veya başka bir değişken olarak sunulmaz. Alışılmadık bir yerleşim için (özel bir başlık, bir sorgu parametresi) token'ı bir adım veextract(..)ile kendiniz isteyin; bkz. Fluent API sayfasındaki "Çok adımlı akış" örneği. - Metriklerde yer almaz. Token istekleri
LoadTestMetricsiçinde sayılmaz: toplamlarda, adımlarda, SLA'larda ve baseline'larda yoktur. Bunun yerine her koşu tek bir INFO satırı loglar:[LoadTest] '<scenario>': OAuth2 token requests=<n> refreshFailures=<m>. - Yalnızca senaryo düzeyi. OAuth2 client credentials ile
LoadStep.auth(..)çağrısıIllegalArgumentExceptionfırlatır.
fromConfig ile api.auth kullanımı
LoadAuth.fromConfig(name), testfly.yml içindeki api.auth.<name> girdisini kullanır; API testleriyle aynı bloktur (bkz. Kimlik Doğrulama). Yalnızca basic, bearer ve oauth2 tipleri desteklenir; başka bir tip, koşudan önce bu üç tipi listeleyen bir mesajla başarısız olur. Girdi koşu doğrulanırken çözümlenir ve ${ENV} yer tutucuları her zamanki gibi çözümlenir. Hâlâ tam bir ${name} olan Basic veya Bearer değeri, her istekte bir yük testi değişkeni olarak çözümlenir; hâlâ yer tutucu olan bir oauth2 değeri reddedilir.
api:
auth:
catalogService:
type: oauth2
tokenUrl: https://auth.example.com/oauth2/token
clientId: ${CATALOG_CLIENT_ID}
clientSecret: ${CATALOG_CLIENT_SECRET}
loadScenario("Catalog search")
.auth(LoadAuth.fromConfig("catalogService").scope("catalog.read"))
.step("Search").get("/catalog?q=shoes").and()
.run();
api.auth girdisinde scope yoktur; scope'u .scope(..) ile ayarlayın. oauth2 olmayan bir girdide .scope(..) ve .clientSecretBasic() reddedilir. @UseAuth yük testlerine uygulanmaz: fromConfig kullanın.
Yönlendirmeler
Bir adım yönlendirmeyi (redirect) izlediğinde kimlik bilgileri başka bir origin'e asla gönderilmez. Gatling'de aynı origin'e giden bir yönlendirme adımı da kimlik bilgisi olmadan gider; JDK motoru aynı origin'deki adımda onları korur.
Doğrulama
Aşağıdakiler herhangi bir istekten önce IllegalArgumentException ile reddedilir. Mesajlar anahtar ve değişken adlarını verir, hiçbir değeri vermez.
- Basic:
nullkullanıcı adı veya parola, boş kullanıcı adı,:içeren sabit (literal) bir kullanıcı adı ve iki değerden birinde#{(yalnızca Gatling'e özgü sözdizimi;${var}kullanın). - Bearer: boş bir token veya
#{içeren bir token. - OAuth2: mutlak bir
httpveyahttpsURL'i olmayan ya da kullanıcı bilgisi veya fragment içeren bir token URL'i; boş client id veya secret; token URL'i, client id, client secret veya scope içinde${ya da#{(token sanal kullanıcı başına değil, koşu başına bir kez istenir); tek boşlukla ayrılmış RFC 6749 scope token'ları olmayan bir scope; adımda OAuth2. fromConfig:api.authiçinde olmayan bir ad veya desteklenmeyen bir tip.- Bir adımda ikisi birden: kendi auth'u ve bir
Authorizationbaşlığı (ad büyük/küçük harf duyarsız) olan bir adım. - URL'de kimlik bilgisi: herhangi bir auth ayarlıyken kullanıcı bilgisi içeren bir senaryo
baseUrldeğeri (https://user:pass@host).basicAuth(..)kullanın.