在Python项目中对接MySQL做登录校验时,核心目标是根据提交的用户名和密码,查出库中是否存在匹配的记录,且整个过程不能被SQL注入,也不能明文保存密码。下面先了解基础写法与风险点。

为什么不能直接拼接SQL
如果把用户名和密码直接拼进SQL字符串,攻击者可在用户名框输入特殊字符改变语义。例如拼接后变成永远成立的条件,从而绕过密码验证。因此必须使用参数化查询。
使用pymysql进行参数化查询
以下示例展示如何用占位符%s传递参数,由驱动层做转义,避免注入。密码字段在库中存的是bcrypt哈希值,登录时用checkpw比对。
import pymysql
import bcrypt
def login_query(username, password):
conn = pymysql.connect(
host='127.0.0.1',
user='app_user',
password='db_pass',
database='test_db',
charset='utf8mb4'
)
try:
with conn.cursor() as cursor:
# 先按用户名查哈希,不直接在SQL里比密码
sql = "SELECT id, password_hash FROM users WHERE username = %s"
cursor.execute(sql, (username,))
row = cursor.fetchone()
if not row:
return False
user_id, pwd_hash = row
# 用bcrypt校验密码
if bcrypt.checkpw(password.encode('utf-8'), pwd_hash.encode('utf-8')):
return user_id
return False
finally:
conn.close()
# 调用示例
# uid = login_query('alice', 'secret123')
使用mysql-connector的写法
若使用官方连接器,语法类似,占位符同样是%s,不要自己格式化字符串。
import mysql.connector
import bcrypt
def safe_login(user, pwd):
cnx = mysql.connector.connect(
host='192.168.0.1',
user='app',
password='pwd',
database='app'
)
cur = cnx.cursor()
q = "SELECT password_hash FROM accounts WHERE username = %s"
cur.execute(q, (user,))
rec = cur.fetchone()
cur.close()
cnx.close()
if rec and bcrypt.checkpw(pwd.encode(), rec[0].encode()):
return True
return False
表结构与哈希存储要点
用户表不应有明文密码列,推荐结构如下:
| 字段 | 类型 | 说明 |
|---|---|---|
| id | INT | 主键 |
| username | VARCHAR(50) | 唯一索引 |
| password_hash | VARCHAR(100) | bcrypt生成的哈希 |
注册时可用bcrypt.hashpw()生成哈希再写入,登录时只做校验,不反向解密。
常见错误与排查
- 在Python里写"SELECT * FROM users WHERE name='" + name + "'"属于高危写法
- 把密码用md5无盐处理仍易被彩虹表破解,应使用bcrypt或argon2
- 忘记对输入做encode导致bcrypt类型报错
安全查询的原则是:参数化防注入,哈希防泄露,应用层只比哈希不碰明文。
小结
在Python中安全查询MySQL匹配用户记录,关键是用驱动参数化语句查用户名,取回哈希后用专用库校验密码。这样既能准确判定登录,也符合基础安全规范。