Files
Knowledge/1 使用/使用NGINX的auth_request进行统一jwt鉴权 - 侯锐的思考与分享.md
T

5.6 KiB

title, source, author, published, created, description, tags
title source author published created description tags
使用NGINX的auth_request进行统一jwt鉴权 - 侯锐的思考与分享 https://www.nosuchfield.com/2025/07/24/Using-NGINX-auth_request-for-unified-JWT-authentication/
Raymond
2025-10-30 <p>NGINX的<code>auth_request</code>模块提供了一种统一的认证机制,可以在NGINX层面进行JWT鉴权,而不需要在每个后端服务中重复实现认证逻辑。</p><p>首先我们定义一下nginx的配置,它的配置如下</p><figure class="highlight plaintext"><figcaption><span>flat</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="li
clippings

NGINX的 auth_request 模块提供了一种统一的认证机制,可以在NGINX层面进行JWT鉴权,而不需要在每个后端服务中重复实现认证逻辑。

首先我们定义一下nginx的配置,它的配置如下

flatserver {

    listen 8965;

    # 鉴权接口,仅供 Nginx 内部 auth_request 使用

    location = /auth {

        internal;  # 该接口只能被 Nginx 内部请求,防止外部访问

        # 转发到实际的认证服务

        proxy_pass http://localhost:5001/api/auth/verify;

        # 不转发请求体,提升效率

        proxy_pass_request_body off;

        # 防止后端因 Content-Length 不确定而报错

        proxy_set_header Content-Length "";

        # 将客户端传来的 Authorization 头(JWT Token)传给认证服务

        proxy_set_header Authorization $http_authorization;

        # 传入原始请求路径,供认证服务判断路径是否需要鉴权

        proxy_set_header X-Original-URI $request_uri;

    }

    # 所有 / 路径下的请求都进行认证

    location / {

        # 认证请求会先调用上面的 /auth 接口

        auth_request /auth;

        # 如果认证失败(如返回 401),跳转到自定义处理逻辑

        error_page 401 = @unauthorized;

        # 从 /auth 的响应头中提取用户信息

        auth_request_set $user_id $upstream_http_x_user_id;

        # 把用户信息注入到请求头中,转发给后端业务服务

        proxy_set_header X-User-ID $user_id;

        # 转发到后端服务

        proxy_pass http://localhost:5001;

    }

    # 自定义未授权响应(认证失败时返回)

    location @unauthorized {

        # 返回 401 状态码 + 文本内容

        return 401 "Unauthorized";

    }

}

有了这个nginx的配置之后,我们就可以实现鉴权的逻辑了,具体逻辑如下

flatimport jwt

from flask import Flask, request, make_response, jsonify

from jwt import InvalidTokenError

app = Flask(__name__)

SECRET_KEY = "a-string-secret-at-least-256-bits-long"

# 路径白名单,不需要鉴权的接口

PUBLIC_PATHS = [

    "/api/public/hello",

    "/api/login",

    "/api/register"

]

def verify_token():

    original_uri = request.headers.get("X-Original-URI", "")

    # 不需要鉴权的接口,直接返回200

    if original_uri in PUBLIC_PATHS:

        return "", 200

    # 否则继续验证 JWT

    auth_header = request.headers.get("Authorization")

    if not auth_header or not auth_header.startswith("Bearer "):

        return "Missing or invalid Authorization header", 401

    # 解析得到token

    token = auth_header.split(" ", 1)[1]

    try:

        payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])

        response = make_response("", 200)

        response.headers["X-User-ID"] = str(payload.get("user_id", ""))

        return response

    except InvalidTokenError:

        return "Invalid token", 401

def hello():

    return jsonify({"message": "hello from auth", "user_id": request.headers.get("X-User-ID")})

def ping():

    return {"msg": "hello without auth"}

if __name__ == "__main__":

    app.run(host="0.0.0.0", port=5001)

启动nginx和如上python服务,之后我们使用如下payload和header以及密钥 生成token

payload

{
    "alg": "HS256",
    "typ": "JWT"
}

header

{
    "user_id": 656670838050885
}

密钥

a-string-secret-at-least-256-bits-long

生成得到token

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjo2NTY2NzA4MzgwNTA4ODV9.caQ6cp-BA-OMxXu4zTUjV0OiZo1iygvdi7GPQNjNVHM

之后我们就可以使用token进行测试了,具体测试结果如下

~ AUTH_HEADER="Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjo2NTY2NzA4MzgwNTA4ODV9.caQ6cp-BA-OMxXu4zTUjV0OiZo1iygvdi7GPQNjNVHM"

~ curl -H "$AUTH_HEADER" http://localhost:8965/api/hello
{"message":"hello from auth","user_id":"656670838050885"}

~ curl -H "$AUTH_HEADER" http://localhost:8965/api/public/hello
{"msg":"hello without auth"}

~ curl -H "$AUTH_HEADER" http://localhost:8965/api/login
<!doctype html>
<html lang=en>
<title>404 Not Found</title>
<h1>Not Found</h1>
<p>The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.</p>

~ curl http://localhost:8965/api/public/hello
{"msg":"hello without auth"}

~ curl http://localhost:8965/api/hello
Unauthorized%

如上我们正确设置了Authorization之后就可以正常访问需要鉴权的接口了,但是去掉了Authorization之后需要鉴权的接口就会返回Unauthorized。此外还可以看到,不需要鉴权的接口,即使不添加鉴权配置也是可以正常访问的。