【Flask】使用 werkzeug 安全地处理密码

使用 werkzeug 安全地处理密码

假设我们要实现一个登录注册的功能,最简单的方式是先创建一个表,有 usernamepassword 字段,然后再编写相应的登录和注册接口。

创建表

以下是一个简单的例子,展示了如何创建一个用户表并定义一些基本的操作:

import sqlite3

def create_connection(db_file):
    """ 创建数据库连接 """
    conn = None
    try:
        conn = sqlite3.connect(db_file)
        return conn
    except Exception as e:
        print(e)

    return conn

def create_table(conn):
    """ 创建一个表 """
    try:
        cursor = conn.cursor()
        cursor.execute("""CREATE TABLE IF NOT EXISTS users (
                          id INTEGER PRIMARY KEY,
                          username TEXT NOT NULL,
                          password TEXT NOT NULL
                      );""")
        conn.commit()
    except Exception as e:
        print(e)

def add_user(conn, username, password):
    """ 向用户表中添加一个新用户 """
    try:
        cursor = conn.cursor()
        cursor.execute("INSERT INTO users (username, password) VALUES (?, ?)", (username, password))
        conn.commit()
    except Exception as e:
        print(e)

def main():
    database = "users.db"

    # 创建数据库连接
    conn = create_connection(database)

    # 创建表
    if conn is not None:
        create_table(conn)

        # 添加用户示例
        add_user(conn, 'username1', 'password1')
    else:
        print("无法创建数据库连接。")

if __name__ == '__main__':
    main()

注意,这个示例中使用明文存储密码,这在实际应用中是不安全的。因此,应该使用像 werkzeug.security 中的 generate_password_hashcheck_password_hash 这样的方法来安全地处理密码。

  1. 安装 Werkzeug:
pip install Werkzeug
  1. 修改添加用户函数:使用 generate_password_hash 来存储密码的哈希值,而不是明文密码。
  2. 创建一个验证用户的函数:使用 check_password_hash 来验证提交的密码是否与存储的哈希匹配。
import sqlite3
from werkzeug.security import generate_password_hash, check_password_hash

def create_connection(db_file):
    """ 创建数据库连接 """
    # ... 代码不变 ...

def create_table(conn):
    """ 创建一个表 """
    # ... 代码不变 ...

def add_user(conn, username, password):
    """ 向用户表中添加一个新用户 """
    hashed_password = generate_password_hash(password)
    try:
        cursor = conn.cursor()
        cursor.execute("INSERT INTO users (username, password) VALUES (?, ?)", (username, hashed_password))
        conn.commit()
    except Exception as e:
        print(e)

def verify_user(conn, username, password):
    """ 验证用户凭据 """
    try:
        cursor = conn.cursor()
        cursor.execute("SELECT password FROM users WHERE username = ?", (username,))
        stored_password = cursor.fetchone()
        if stored_password and check_password_hash(stored_password[0], password):
            return True
        else:
            return False
    except Exception as e:
        print(e)
        return False

# main 函数和其他部分的代码不变

接口编写

首先,定义 User 模型

from database import db

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    password = db.Column(db.String(120), nullable=False)

    def to_dict(self):
        return {
            'id': self.id,
            'username': self.username
            # 注意:不要返回密码
        }

然后,在 Flask 应用中添加注册和登录路由处理函数:

from flask import Flask, request, jsonify
from werkzeug.security import generate_password_hash, check_password_hash
# ... 其他导入 ...

# ... Flask 应用的其他部分 ...

@app.route('/api/register', methods=['POST'])
def register():
    data = request.get_json()

    # 确认数据有效性
    username = data.get('username')
    password = data.get('password')
    if not username or not password:
        return jsonify({'message': 'Missing username or password'}), 400

    # 检查用户名是否已存在
    if User.query.filter_by(username=username).first():
        return jsonify({'message': 'Username already exists'}), 400

    # 创建新用户
    hashed_password = generate_password_hash(password)
    new_user = User(username=username, password=hashed_password)
    db.session.add(new_user)
    db.session.commit()

    return jsonify({'message': 'User created successfully', 'user': new_user.to_dict()}), 201

@app.route('/api/login', methods=['POST'])
def login():
    data = request.get_json()

    username = data.get('username')
    password = data.get('password')
    if not username or not password:
        return jsonify({'message': 'Missing username or password'}), 400

    # 检查用户是否存在
    user = User.query.filter_by(username=username).first()
    if user and check_password_hash(user.password, password):
        # 在这里,你应该生成并返回一个认证令牌
        return jsonify({'message': 'Login successful', 'user': user.to_dict()}), 200
    else:
        return jsonify({'message': 'Invalid credentials'}), 401

# ... Flask 应用的其他部分 ...

if __name__ == '__main__':
    app.run(debug=True)

注意

使用 Werkzeug 的 generate_password_hash 函数对密码进行哈希处理后,原始密码将不再可见,因此不再存储明文密码。这是哈希算法的一种基本原则,密码不应该以明文形式存储,以提高安全性。

因此,一旦密码被哈希处理,就不再能够直接查看密码的明文值。验证密码时,您将使用 check_password_hash 函数来比较哈希值,而不是查看明文密码。

当然,我们可以使用 Werkzeug 验证密码:、

from werkzeug.security import generate_password_hash, check_password_hash

# 生成哈希密码
password = "userpassword123"
hashed_password = generate_password_hash(password)

# 检查密码
user_input_password = "userpassword123"  # 用户输入的密码
if check_password_hash(hashed_password, user_input_password):
    print("密码正确")
else:
    print("密码错误")

在上述示例中,generate_password_hash 用于创建哈希密码,而 check_password_hash 用于验证用户提供的密码是否与哈希密码匹配。由于哈希是单向的,所以无法从哈希密码还原出明文密码。

你可能感兴趣的:(flask,安全,数据库)