Nginx HTTP认证配置

HTTP Authentication Configuration

概述

HTTP认证是保护Web资源的基本安全机制。Nginx支持多种认证方式,包括基础认证、摘要认证、客户端证书认证等。本文将详细介绍各种HTTP认证的配置方法。

1. 基础认证 (Basic Authentication)

1.1 基本配置

server {
    listen 80;
    server_name secure.example.com;

    location /admin {
        auth_basic "Administrator Area";
        auth_basic_user_file /etc/nginx/.htpasswd;

        root /var/www/admin;
        index index.html;
    }

    location /users {
        auth_basic "User Area";
        auth_basic_user_file /etc/nginx/users.htpasswd;

        root /var/www/users;
        index index.html;
    }
}

1.2 创建密码文件

# 安装htpasswd工具
sudo apt install apache2-utils  # Ubuntu/Debian
sudo yum install httpd-tools    # CentOS/RHEL

# 创建密码文件(第一个用户)
sudo htpasswd -c /etc/nginx/.htpasswd admin

# 添加更多用户
sudo htpasswd /etc/nginx/.htpasswd user1
sudo htpasswd /etc/nginx/.htpasswd user2

# 批量创建用户
#!/bin/bash
# create-users.sh
PASSWD_FILE="/etc/nginx/.htpasswd"
USERS=("admin:admin123" "user1:pass123" "user2:secret456")

for user_pass in "${USERS[@]}"; do
    username=$(echo $user_pass | cut -d: -f1)
    password=$(echo $user_pass | cut -d: -f2)
    echo "$password" | htpasswd -i $PASSWD_FILE $username
done

1.3 高级基础认证配置

http {
    # 定义认证区域映射
    map $request_uri $auth_realm {
        ~^/admin/   "Admin Panel - Restricted Access";
        ~^/api/     "API Access - Authentication Required";
        ~^/reports/ "Reports Section - Authorized Personnel Only";
        default     "Restricted Area";
    }

    map $request_uri $auth_file {
        ~^/admin/   /etc/nginx/auth/admin.htpasswd;
        ~^/api/     /etc/nginx/auth/api.htpasswd;
        ~^/reports/ /etc/nginx/auth/reports.htpasswd;
        default     /etc/nginx/auth/default.htpasswd;
    }

    server {
        listen 80;
        server_name secure.example.com;

        location ~ ^/(admin|api|reports) {
            auth_basic $auth_realm;
            auth_basic_user_file $auth_file;

            # 记录认证尝试
            access_log /var/log/nginx/auth.log auth_format;

            try_files $uri $uri/ =404;
        }
    }

    # 认证日志格式
    log_format auth_format '$remote_addr - $remote_user [$time_local] '
                          '"$request" $status $body_bytes_sent '
                          '"$http_referer" "$http_user_agent" '
                          'auth_realm="$auth_realm"';
}

2. 条件认证

2.1 基于IP的条件认证

server {
    listen 80;
    server_name conditional.example.com;

    location /admin {
        # 内网IP无需认证
        satisfy any;
        allow 192.168.1.0/24;
        allow 10.0.0.0/8;
        deny all;

        # 外网IP需要认证
        auth_basic "Admin Area";
        auth_basic_user_file /etc/nginx/.htpasswd;

        root /var/www/admin;
    }

    location /secure {
        # 必须同时满足IP和认证条件
        satisfy all;
        allow 192.168.1.100;
        allow 192.168.1.101;
        deny all;

        auth_basic "Highly Secure Area";
        auth_basic_user_file /etc/nginx/secure.htpasswd;

        root /var/www/secure;
    }
}

2.2 基于时间的条件认证

http {
    # 定义工作时间
    map $time_local $is_work_hours {
        default 0;
        ~^[0-9]+/[A-Za-z]+/[0-9]+:(09|10|11|12|13|14|15|16|17):[0-9]+:[0-9]+ 1;
    }

    server {
        listen 80;
        server_name timeauth.example.com;

        location /office {
            # 工作时间内需要认证,其他时间拒绝访问
            if ($is_work_hours = 0) {
                return 403 "Access denied outside work hours";
            }

            auth_basic "Office Access";
            auth_basic_user_file /etc/nginx/office.htpasswd;

            root /var/www/office;
        }
    }
}

2.3 基于用户代理的认证

http {
    map $http_user_agent $skip_auth {
        default 1;
        ~*bot 0;           # 机器人需要认证
        ~*curl 0;          # curl需要认证
        ~*wget 0;          # wget需要认证
        ~*scanner 0;       # 扫描工具需要认证
    }

    server {
        listen 80;
        server_name uaauth.example.com;

        location /api {
            if ($skip_auth = 0) {
                auth_basic "API Access";
                auth_basic_user_file /etc/nginx/api.htpasswd;
            }

            proxy_pass http://backend_api;
        }
    }
}

