在for循环中迭代fetchall()字典不起作用。

问题描述 投票:0回答:1

我打算为数据库中的每个用户返回个性化的代币;为了做到这一点,我试着迭代数据库中的用户列表。for 循环,检查当前用户是否在db表中,如果在,则检查当前用户是否在db表中 GET 方法会返回token值。我使用了 postgresqlpsycopg2,我用 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在上面的代码中仍然不满足.我应该如何迭代它们呢.有什么方法可以让这个问题得到解决吗.谢谢。

python postgresql flask psycopg2
1个回答
3
投票

这个循环中的逻辑不工作。

    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")

无论你选择哪种解决方案,得到的启示是,当将数据与数据库中的数据进行匹配时,你要避免从数据库中获取大量的行,并在应用程序中进行匹配。 通过制作一个合适的查询,让数据库来完成工作,通常会快得多。

© www.soinside.com 2019 - 2024. All rights reserved.