Flask 在单元测试中禁用 CSRF

Flask disable CSRF in unittest

在我的项目中 __init__.py 我有这个:

app = Flask(__name__)
app.config.from_object('config')
CsrfProtect(app)
db = SQLAlchemy(app)

我的开发配置文件如下所示:

import os
basedir = os.path.abspath(os.path.dirname(__file__))

DEBUG = True
WTF_CSRF_ENABLED = True
SECRET_KEY = 'supersecretkey'
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'project.db')
SQLALCHEMY_TRACK_MODIFICATIONS = False

在我的单元测试设置中我有这个:

from project import app, db

class ExampleTest(unittest.TestCase):
   def setUp(self):
        app.config['TESTING'] = True
        app.config['WTF_CSRF_ENABLED'] = False
        app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://'
        self.app = app.test_client()
        db.create_all()

理论上,在这里将 WTF_CSRF_ENABLED 设置为 False 应该可以防止单元测试的 CSRF,但是如果我在单元测试时执行 POST,我仍然会遇到 CSRF 错误。我认为这是因为我已经调用了 CsrfProtect(app) 而 WTF_CSRF_ENABLED 是 True (当我导入应用程序时,它被调用)。如果我在配置文件中设置 WTF_CSRF_ENABLED = False,它会按预期工作。

我是否可以在启用 CSRF 后禁用它?还是我找错树了?

查看 csrf_protect 的代码,它会在每次收到请求时检查 app.config['WTF_CSRF_METHODS'] 以查看此请求类型是否应受 CSRF 保护。默认情况下,受保护的方法是:

app.config.setdefault('WTF_CSRF_METHODS', ['POST', 'PUT', 'PATCH'])

因为它实际上每次都会检查 app.config,只需在我的单元测试设置中将其更改为空列表即可解决问题:

from project import app, db

class ExampleTest(unittest.TestCase):
   def setUp(self):
        app.config['TESTING'] = True
        app.config['WTF_CSRF_METHODS'] = []  # This is the magic
        app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://'
        self.app = app.test_client()
        db.create_all()

另外,它确实使用 app.before_request() 注册了 csrf 保护,所以我认为可以通过修改 before request functions 来注销它。但我认为走这条路更有可能在未来的更新中看到问题。

如果可以的话,保留 csrf_token 并不难。我能够使用一些正则表达式并使用 Flask docs 中关于测试的登录功能成功登录到使用 csrf_token 的应用程序。

def login(self, username, password):
    rv = self.client.get('/login')
    m = re.search(b'(<input id="csrf_token" name="csrf_token" type="hidden" value=")([-A-Za-z.0-9]+)', rv.data)

    return self.client.post('/login', data=dict(
        userName=username,
        password=password,
        csrf_token=m.group(2).decode("utf-8")
    ), follow_redirects=True)

所以我在这里所做的是使 csrf_token 成为第二个捕获组的一部分。这可以很容易地用于在整个应用程序中查找令牌。

您可以使用配置变量 WTF_CSRF_ENABLED

禁用它

例如

class TestConfig(Config):
    TESTING = True
    WTF_CSRF_ENABLED = False
    ...

app.config['WTF_CSRF_ENABLED'] = False

另见 flask-WTF documentation