3. 客户端证书认证

3.1 SSL客户端证书配置

server {
    listen 443 ssl;
    server_name clientcert.example.com;

    # 服务器证书
    ssl_certificate /etc/ssl/certs/server.crt;
    ssl_certificate_key /etc/ssl/private/server.key;

    # 客户端证书验证
    ssl_client_certificate /etc/ssl/certs/ca.crt;
    ssl_verify_client on;
    ssl_verify_depth 2;

    location / {
        # 客户端证书信息传递给后端
        proxy_set_header X-SSL-Client-Verify $ssl_client_verify;
        proxy_set_header X-SSL-Client-DN $ssl_client_s_dn;
        proxy_set_header X-SSL-Client-CN $ssl_client_s_dn_cn;
        proxy_set_header X-SSL-Issuer $ssl_client_i_dn;
        proxy_set_header X-SSL-Client-Serial $ssl_client_serial;
        proxy_set_header X-SSL-Client-Fingerprint $ssl_client_fingerprint;

        proxy_pass http://secure_backend;
    }

    # 证书信息展示页面
    location /cert-info {
        return 200 "Client Certificate Information:
Subject: $ssl_client_s_dn
Issuer: $ssl_client_i_dn
Serial: $ssl_client_serial
Fingerprint: $ssl_client_fingerprint
Verify: $ssl_client_verify
";
        add_header Content-Type text/plain;
    }
}

3.2 可选客户端证书认证

server {
    listen 443 ssl;
    server_name optional-cert.example.com;

    ssl_certificate /etc/ssl/certs/server.crt;
    ssl_certificate_key /etc/ssl/private/server.key;
    ssl_client_certificate /etc/ssl/certs/ca.crt;
    ssl_verify_client optional;

    location / {
        # 根据证书验证结果进行不同处理
        if ($ssl_client_verify != SUCCESS) {
            return 301 https://$server_name/login;
        }

        root /var/www/secure;
    }

    location /login {
        # 证书验证失败时的登录页面
        auth_basic "Login Required";
        auth_basic_user_file /etc/nginx/.htpasswd;

        root /var/www/login;
    }

    location /public {
        # 公开区域,无需证书
        root /var/www/public;
    }
}

4. 外部认证集成

4.1 LDAP认证集成

# 需要安装nginx-auth-ldap模块
server {
    listen 80;
    server_name ldap.example.com;

    location /ldap-auth {
        auth_ldap "LDAP Authentication";
        auth_ldap_servers ldap_server;

        root /var/www/ldap-protected;
    }
}

# LDAP服务器配置
ldap_server ldap_server {
    url ldap://ldap.company.com:389/DC=company,DC=com?sAMAccountName?sub?(objectClass=person);
    binddn "CN=nginx,CN=Users,DC=company,DC=com";
    binddn_passwd "password123";
    group_attribute member;
    group_attribute_is_dn on;
    require group "CN=WebUsers,CN=Groups,DC=company,DC=com";
    require valid_user;
    satisfy all;
}

4.2 OAuth2/JWT认证

# 使用lua脚本进行JWT验证
location /api {
    access_by_lua_block {
        local jwt = require "resty.jwt"
        local secret = "your-secret-key"

        -- 获取Authorization头
        local auth_header = ngx.var.http_authorization
        if not auth_header then
            ngx.status = 401
            ngx.header.content_type = "application/json"
            ngx.say('{"error":"Missing Authorization header"}')
            ngx.exit(401)
        end

        -- 提取JWT token
        local token = string.match(auth_header, "Bearer%s+(.+)")
        if not token then
            ngx.status = 401
            ngx.header.content_type = "application/json"
            ngx.say('{"error":"Invalid Authorization format"}')
            ngx.exit(401)
        end

        -- 验证JWT
        local jwt_obj = jwt:verify(secret, token)
        if not jwt_obj.valid then
            ngx.status = 401
            ngx.header.content_type = "application/json"
            ngx.say('{"error":"Invalid token"}')
            ngx.exit(401)
        end

        -- 检查权限
        if not jwt_obj.payload.scope or 
           not string.find(jwt_obj.payload.scope, "api:read") then
            ngx.status = 403
            ngx.header.content_type = "application/json"
            ngx.say('{"error":"Insufficient permissions"}')
            ngx.exit(403)
        end

        -- 传递用户信息到后端
        ngx.req.set_header("X-User-ID", jwt_obj.payload.sub)
        ngx.req.set_header("X-User-Role", jwt_obj.payload.role)
        ngx.req.set_header("X-User-Scope", jwt_obj.payload.scope)
    }

    proxy_pass http://api_backend;
}

