#!/usr/bin/env bash # 测试 Envoy Lua 双 Cookie XSRF + 登录接口 + 限流头 # 用法: # ./test-login-xsrf-rl.sh [BASE_URL] [WARM_PATH] [LOGIN_PATH] [COUNT] [INTERVAL] # 环境变量: # INSECURE=1 # https 自签时跳过校验 # TOKEN_HEADER=xsrf-token # USERNAME=testUser # PASSWORD=string # PHONE=string # REMEMBER=true # true/false # DATA='{"username":"..."}' # 直接提供完整 JSON,将覆盖上面四项 # # 例子: # ./test-login-xsrf-rl.sh http://127.0.0.1:8080 /healthz /api/v1/auth/login 25 0.1 set -euo pipefail BASE_URL="${1:-http://127.0.0.1:8080}" WARM_PATH="${2:-/healthz}" LOGIN_PATH="${3:-/api/v1/auth/login}" COUNT="${4:-20}" INTERVAL="${5:-0.05}" METHOD="POST" CONTENT_TYPE="application/json" TOKEN_HEADER="${TOKEN_HEADER:-xsrf-token}" TOKEN_COOKIE="__Host-XSRF-TOKEN" GUARD_COOKIE="__Host-XSRF-GUARD" USERNAME="${USERNAME:-testUser}" PASSWORD="${PASSWORD:-string}" PHONE="${PHONE:-string}" REMEMBER="${REMEMBER:-true}" INSECURE="${INSECURE:-}" CURL_COMMON=(-sS -L) [[ -n "${INSECURE}" ]] && CURL_COMMON+=(-k) tmpdir="$(mktemp -d)" trap 'rm -rf "$tmpdir"' EXIT hdr="$tmpdir/hdr" echo "# Base=${BASE_URL} Warm=${WARM_PATH} Login=${LOGIN_PATH} Count=${COUNT} Interval=${INTERVAL}s" # 构造请求体(优先使用 DATA;否则用变量拼装) build_data() { if [[ -n "${DATA:-}" ]]; then DATA_PAYLOAD="${DATA}" return fi # 规范化 REMEMBER 为 true/false case "${REMEMBER,,}" in true) REMEMBER=true ;; false) REMEMBER=false ;; *) REMEMBER=true ;; esac if command -v jq >/dev/null 2>&1; then DATA_PAYLOAD="$( jq -n \ --arg username "$USERNAME" \ --arg password "$PASSWORD" \ --arg phone "$PHONE" \ --argjson remember "$REMEMBER" \ '{username:$username,password:$password,phone:$phone,remember:$remember}' )" else # 无 jq 的简易拼装(注意变量里如含双引号会破坏 JSON,生产建议安装 jq) DATA_PAYLOAD=$(cat </dev/null | head -n1 | awk '{print $2}' | tr -d '\r' || true) REMAIN=$(grep -i '^x-ratelimit-remaining:' "$hdr" 2>/dev/null | head -n1 | awk '{print $2}' | tr -d '\r' || true) RESET=$(grep -i '^x-ratelimit-reset:' "$hdr" 2>/dev/null | head -n1 | awk '{print $2}' | tr -d '\r' || true) RLED=$(grep -i '^x-envoy-ratelimited:' "$hdr" 2>/dev/null | head -n1 | awk '{print $2}' | tr -d '\r' || true) printf "limit=%s remaining=%s reset=%s ratelimited=%s" "${LIMIT:--}" "${REMAIN:--}" "${RESET:--}" "${RLED:-no}" } do_request() { local with_header="$1" # yes/no local url="${BASE_URL}${LOGIN_PATH}" rm -f "$hdr" if [[ "$with_header" == "yes" ]]; then CODE=$(curl "${CURL_COMMON[@]}" -X "$METHOD" \ -H "Content-Type: ${CONTENT_TYPE}" \ -H "Cookie: ${COOKIE_HEADER}" \ -H "${TOKEN_HEADER}: ${TOKEN}" \ --data "$DATA_PAYLOAD" \ -D "$hdr" -o /dev/null -w "%{http_code}" \ "$url") else CODE=$(curl "${CURL_COMMON[@]}" -X "$METHOD" \ -H "Content-Type: ${CONTENT_TYPE}" \ -H "Cookie: ${COOKIE_HEADER}" \ --data "$DATA_PAYLOAD" \ -D "$hdr" -o /dev/null -w "%{http_code}" \ "$url") fi printf "%s code=%s " "$(date '+%H:%M:%S')" "${CODE}" extract_rl_headers printf "\n" } # 1) 构造登录请求体 build_data echo "# 登录请求体: ${DATA_PAYLOAD}" # 2) 预热拿 Cookie warm_set_cookies # 3) 负例:不带 xsrf-token 头(应 403) echo "# 负例:不带 XSRF 头,预期 403" do_request "no" # 4) 正例:带齐 Cookie + xsrf-token,循环观察限流头 echo "# 正例:带齐 Cookie 与 XSRF 头,开始压测以观测限流头" for i in $(seq 1 "${COUNT}"); do printf "#%02d " "$i" do_request "yes" sleep "${INTERVAL}" done