游客发表
用Flask写登录注册,录和核心就三件事:路由接请求、注册数据库存用户、录和session记状态。注册别把它想复杂了,录和我见过有人上来就搞JWT、注册OAuth2.0,录和结果连request.form怎么取值都没弄明白。注册先把最朴素的录和版本跑通,再谈扩展。注册
项目结构我习惯这样摆:app.py放主逻辑,录和templates文件夹扔HTML,注册static放CSS。录和数据库用SQLite配SQLAlchemy,注册省得折腾MySQL连接配置。录和先装依赖:
pip install flask flask-sqlalchemy flask-login 模型定义直接了当。User表就四个字段:id、username、password_hash、created_at。密码绝对不能明文存,用werkzeug.security的generate_password_hash和check_password_hash,这两函数是Flask自带的,不用额外装库。
from flask_sqlalchemy import SQLAlchemy from werkzeug.security import generate_password_hash, check_password_hash db = SQLAlchemy() class User(db.Model): id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(80), unique=True, nullable=False) password_hash = db.Column(db.String(200), nullable=False) def set_password(self, pwd): self.password_hash = generate_password_hash(pwd) def check_password(self, pwd): return check_password_hash(self.password_hash, pwd) 注册路由的坑比想象中多。用户提交表单后,先查username是否已存在,再验证两次密码是否一致。这两步顺序有讲究——如果先验证密码一致性,攻击者能通过响应时间差探测用户名是否存在。虽然小项目无所谓,但习惯得养好。
@app.route('/register', methods=['GET', 'POST']) def register(): if request.method == 'POST': username = request.form['username'].strip() password = request.form['password'] confirm = request.form['confirm'] if User.query.filter_by(username=username).first(): flash('用户名已被占用') return redirect(url_for('register')) if password != confirm: flash('两次密码不一致') return redirect(url_for('register')) user = User(username=username) user.set_password(password) db.session.add(user) db.session.commit() return redirect(url_for('login')) return render_template('register.html') 登录逻辑稍微绕一点。验证通过后,把user.id写进session。Flask的session是加密cookie,默认存在客户端,密钥用app.secret_key设置。别用硬编码字符串,从环境变量读。
@app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': username = request.form['username'] password = request.form['password'] user = User.query.filter_by(username=username).first() if user and user.check_password(password): session['user_id'] = user.id return redirect(url_for('dashboard')) flash('用户名或密码错误') return render_template('login.html') 模板部分用Jinja2,base.html里放导航栏和flash消息区块。login.html就一个form,method设post,action留空提交到当前路由。输入框加required属性做前端校验,但后端必须再验一遍——前端校验是给用户省事的,不是给服务器省事的。
<form method="post"> <input type="text" name="username" required> <input type="password" name="password" required> <button type="submit">登录</button> </form> 保护页面用装饰器。自己写一个login_required,检查session.get('user_id'),没有就重定向到登录页。比装flask-login轻量,二十行代码的事。
from functools import wraps def login_required(f): @wraps(f) def decorated(*args, **kwargs): if not session.get('user_id'): return redirect(url_for('login')) return f(*args, **kwargs) return decorated 实际跑起来还有几个细节。CSRF防护得加,用Flask-WTF的CSRFProtect,一行代码全局启用。数据库迁移别手动改表,上Flask-Migrate。密码强度校验放在注册路由里,长度至少8位,别用123456这种。
测试的时候用curl -X POST -d "username=test&password=12345678&confirm=12345678" http://127.0.0.1:5000/register,比浏览器点来点去快。注册完查一下SQLite:sqlite3 instance/app.db "select * from user;",确认密码哈希存进去了。
整套流程跑通大概四十分钟。代码量不到一百行,但覆盖了Web开发最核心的输入验证、状态管理和数据持久化。把这套吃透,后面加邮箱验证、第三方登录都是在这个骨架上长肉。
友情链接