Files
core/test/rust/performance/auth/measure.rs
T
2026-11-03 03:32:07 +00:00

298 lines
11 KiB
Rust

// JWT 解析在计时前完成;本场景测 Provider 数据库复核及随后实际角色读取。
use dever_runtime::api::{Request, Response};
struct AuthInput {
service: Arc<auth::Service>,
requests: Vec<Request>,
claims: Vec<Claims>,
now: i64,
}
fn prepare(path: &Path) -> (Arc<dever_runtime::api::Config>, AuthInput) {
let config = Arc::new(dever_runtime::api::Config::load(path, &["admin"]).unwrap());
let service = Arc::new(
auth::Service::new(
config.clone(),
&[auth::Verification {
name: "bench.account.verify",
user_model: AUTH_USER_MODEL,
tenant_model: AUTH_TENANT_MODEL,
}],
&[auth::Route {
topic: "admin",
required: true,
user_models: &[AUTH_USER_MODEL],
tenant_models: &[AUTH_TENANT_MODEL],
}],
)
.unwrap(),
);
let now = auth::now().unwrap();
let claims = (0..17)
.map(|index| Claims {
subject: format!("account-{index:02} 中文\""),
session: format!("session-{index}"),
site: "admin".into(),
tenant: None,
})
.collect::<Vec<_>>();
let requests = claims
.iter()
.map(|claims| {
// 只用公开 Cookie 接口准备凭据,不读取 Secret 的私有字节。
let mut bootstrap = Request::new(Default::default());
*bootstrap.method_mut() = "POST".parse().unwrap();
*bootstrap.uri_mut() = "/login".parse().unwrap();
bootstrap
.headers_mut()
.insert("host", "benchmark.test".parse().unwrap());
bootstrap
.headers_mut()
.insert("origin", "https://benchmark.test".parse().unwrap());
let (mut context, _) = service.prepare("admin", &bootstrap, true, now).unwrap();
context
.issue_cookie(&claims.subject, &claims.session, None, now)
.unwrap();
let mut response = Response::new(Default::default());
context.publish(&mut response);
let cookie = response.headers().get("set-cookie").unwrap();
let cookie = std::str::from_utf8(cookie.as_bytes())
.unwrap()
.split(';')
.next()
.unwrap()
.as_bytes()
.to_vec();
let mut request = Request::new(Default::default());
*request.uri_mut() = "/bench/account/admin/permissions".parse().unwrap();
request
.headers_mut()
.insert("host", "benchmark.test".parse().unwrap());
request
.headers_mut()
.insert("cookie", cookie.try_into().unwrap());
request
})
.collect();
(
config,
AuthInput {
service,
requests,
claims,
now,
},
)
}
fn measure_auth(
runtime: &Runtime,
registry: &Arc<Registry>,
input: &AuthInput,
generated: bool,
roles: bool,
count: usize,
) -> (u128, u64) {
runtime
.run(async |scope| {
let mut contexts = input.requests[..16]
.iter()
.map(|request| {
input
.service
.prepare("admin", request, false, input.now)
.unwrap()
.0
})
.collect::<Vec<_>>();
let mut checksum = 0u64;
let start = Instant::now();
for index in 0..count {
let position = index % contexts.len();
let context = &mut contexts[position];
verify(
generated,
context,
black_box(&input.claims[position]).clone(),
scope,
registry,
)
.await
.map_err(|error| error.0)
.unwrap();
checksum += black_box(
context.user_id(None).unwrap().unwrap() as u64
+ context.id().unwrap().len() as u64,
);
if roles {
let mut database = DbContext::new(registry.clone());
let values =
black_box(permissions(generated, context, scope, &mut database).await);
for value in &values {
for field in value {
checksum += field.bytes().map(u64::from).sum::<u64>();
}
}
}
}
(start.elapsed().as_nanos(), checksum)
})
.unwrap()
}
fn check_auth(runtime: &Runtime, registry: &Arc<Registry>, input: &AuthInput) {
runtime
.run(async |scope| {
let expected = vec![
[
"bench.account.admin.permissions",
"bench",
"account",
"admin",
"permissions",
"GET",
"http",
]
.map(String::from),
];
for generated in [true, false] {
for index in 0..16 {
let (mut context, claims) = input
.service
.prepare("admin", &input.requests[index], false, input.now)
.unwrap();
verify(generated, &mut context, claims.unwrap(), scope, registry)
.await
.map_err(|error| error.0)
.unwrap();
assert_eq!(context.id().unwrap(), input.claims[index].subject);
assert_eq!(
context.user_id(Some(AUTH_USER_MODEL)).unwrap(),
Some(index as i64 + 1)
);
assert!(
context
.tenant_id(Some(AUTH_TENANT_MODEL))
.unwrap()
.is_none()
);
let mut database = DbContext::new(registry.clone());
assert_eq!(
permissions(generated, &mut context, scope, &mut database).await,
expected
);
}
for (index, wrong_session) in [(16, false), (0, true)] {
let (mut context, claims) = input
.service
.prepare("admin", &input.requests[index], false, input.now)
.unwrap();
let mut claims = claims.unwrap();
if wrong_session {
claims.session.push_str("-revoked");
}
assert_eq!(
verify(generated, &mut context, claims, scope, registry)
.await
.unwrap_err()
.0,
401
);
assert!(context.id().is_err());
}
}
})
.unwrap();
}
fn main() {
let (directory, path) = directory(
"auth",
r#""http":{"host":"127.0.0.1","port":18001},"sites":{"admin":{"path":"admin","hosts":["benchmark.test"],"auth":"session","origin":"https://benchmark.test"}},"auth":{"providers":{"session":{"verify":"bench.account.verify","jwtSecret":"benchmark-only-key-not-for-any-deployment","ttlSeconds":3600,"cookie":"session"}}}"#,
);
let runtime = runtime();
let registry = runtime
.run(async |scope| database_registry(&path, scope).await)
.unwrap()
.unwrap();
let (config, input) = prepare(&path);
runtime
.run(async |scope| {
let mut database = DbContext::new(registry.clone());
for (index, claims) in input.claims.iter().enumerate() {
let account = GENERATED_SEED(
&claims.subject,
&claims.session,
index < 16,
scope,
&mut database,
)
.await
.unwrap();
assert_eq!(account.field0.0, index as i64 + 1);
}
let (mut context, claims) = input
.service
.prepare("admin", &input.requests[0], false, input.now)
.unwrap();
drop(database);
api_verify(&mut context, claims.unwrap(), scope, &registry)
.await
.map_err(|error| error.0)
.unwrap();
let mut database = DbContext::new(registry.clone());
let executor = database.on(AUTH_BINDING, scope).await.unwrap();
dever_runtime::authorization::synchronize(
executor,
scope,
&config,
&[dever_runtime::authorization::Declaration {
topic: "admin",
component: "bench",
domain: "account",
action: "permissions",
method: "GET",
}],
)
.await
.unwrap();
dever_runtime::authorization::save(
&mut database,
AUTH_BINDING,
scope,
&context,
"reader",
"Benchmark reader",
false,
&["bench.account.admin.permissions".into()],
)
.await
.unwrap();
for user in 1..=16 {
let executor = database.on(AUTH_BINDING, scope).await.unwrap();
dever_runtime::authorization::grant(executor, scope, &context, user, "reader")
.await
.unwrap();
}
})
.unwrap();
check_auth(&runtime, &registry, &input);
header();
if HTTP_BENCH {
protected_http::run(&runtime, &registry, &config, &input);
} else {
for (name, roles) in [
("auth_provider_verify", false),
("auth_provider_role_read", true),
] {
samples(name, |generated, count| {
measure_auth(&runtime, &registry, &input, generated, roles, count)
});
}
}
check_auth(&runtime, &registry, &input);
drop(registry);
drop(runtime);
drop(directory);
}