![]()
一个完整用户系统的后端代码,网上能搜到上万份。但把「为什么这样写」讲清楚的,十不足一。
这篇教程用Flask+MySQL搭了一套带登录、注册、商品展示的全栈应用。代码量不大,却塞进了三层架构、会话管理和数据库交互三种核心模式。对想从调包侠进阶的开发者来说,这是块不错的垫脚石。
三层架构不是炫技,是分工
教程把系统拆成表现层、业务逻辑层、数据访问层。表现层只管渲染模板和收发表单,业务层处理验证和流程控制,数据层只和MySQL打交道。
这种拆法让改需求变得便宜。想换前端框架?动表现层就行。数据库从MySQL迁到PostgreSQL?改数据层的连接配置。各层之间用接口约定,而不是直接掏对方的数据结构。
Flask本身没强制分层,所以新手常犯的错误是把SQL语句直接糊在路由函数里。教程里的products()路由虽然简单,但已经做了示范:cursor的创建、查询、关闭三步走,没让数据库连接泄漏到全局。
会话管理:Flask的secret_key到底防什么
代码里有一行容易被跳过:app.secret_key = 'secret-key'。这串字符是会话签名用的私钥,不是加密用户数据的。
Flask的session默认存客户端cookie里,内容是Base64编码的JSON,任何人都能解码看明文。secret_key的作用是生成HMAC签名,让服务端能识别cookie有没有被篡改。攻击者改了session里的customer_id,签名对不上,服务端直接拒掉。
教程用的'secret-key'是占位符,生产环境得换成32字节以上的随机串。2023年有团队因为把密钥提交到GitHub,被爬虫批量薅走数据库凭证,这类事故在OWASP报告里年年上榜。
登录流程的防御细节
顾客登录路由customer_login做了几件事:POST请求时取表单数据,SQL参数化查询防注入,验证通过后往session写用户ID和姓名,失败则回传错误信息。
参数化查询的%s不是字符串拼接,是MySQLdb的占位符机制。即使用户输入' OR '1'='1,也会被当作普通字符串处理,不会篡改查询语义。这是2008年就成熟的防御手段,但2024年的漏洞库里依然能搜到拼接SQL的新项目。
session里只存了customer_id和customer_name,没存密码哈希。这是正确的——密码验证是一次性的,没必要在内存里留着。有些框架的示例代码会把整个用户对象塞进session,既浪费带宽又增加泄露面。
注册校验的边界情况
注册路由customer_signup的校验逻辑值得细看。先检查四个字段是否全填,再比对密码和确认密码,最后查邮箱是否已存在。三步校验的顺序有讲究:前端该做的格式检查(如邮箱正则)这里没做,因为教程聚焦后端;但后端必须防的重复注册和空值注入,一步没落。
密码比对用明文,教程注释里应该提了生产环境要换bcrypt或Argon2。明文存储是2010年前的做法,现在连CSDN都不再犯这个错。如果这是教学代码,需要加显式警告;如果是项目代码,属于需要立即修复的漏洞。
查重邮箱的SQL用了SELECT id而不是SELECT *,这是个小优化。MySQL的覆盖索引机制下,只查主键可以不走回表,虽然在这个数据量下差别微乎其微,但习惯养成后在大表上能省出数量级的时间。
模板渲染的权限暗示
登录和注册路由都传了role和role_key给模板,说明这套系统可能还有管理员或其他角色。模板层根据这些变量渲染不同的文案和跳转链接,但真正的权限校验应该在路由装饰器或中间件里做。
教程没展示管理员代码,但从结构能推测:如果/admin路由只检查session.get('admin_id'),那伪造session就能越权。完整的方案是每次请求都查数据库验证角色,或者把角色信息写进JWT并设短过期时间。Flask-Login这类扩展封装了这些细节,但理解原生实现有助于调bug。
MySQL连接的隐式坑
配置段把用户名密码写死在代码里,是教程的常见妥协。实际部署至少要用环境变量或密钥管理服务,Docker场景下可以挂secret文件。
更隐蔽的问题是连接池。Flask-MySQLdb每次请求新建连接,小流量没事,并发稍高就会打爆MySQL的max_connections。生产环境该换SQLAlchemy配连接池,或者把Flask跑在Gunicorn多进程模式下分担压力。教程没提这些,但代码结构预留了替换空间——数据层集中在几个路由函数里,不像有些项目把SQL撒得满屏都是。
数据库名noeari看起来是随机打的,这种命名在团队协作时会制造困惑。测试环境用app_test、生产用app_prod是更清晰的约定,配合Flask的app.config.from_envvar可以按环境加载不同配置。
商品列表路由products加了is_available = 1的过滤条件,说明数据库里可能有软删除或下架机制。没展示的是这个字段谁有权修改——如果普通用户能通过某个接口把自己买的商品标为不可用,就属于业务逻辑漏洞。三层架构防不了这种错,得靠测试用例覆盖。
代码里没异常处理。MySQL连接失败会抛OperationalError,直接暴露500页面和堆栈信息。Flask的errorhandler装饰器可以拦截这些异常,返回友好的错误页并记日志。对新手教程来说,省略异常处理是合理的,但上线前必须补上。
session的持久化也没提。默认cookie-based session在浏览器关闭后就消失,要实现「记住我」功能得换服务端存储,比如Redis或数据库表。Flask-Session扩展做了这层封装,但理解cookie的局限性能帮你选对方案。
整个项目的代码风格是教学式的:变量名直白,注释分段清晰,没有抽象过度。比如路由函数里直接操作cursor,没封装成DAO类。这种写法在真实项目里会膨胀,但作为入门材料,让读者一眼看清数据流向比追求架构完美更重要。
如果你跟着敲完这套代码,下一步该问自己:如果把MySQL换成MongoDB,哪些层要动?如果要做API版本控制,URL路由怎么设计?如果用户量涨到十万,session存储怎么扩展?这些问题没有标准答案,但三层架构给了你做决定的坐标系。
教程最后没给部署指南,这是故意的——本地跑通和上线运维之间,还隔着Nginx配置、HTTPS证书、日志轮转、监控告警十条街。那些是另一篇文章的篇幅,也是区分「能写代码」和「能扛系统」的分水岭。
你现在手边有类似的项目吗?是直接把SQL写在视图函数里,还是已经拆出了独立的数据层?
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.