На моей нынешней работе мне довелось быть частью бэкэнд-команды, которая занимается созданием API. Затем API должен быть передан в приложение JavaScript и должен быть достаточно быстрым (100 мс или около того). Однако это не так.
После некоторого профилирования мы выяснили, что нас сдерживает аутентификация токена в Flask-security
(см. MWE).
MWE
import flask
from flask_security import Security, SQLAlchemyUserDatastore, UserMixin, RoleMixin, auth_required
from flask_sqlalchemy import SQLAlchemy
app = flask.Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/database.sqlite3'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['WTF_CSRF_ENABLED'] = False
app.config['SECURITY_TOKEN_AUTHENTICATION_HEADER'] = 'Authorization'
app.config['SECURITY_PASSWORD_HASH'] = 'pbkdf2_sha512'
app.config['SECURITY_PASSWORD_SALT'] = b'secret'
app.config['SECRET_KEY'] = "super_secret"
db = SQLAlchemy(app)
roles_users = db.Table('roles_users',
db.Column('user_id', db.Integer(), db.ForeignKey('user.id')),
db.Column('role_id', db.Integer(), db.ForeignKey('role.id')))
class Role(db.Model, RoleMixin):
id = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.String(80), unique=True)
description = db.Column(db.String(255))
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(255), unique=True)
password = db.Column(db.String(255))
active = db.Column(db.Boolean())
confirmed_at = db.Column(db.DateTime())
roles = db.relationship('Role', secondary=roles_users,
backref=db.backref('users', lazy='dynamic'))
user_datastore = SQLAlchemyUserDatastore(db, User, Role)
# Setup Flask-Security
security = Security(app, user_datastore)
db.drop_all()
db.create_all()
admin_role = Role(**{'name': 'admin', 'description': 'Admin role'})
db.session.add(admin_role)
db.session.commit()
user_datastore.create_user(email='[email protected]', password='test', active=True, roles=[Role.query.first()])
db.session.commit()
@app.route('/')
@auth_required('basic', 'token')
def hello():
return flask.jsonify({'hello': 'world'})
if __name__ == '__main__':
app.run(debug=True)
Время базовой аутентификации
Тайминги идеальные (менее 100 мс), но это не то, как мы должны это делать.
time curl http://127.0.0.1:5000/ -u "[email protected]:test"
{
"hello": "world"
}
real 0m0.076s
user 0m0.008s
sys 0m0.006s
Время аутентификации токена
Получение токена в порядке.
curl -H "Content-Type: application/json" -X POST -d '{"email":"[email protected]","password":"test"}' http://127.0.0.1:5000/login
{
"meta": {
"code": 200
},
"response": {
"user": {
"authentication_token": "WyIxIiwiJDUkcm91bmRzPTUzNTAwMCRFRUpLRFNONlB2L1hzL2lRJDhMWFZvZlpLMmVoa1BVdWtpRlhUR1lvNEJ3T3FjS3dKMVhVWGlOczRwZDMiXQ.DOLjcQ.oBrT4gr1m49rISyxhaj9Lxu1VNk",
"id": "1"
}
}
}
Но чем запрос ужасно медленный. Тайминги в 20 раз медленнее.
time curl "http://127.0.0.1:5000/?auth_token=WyIxIiwiJDUkcm91bmRzPTUzNTAwMCRFRUpLRFNONlB2L1hzL2lRJDhMWFZvZlpLMmVoa1BVdWtpRlhUR1lvNEJ3T3FjS3dKMVhVWGlOczRwZDMiXQ.DOLjcQ.oBrT4gr1m49rISyxhaj9Lxu1VNk"
{
"hello": "world"
}
real 0m2.371s
user 0m0.005s
sys 0m0.006s
Что с этим???
Я знаю, что Flask-security
объединяет несколько других пакетов безопасности flask (Flask-login
, Flask-WTF
, ...).
- Вы знаете, что может быть причиной? (это
Flask-security
илиFlask-login
или что-то глубже?) - Кажется, что медленный алгоритм хеширования работает для каждого запроса. Однако, возможно, нет необходимости делать это каждый раз. Должно быть достаточно только сохранить токен и проверить, совпадает ли входящий токен с сохраненным. Есть ли способ сделать это так (либо с
Flask-security
, либо без)? - Могу ли я настроить приложение (
app.config
) по-другому, чтобы оно работало быстрее (по-прежнему используя авторизацию по токену)? - Есть ли обходной путь (по-прежнему используется
Flask-security
)? - Я сам напишу? Это
Flask-security
сдерживает нас? - Кто-нибудь знает об этом?
Я опубликовал это как проблему на GitHub.
flask-security
(подробнее в github issue comment< /а>) - person KrysotL   schedule 03.01.2018