如何防止使用nginx的auth_request指令和flask应用程序在重新验证提示中出现随机性?

2024-04-20 06:35:30 发布

您现在位置:Python中文网/ 问答频道 /正文

设置:

系统应如下所示:

  • 有两个AWS负载平衡器,每个都运行一个Docker容器。一个是私有的loadbalancer,它包含一个应该受到保护以防止未经身份验证的访问的任意服务;另一个是面向公共的loadbalancer,它包含身份验证门户。
    • Docker容器内(在公共负载平衡器上)有一个NginX服务器监听端口80,一个Flask应用程序在端口8000上服务。在
    • NginX使用auth_request指令向Flask应用程序发出一个子请求(将凭证作为请求的头传递),期望响应200或401。
      • Flask应用程序从请求中提取凭证并检查该用户名是否已经是flask.session的一部分。如果是这样,它将立即返回200个响应;否则,它将尝试根据外部真实源进行身份验证。如果成功,则将该用户添加到flask.session,应用程序将返回200响应;否则,它将返回401响应。在
      • 还存在过期检查;例如,如果用户登录超过一小时,应用程序应该将其从flask.session中删除并返回401响应。然后用户可以发出新的登录请求。在
    • 如果响应为200,则流量将路由到专用负载平衡器。在

用户的浏览器应该缓存他们提交的凭据,这样每个新请求都应该能够立即看到用户是flask.session的一部分,并避免进行新的身份验证尝试。在

问题是:

似乎是随机的,在刷新或导航受保护的资源时(在成功的身份验证之后),有时会出现身份验证弹出窗口,要求用户再次进行身份验证。它们可以提交,在再次提示重新验证之前,将加载单个资源。在

示例:

受保护的资源是一个静态网站,由一个索引页、一个CSS文件和三个图像组成。初始身份验证后,用户刷新页面多次。其中一次,将触发身份验证提示。他们将再次输入凭据,然后加载索引页。他们将再次提示输入CSS文件,然后再次提示每个图像。

代码

我不确定要在这里链接多少东西才能解决这个问题,所以我先从nginx文件开始,nginx文件负责生成auth_request子请求和随后的路由,以及两个python文件,它们负责发出auth请求和处理会话。在

nginx.default

server {
  listen 80;
  server_name _;

  location / {
    auth_request /auth;
    proxy_pass {{resource_endpoint}};
  }

  location /auth {
    proxy_pass {{auth_backend}};
    proxy_pass_request_body off;
    proxy_set_header Content-Length "";
    proxy_set_header X-Original-URI $request_uri;
  }

  location /logout {
    proxy_pass {{auth_backend}};
    proxy_pass_request_body off;
    proxy_set_header Content-Length "";
    proxy_set_header X-Original-URI $request_uri;
  }
}

app.py

^{pr2}$

auth0.py

import json
import requests
import flask
import datetime
import os
from functools import wraps


def check_auth(username, password):
    if 'username' in flask.session:
        import pdb; pdb.set_trace()
        if 'logged_in' in flask.session:
            now = datetime.datetime.now()
            expiry_window = datetime.timedelta(
                minutes=int(os.getenv('AUTH0_EXPIRY'))
            )

            if flask.session['logged_in'] >= now - expiry_window:
                return True
            else:
                flask.session.pop('username', None)

    data = {
        'client_id': os.getenv("AUTH0_CLIENT_ID"),
        'username': username,
        'password': password,
        'id_token': '',
        'connection': os.getenv("AUTH0_CONNECTION"),
        'grant_type': 'password',
        'scope': 'openid',
        'device': ''
    }

    resp = requests.post(
        url="https://" + os.getenv('AUTH0_DOMAIN') + "/oauth/ro",
        data=json.dumps(data),
        headers={"Content-type": "application/json"}
    )

    if 'error' in json.loads(resp.text):
        return False
    else:
        flask.session['username'] = username
        flask.session['logged_in'] = datetime.datetime.now()
        return True


def authenticate():
    return flask.Response(
        'Could not verify your access level for that URL.\n'
        'You have to login with proper credentials', 401,
        {'WWW-Authenticate': 'Basic realm="Login Required"'},
    )


def requires_auth(f):    
    @wraps(f)
    def decorated(*args, **kwargs):
        auth = flask.request.authorization
        if not auth or not check_auth(auth.username, auth.password):
            return authenticate()
        return f(*args, **kwargs)
    return decorated

Tags: 文件用户inimportauth身份验证应用程序flask