4.3 外部认证服务

# 使用auth_request模块
location /external-auth {
    auth_request /auth;

    # 传递认证结果
    auth_request_set $user $upstream_http_x_user;
    auth_request_set $role $upstream_http_x_role;

    proxy_set_header X-User $user;
    proxy_set_header X-Role $role;
    proxy_pass http://protected_service;
}

# 内部认证检查
location = /auth {
    internal;
    proxy_pass http://auth_service/verify;
    proxy_pass_request_body off;
    proxy_set_header Content-Length "";
    proxy_set_header X-Original-URI $request_uri;
    proxy_set_header X-Original-Method $request_method;
    proxy_set_header X-Real-IP $remote_addr;
}

# 认证失败重定向
error_page 401 = @error401;

location @error401 {
    return 302 https://auth.example.com/login?redirect=$request_uri;
}

5. 多因素认证

5.1 组合认证策略

server {
    listen 443 ssl;
    server_name mfa.example.com;

    ssl_certificate /etc/ssl/certs/server.crt;
    ssl_certificate_key /etc/ssl/private/server.key;
    ssl_client_certificate /etc/ssl/certs/ca.crt;
    ssl_verify_client optional;

    location /high-security {
        # 多层认证:客户端证书 + 基础认证
        if ($ssl_client_verify != SUCCESS) {
            return 401 "Client certificate required";
        }

        auth_basic "Additional Authentication Required";
        auth_basic_user_file /etc/nginx/mfa.htpasswd;

        # IP白名单
        allow 192.168.1.0/24;
        deny all;

        # 时间限制
        if ($time_local !~ "^[0-9]+/[A-Za-z]+/[0-9]+:(09|10|11|12|13|14|15|16|17):[0-9]+:[0-9]+") {
            return 403 "Access denied outside business hours";
        }

        root /var/www/high-security;
    }
}

5.2 基于风险的认证

http {
    # 定义可疑活动检测
    map $remote_addr $is_suspicious_ip {
        default 0;
        include /etc/nginx/suspicious_ips.map;
    }

    map $http_user_agent $is_suspicious_ua {
        default 0;
        ~*bot 1;
        ~*scanner 1;
        ~*curl 1;
    }

    # GeoIP检测
    geoip_country /usr/share/GeoIP/GeoIP.dat;
    map $geoip_country_code $is_high_risk_country {
        default 0;
        CN 1;  # 示例:某些国家需要额外验证
        RU 1;
        KP 1;
    }

    server {
        listen 80;
        server_name risk-based.example.com;

        location /sensitive {
            # 风险评估
            set $risk_score 0;

            if ($is_suspicious_ip) {
                set $risk_score 1;
            }

            if ($is_suspicious_ua) {
                set $risk_score 2;
            }

            if ($is_high_risk_country) {
                set $risk_score 3;
            }

            # 根据风险等级要求不同认证
            if ($risk_score = 0) {
                # 低风险:无需认证
                root /var/www/sensitive;
                break;
            }

            if ($risk_score = 1) {
                # 中风险:基础认证
                auth_basic "Medium Risk - Authentication Required";
                auth_basic_user_file /etc/nginx/.htpasswd;
            }

            if ($risk_score >= 2) {
                # 高风险:强认证或拒绝
                return 403 "High risk access denied";
            }

            root /var/www/sensitive;
        }
    }
}

6. 认证性能优化

6.1 认证缓存

# 使用auth_request缓存
proxy_cache_path /var/cache/nginx/auth 
                 levels=1:2 keys_zone=auth_cache:10m 
                 inactive=60m max_size=100m;

location = /auth-cached {
    internal;
    proxy_cache auth_cache;
    proxy_cache_key "$remote_addr$http_authorization";
    proxy_cache_valid 200 5m;
    proxy_cache_valid 401 403 1m;

    proxy_pass http://auth_service/verify;
    proxy_pass_request_body off;
    proxy_set_header Content-Length "";
}

location /protected-cached {
    auth_request /auth-cached;

    proxy_pass http://backend;
}

6.2 认证会话管理

# 使用Redis进行会话管理
location /session-auth {
    access_by_lua_block {
        local redis = require "resty.redis"
        local red = redis:new()
        red:connect("127.0.0.1", 6379)

        -- 检查会话
        local session_id = ngx.var.cookie_session_id
        if session_id then
            local user_info = red:get("session:" .. session_id)
            if user_info and user_info ~= ngx.null then
                -- 会话有效,更新过期时间
                red:expire("session:" .. session_id, 3600)
                ngx.req.set_header("X-User-Info", user_info)
                return
            end
        end

        -- 会话无效,要求认证
        ngx.status = 401
        ngx.header.www_authenticate = 'Basic realm="Session Required"'
        ngx.exit(401)
    }

    proxy_pass http://backend;
}

