我打算为数据库中的每个用户返回个性化的代币;为了做到这一点,我试着迭代数据库中的用户列表。for
循环,检查当前用户是否在db表中,如果在,则检查当前用户是否在db表中 GET
方法会返回token值。我使用了 postgresql
和 psycopg2
,我用 fetchall()
以获取db表中的所有实例,但我需要迭代每一行并检查它是否在db中。我尝试了for循环,但在终端出现了以下错误。
curl -X GET --header 'Accept: application/json' 'http://127.0.0.1:5000/token?username=tom%20hardy&password=password3'
http://127.0.0.1:5000/token?username=tom%20hardy&password=password3
error message in response body:
{
"message": "Username: tom hardy doesn't exist. You have requested this URI [/token] but did you mean /token ?"
}
it is not true because user in request body is actually in db. 因为请求体中的用户实际上在db中。我假设迭代 fetchall()
dict在下面的代码尝试中不工作,如何使其工作?
postgresql DB:
CREATE TABLE authorized_user_table(
user_id serial PRIMARY KEY,
username VARCHAR (50) UNIQUE NOT NULL,
password VARCHAR (50) NOT NULL
);
insert into authorized_user_table(user_id,username,password) values
(1,'jen hank','password'),
(2,'andy roy','password2'),
(3, 'tom hardy', 'password3'),
(4, 'shah khan', 'password4');
我的后台代码
from flask_restplus import Resource, Api, Namespace
from flask_restplus import abort, fields, inputs, reqparse
from psycopg2 import sql
from flask import Flask, request, jsonify
import psycopg2, json, request
app = Flask(__name__)
api = Api(app)
credential_parser = reqparse.RequestParser()
credential_parser.add_argument('username', type=str)
credential_parser.add_argument('password', type=str)
@api.route('/token')
class Token(Resource):
@api.response(200, 'Successful')
@api.doc(description="Generates a authentication token")
@api.expect(credential_parser, validate=True)
def get(self):
args = credential_parser.parse_args()
username = args.get('username')
password = args.get('password')
cursor = db.cursor()
cursor.execute('SELECT * FROM public.authorized_user_table')
users = cursor.fetchall()
for user in users:
if username != user[1]: ## user[1] gives username
api.abort(404, "Username: {} doesn't exist".format(username))
if password != user[2]:
api.abort(401, "Wrong password")
return {"token": generate_token(username)
def generate_token(self, username):
info = {
'username': username,
'creation_time': time()
}
token = self.serializer.dumps(info)
return token.decode()
if __name__ == '__main__':
db = psycopg2.connect(database='test_db', user='postgres', password='password', host='localhost', port="5432")
app.run(debug=True)
迭代 fetchall()
dict在上面的代码中仍然不满足.我应该如何迭代它们呢.有什么方法可以让这个问题得到解决吗.谢谢。
这个循环中的逻辑不工作。
for user in users:
if username != user[1]: ## user[1] gives username
api.abort(404, "Username: {} doesn't exist".format(username))
if password != user[2]:
api.abort(401, "Wrong password")
我们在遍历所有的用户,但是如果任何一个用户的名字和请求中的名字不一致,我们就会立即返回一个404。同样,如果密码不匹配,我们会立即返回一个401。
这样更好。
...
for user in users:
if username == user[1]: ## user[1] gives username
# Assume user names are unique
# Only check password if the username matches
if password == user[2]:
# FIXME: in the question generate_token appears to be
# a method of this class, but is outside the class
return {"token": self.generate_token(username)}
api.abort(401, "Wrong password")
api.abort(404, "Username: {} doesn't exist".format(username))
不过我们可以通过一个数据库查询来完成这项工作 通过询问数据库是否有一条与用户名匹配的表 和 的密码。
首先,让我们确保没有两个用户可以拥有相同的用户名,通过将它设为 独一无二 的数据库中。
CREATE UNIQUE INDEX idx_unique_user_name ON authorized_user_table (user_name);
现在在Python代码中
...
# Count the rows that match username AND password
cursor.execute("""SELECT COUNT(*) FROM public.authorized_user_table """
"""WHERE user_name = %s AND user_password = %s""",
(username, password))
# COUNT will always return just one row
user = cursor.fetchone()
if user[0] == 1:
return {"token": self.generate_token(username)}
# Don't let an attacker know what they have got right or wrong.
api.abort(401, "Invalid user or password")
上面的变体在发生错误时返回的信息较少。 对于登录处理程序来说,这通常是一个好主意,因为如果攻击者正在猜测用户名和密码,你不想让他们知道他们是否找到了一个有效的用户名。
如果你想让响应区分不正确的用户名和密码,你可以结合这两种方法。
...
cursor.execute("""SELECT user_password FROM public.authorized_user_table """
"""WHERE user_name = %s""",
(username,))
user = cursor.fetchone()
if not user:
api.abort(404, "Username: {} doesn't exist".format(username))
if user[0] == password:
return {"token": self.generate_token(username)}
api.abort(401, "Wrong password")
无论你选择哪种解决方案,得到的启示是,当将数据与数据库中的数据进行匹配时,你要避免从数据库中获取大量的行,并在应用程序中进行匹配。 通过制作一个合适的查询,让数据库来完成工作,通常会快得多。