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
在我的项目中 __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