7. 安全最佳实践

7.1 防暴力破解

# 限制认证尝试次数
limit_req_zone $binary_remote_addr zone=auth_limit:10m rate=1r/m;

server {
    listen 80;
    server_name secure.example.com;

    location /admin {
        limit_req zone=auth_limit burst=3 nodelay;

        auth_basic "Admin Login";
        auth_basic_user_file /etc/nginx/.htpasswd;

        # 记录失败的认证尝试
        error_page 401 = @auth_failed;

        root /var/www/admin;
    }

    location @auth_failed {
        access_log /var/log/nginx/auth_failures.log;
        return 401 "Authentication failed";
    }
}

7.2 密码文件安全

# 安全设置密码文件
#!/bin/bash
# secure-passwd-file.sh

PASSWD_FILE="/etc/nginx/.htpasswd"

# 设置正确的权限
chmod 600 $PASSWD_FILE
chown root:nginx $PASSWD_FILE

# 定期轮换密码
rotate_passwords() {
    backup_file="${PASSWD_FILE}.$(date +%Y%m%d)"
    cp $PASSWD_FILE $backup_file

    # 生成新密码并更新
    new_password=$(openssl rand -base64 12)
    echo "New password for admin: $new_password"
    echo "$new_password" | htpasswd -i $PASSWD_FILE admin
}

# 监控密码文件变化
inotifywait -m -e modify $PASSWD_FILE --format '%w %e %T' --timefmt '%Y-%m-%d %H:%M:%S' |
while read file event time; do
    echo "[$time] Password file modified: $file $event" >> /var/log/nginx/passwd_changes.log
done

8. 故障排除

8.1 常见认证问题

# 诊断认证问题的脚本
#!/bin/bash
# auth-troubleshoot.sh

echo "=== Nginx认证故障排除 ==="

# 检查密码文件
echo "1. 检查密码文件:"
if [ -f "/etc/nginx/.htpasswd" ]; then
    echo "   ✓ 密码文件存在"
    echo "   权限: $(ls -l /etc/nginx/.htpasswd)"
    echo "   用户数: $(wc -l < /etc/nginx/.htpasswd)"
else
    echo "   ✗ 密码文件不存在"
fi

# 检查配置语法
echo "2. 检查Nginx配置:"
if nginx -t &>/dev/null; then
    echo "   ✓ 配置语法正确"
else
    echo "   ✗ 配置语法错误:"
    nginx -t
fi

# 检查认证模块
echo "3. 检查认证模块:"
nginx -V 2>&1 | grep -q "http_auth_basic_module" && echo "   ✓ Basic认证模块已启用" || echo "   ✗ Basic认证模块未启用"

# 测试认证
echo "4. 测试认证:"
response=$(curl -s -o /dev/null -w "%{http_code}" http://localhost/admin)
if [ "$response" = "401" ]; then
    echo "   ✓ 认证保护正常工作"
else
    echo "   ✗ 认证保护可能未生效 (状态码: $response)"
fi

# 检查日志
echo "5. 检查最近的认证日志:"
tail -5 /var/log/nginx/error.log | grep -i auth

8.2 性能监控

# 认证性能监控脚本
#!/bin/bash
# auth-performance-monitor.sh

while true; do
    echo "=== $(date) ==="

    # 统计认证请求
    auth_requests=$(tail -1000 /var/log/nginx/access.log | grep -c "401")
    successful_auth=$(tail -1000 /var/log/nginx/access.log | grep -c "200.*auth")

    echo "认证请求数: $auth_requests"
    echo "成功认证数: $successful_auth"

    if [ "$auth_requests" -gt 0 ]; then
        success_rate=$(echo "scale=2; $successful_auth * 100 / $auth_requests" | bc)
        echo "认证成功率: ${success_rate}%"
    fi

    # 检查认证延迟
    avg_response_time=$(tail -1000 /var/log/nginx/access.log | 
                       awk '$9==401 {sum+=$NF; count++} END {print sum/count}')
    echo "平均认证响应时间: ${avg_response_time}s"

    sleep 60
done

小结

通过本文学习,你应该掌握:

  1. 基础HTTP认证的配置和管理
  2. 条件认证和多因素认证策略
  3. 客户端证书认证的实现
  4. 外部认证系统集成方法
  5. 认证性能优化技巧
  6. 安全最佳实践和防护措施
  7. 常见认证问题的诊断和解决

下一篇文章将介绍Nginx的缓存配置与优化。

powered by Gitbook© 2025 编外计划 | 最后修改: 2025-08-29 15:40:15

results matching ""

    No results matching ""