【发布时间】:2018-04-19 19:19:37
【问题描述】:
在我目前的工作中,我碰巧是一个后端团队的一员,该团队正在创建一个 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='test@example.com', 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 "test@example.com:test"
{
"hello": "world"
}
real 0m0.076s
user 0m0.008s
sys 0m0.006s
令牌认证时机
获取令牌就OK了。
curl -H "Content-Type: application/json" -X POST -d '{"email":"test@example.com","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-login、Flask-WTF...)包装在一起。
- 您知道可能是什么原因吗? (是
Flask-security或Flask-login还是更深层次的东西?) - 似乎对每个请求都运行缓慢的散列算法。
但是,可能没有必要每次都这样做。
只存储令牌并检查传入的令牌是否与存储的令牌相同就足够了。
有没有办法做到这一点(
Flask-security或没有)? - 我可以为应用程序 (
app.config) 设置不同的方式以使其更快(仍然使用令牌身份验证。)? - 是否有解决方法(仍在使用
Flask-security)? - 要我自己写吗?是
Flask-security阻碍了我们吗? - 有人知道这件事吗?
我已将此作为问题交叉发布到 GitHub。
【问题讨论】:
-
问得好,但请不要在此处提及截止日期 - 您的听众几乎完全是志愿者,他们会在闲暇时来到这里。
-
@POLOSTutorials:如果您在问题中看到交叉发布声明,请不要将其删除。交叉发布可能被视为有些不耐烦,但未声明的交叉发布更糟糕,即使第一个版本正确拥有交叉发布,OP 也可能会因此而受到反对。
-
KrysotL:我看不到交叉发布的问题,但请确保您链接回此处,以免浪费将来有帮助的人的时间,他们可能会无意中帮助您在其他地方已经解决的问题上。
-
@halfer 感谢您的编辑。 PythonistaCafe 是一个私人论坛,所以我删除了它。但是,我在打开的 github 问题中添加了交叉帖子。
-
好吧,我们正在远离
flask-security(更多细节在github issue comment)
标签: python authentication flask flask-login flask-security