feat: add 7 built-in middleware components inspired by Hono.js

New Middleware:
- CORS: Cross-Origin Resource Sharing with full configuration support
- Cookie Helper: Cookie management with signed cookie support (HMAC-SHA256)
- JWT: JSON Web Token authentication (HS256/384/512)
- Bearer Auth: Simple Bearer token authentication
- Compression: Gzip and deflate response compression
- Rate Limiting: Request rate limiting with customizable stores
- Validator: Schema-based request validation

Features:
- Context store for middleware data sharing (get/set/get_client_ip)
- Middleware export module with utility functions
- Comprehensive property-based tests for all middleware
- Example file demonstrating all middleware usage

Files Added:
- cors.v, cookie.v, jwt.v, bearer_auth.v, compress.v, rate_limit.v, validator.v
- middleware.v (unified exports)
- examples/middleware_demo.v
- tests/test_*.v (9 new test files)

Documentation:
- Updated README.md with detailed middleware usage guide
- Added Schema Builder API documentation
This commit is contained in:
meiseayoung
2026-01-04 11:11:15 +08:00
parent 44ba82c194
commit 4e2c6a90c3
23 changed files with 7156 additions and 89 deletions
+137
View File
@@ -0,0 +1,137 @@
module hono
import net.http
// BearerToken 类型 - 支持单 token 或多 token 配置
pub type BearerToken = string | []string
// BearerAuthOptions 结构体 - Bearer Auth 配置选项
pub struct BearerAuthOptions {
pub:
token BearerToken // Token 配置(必需)
realm string // WWW-Authenticate realm
prefix string = 'Bearer' // 认证前缀,默认 "Bearer"
header_name string = 'Authorization' // 请求头名称,默认 "Authorization"
hash_function ?fn (string) string // 哈希函数(用于安全比较)
verify_token ?fn (string, Context) bool // 自定义验证回调
}
// bearer_auth - Bearer Auth 中间件工厂函数
// 返回一个 ContextMiddleware,用于验证 Bearer Token
pub fn bearer_auth(options BearerAuthOptions) ContextMiddleware {
return fn [options] (mut c Context, next fn (mut Context) http.Response) http.Response {
// 获取 Authorization 头
auth_header := c.req.header.get_custom(options.header_name) or {
return unauthorized_response(mut c, options.realm, 'Missing authorization header')
}
if auth_header.len == 0 {
return unauthorized_response(mut c, options.realm, 'Missing authorization header')
}
// 解析 token
token := extract_bearer_token(auth_header, options.prefix) or {
return unauthorized_response(mut c, options.realm, 'Invalid token format')
}
// 验证 token
if !validate_bearer_token(token, options, c) {
return unauthorized_response(mut c, options.realm, 'Invalid token')
}
// 将 token 存储到 Context(便于后续使用)
c.set('bearer_token', token)
// 继续处理请求
return next(mut c)
}
}
// extract_bearer_token - 从 Authorization 头提取 token
fn extract_bearer_token(auth_header string, prefix string) !string {
expected_prefix := '${prefix} '
if !auth_header.starts_with(expected_prefix) {
return error('Invalid authorization format')
}
token := auth_header[expected_prefix.len..].trim_space()
if token.len == 0 {
return error('Empty token')
}
return token
}
// validate_bearer_token - 验证 Bearer Token
fn validate_bearer_token(token string, options BearerAuthOptions, c Context) bool {
// 优先使用自定义验证回调
if verify_fn := options.verify_token {
return verify_fn(token, c)
}
// 使用配置的 token 进行验证
match options.token {
string {
// 单 token 验证
return secure_token_compare(token, options.token, options.hash_function)
}
[]string {
// 多 token 验证
for valid_token in options.token {
if secure_token_compare(token, valid_token, options.hash_function) {
return true
}
}
return false
}
}
}
// secure_token_compare - 安全的 token 比较(防止时序攻击)
fn secure_token_compare(provided string, expected string, hash_fn ?fn (string) string) bool {
// 如果提供了哈希函数,先对 token 进行哈希
if hash_function := hash_fn {
hashed_provided := hash_function(provided)
hashed_expected := hash_function(expected)
return constant_time_compare_bearer(hashed_provided, hashed_expected)
}
// 直接使用常量时间比较
return constant_time_compare_bearer(provided, expected)
}
// constant_time_compare_bearer - 常量时间字符串比较,防止时序攻击
fn constant_time_compare_bearer(a string, b string) bool {
if a.len != b.len {
return false
}
mut result := u8(0)
for i := 0; i < a.len; i++ {
result |= a[i] ^ b[i]
}
return result == 0
}
// unauthorized_response - 返回 401 未授权响应
fn unauthorized_response(mut c Context, realm string, message string) http.Response {
c.status(401)
// 设置 WWW-Authenticate 头
mut www_auth := 'Bearer'
if realm.len > 0 {
www_auth = 'Bearer realm="${realm}"'
}
c.headers['WWW-Authenticate'] = www_auth
return c.json('{"error":"Unauthorized","message":"${message}"}')
}
// get_bearer_token - 从 Context 获取已验证的 Bearer Token
// 这是一个便捷方法,用于在 handler 中获取已验证的 token
pub fn get_bearer_token(c Context) ?string {
return c.get('bearer_token')
}