Flask登录注册完整实现|密码加密+表单校验+Session管理(附源码)

⚠️ 免责声明 :本文项目仅供技术学习参考,严禁直接作为毕业设计或课程作业提交。请结合自身需求进行二次开发与创新,学术诚信从我做起 🙂


这模块我翻过车

登录注册这模块我第一版写得稀烂,密码明文存的,被导师一眼盯上------这次彻底重写。

做不好,答辩老师上来第一印象就差。

登录注册这条线,我一次讲完:密码怎么加密、前后端双重校验怎么做、Session 登录态怎么管、注册流程怎么防刷。

这几个点过一遍,你的登录注册模块基本就齐了。


技术选型

功能 技术方案 说明
后端框架 Flask 轻量、灵活
登录管理 Flask-Login 官方推荐,成熟稳定
密码加密 Werkzeug generate_password_hash 自带PBKDF2算法,不用自己造轮子
会话管理 Session(服务端) Flask session 客户端签名,配合 Flask-Login
表单校验 WTForms + 前端 JS 前后端各做一次

为什么选 Flask-Login 不用自己写?

因为自己写登录态管理,容易出各种安全问题------Cookie 伪造、会话劫持、CSRF 攻击。Flask-Login 都帮你处理好了,专注业务逻辑就行。


密码加密:绝对不能存明文

这是底线。

存明文密码的项目,答辩直接扣分,老师一句话就给你打回来:"你密码明文存的?"

加密方案

用 Werkzeug 自带的 generate_password_hash 和 check_password_hash。

python 复制代码
from werkzeug.security import generate_password_hash, check_password_hash

# 注册时加密
password_hash = generate_password_hash(password)

# 登录时校验
if check_password_hash(user.password, password):
    # 密码正确

底层用的是 PBKDF2-SHA256,加盐 + 多轮哈希,安全系数足够毕设用。

为什么不用 MD5 / SHA1?

因为已经被破解了------彩虹表几秒就能撞出来。

MD5 早在 2004 年就被王小云教授攻破了,现在还在用 MD5 存密码,等于裸奔。

SHA1 也不安全,2017 年 Google 就演示过碰撞攻击。

毕设最起码得上 PBKDF2 或 bcrypt。

我最早图快用了 MD5,被同学指出来才换,这步千万别省。


注册流程:三重校验

注册这一步,前端校验、后端校验、数据库唯一约束,三层都不能少。

注册页长这样------用户名、密码、手机号、验证码,输入框旁边直接标着校验规则(用户名3-20位、密码至少6位):

第一层:前端校验(用户体验)

用户还没点提交,就告诉他哪里错了,不用等服务器返回。

javascript 复制代码
// 前端注册表单校验
function validateRegisterForm() {
    var username = document.getElementById('username').value;
    var password = document.getElementById('password').value;
    var confirmPwd = document.getElementById('confirm_password').value;
    var email = document.getElementById('email').value;

    // 用户名长度
    if (username.length < 3 || username.length > 20) {
        alert('用户名长度在3-20个字符之间');
        return false;
    }

    // 密码长度
    if (password.length < 6) {
        alert('密码至少6位');
        return false;
    }

    // 两次密码一致
    if (password !== confirmPwd) {
        alert('两次输入的密码不一致');
        return false;
    }

    // 邮箱格式
    var emailReg = /^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$/;
    if (!emailReg.test(email)) {
        alert('邮箱格式不正确');
        return false;
    }

    return true;
}

第二层:后端校验(安全防线)

前端校验是可以绕过的------用户禁用 JS、或者直接用 Postman 调接口,前端就失效了。

所以后端必须再校验一遍,这才是真防线。

python 复制代码
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired, Length, Email, EqualTo, ValidationError
from .models import User

class RegisterForm(FlaskForm):
    username = StringField('用户名', validators=[
        DataRequired('用户名不能为空'),
        Length(min=3, max=20, message='用户名长度3-20个字符')
    ])
    password = PasswordField('密码', validators=[
        DataRequired('密码不能为空'),
        Length(min=6, message='密码至少6位')
    ])
    confirm_password = PasswordField('确认密码', validators=[
        DataRequired('请确认密码'),
        EqualTo('password', message='两次密码不一致')
    ])
    email = StringField('邮箱', validators=[
        DataRequired('邮箱不能为空'),
        Email('邮箱格式不正确')
    ])
    submit = SubmitField('注册')

    # 自定义校验:用户名不能重复
    def validate_username(self, field):
        if User.query.filter_by(username=field.data).first():
            raise ValidationError('用户名已被注册')

    # 自定义校验:邮箱不能重复
    def validate_email(self, field):
        if User.query.filter_by(email=field.data).first():
            raise ValidationError('邮箱已被注册')

第三层:数据库唯一约束(最后兜底)

后端校验和数据库写入之间有时间差------并发注册时,两个请求可能都通过了"用户名未被注册"的校验,然后都去插入数据。

所以数据库层面必须加唯一索引,这是最后一道防线。

sql 复制代码
-- 建表时加上
UNIQUE KEY `uk_username` (`username`),
UNIQUE KEY `uk_email` (`email`)

三层校验的关系:

  • 前端:提升体验,减少无效请求
  • 后端:主要防线,处理大部分情况
  • 数据库:兜底,防止并发极端情况

登录流程:两种方式

登录页长这样------用户名、密码、图形验证码,三个输入项(验证码是防刷用的,后面细讲):

方式一:账号密码登录

python 复制代码
from flask_login import login_user
from werkzeug.security import check_password_hash

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        username = request.form.get('username')
        password = request.form.get('password')

        # 1. 查用户
        user = User.query.filter_by(username=username).first()
        if not user:
            flash('用户名或密码错误', 'error')
            return render_template('login.html')

        # 2. 检查账号状态
        if user.status != 1:
            flash('账号已被禁用,请联系管理员', 'error')
            return render_template('login.html')

        # 3. 校验密码
        if not check_password_hash(user.password, password):
            flash('用户名或密码错误', 'error')
            return render_template('login.html')

        # 4. 登录成功,写入 session
        login_user(user, remember=True)  # remember=True 会写记住我cookie

        # 5. 更新最后登录时间
        user.last_login = datetime.now()
        db.session.commit()

        return redirect(url_for('index'))

    return render_template('login.html')

几个注意点:

  1. 错误信息统一说"用户名或密码错误"------别说"用户名不存在"或"密码不对",分开说等于告诉攻击者哪个是对的,方便他们暴力破解。

  2. 账号状态检查------用户被禁用了就不能登录,毕设里这个功能很加分,说明你考虑了后台管理。

  3. 密码比较用 check_password_hash------别自己解密对比,哈希是单向的,只能用内置方法验证。

方式二:手机号验证码登录(可选加分项)

如果毕设想做得更完善,可以加手机号+短信验证码登录。

接入阿里云 SMS 或腾讯云 SMS,几十行代码搞定。

流程:

  1. 用户输入手机号,点击"获取验证码"
  2. 后端生成6位随机验证码,调短信接口发送,同时存 Redis 或 Session,设置5分钟过期
  3. 用户输入验证码,后端校验,通过则登录

答辩时老师多问两句短信登录,你能顺着讲完整个流程,比只做账密的同学多一块谈资。


登录态管理:Flask-Login 怎么用

配置

python 复制代码
from flask_login import LoginManager

login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'login'  # 未登录时跳转到哪个页面
login_manager.login_message = '请先登录'
login_manager.login_message_category = 'warning'

# 用户加载回调------Flask-Login 需要知道怎么通过ID拿到用户对象
@login_manager.user_loader
def load_user(user_id):
    return User.query.get(int(user_id))

User 模型要实现的方法

User 模型需要继承 UserMixin,它已经帮你实现了 is_authenticated、is_active、is_anonymous、get_id 这四个方法。

python 复制代码
from flask_login import UserMixin
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

class User(db.Model, UserMixin):
    __tablename__ = 'user'
    
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(50), unique=True, nullable=False)
    password = db.Column(db.String(255), nullable=False)
    email = db.Column(db.String(100))
    phone = db.Column(db.String(20))
    nickname = db.Column(db.String(50))
    avatar = db.Column(db.String(255))
    status = db.Column(db.Integer, default=1)
    created_at = db.Column(db.DateTime, default=datetime.now)
    
    # 如果 status 字段控制账号状态,重写 is_active
    @property
    def is_active(self):
        return self.status == 1

重写 is_active 方法,返回 status == 1------这样禁用的用户即使有密码也登不上。

在视图里判断登录状态

python 复制代码
from flask_login import login_required, current_user

@app.route('/cart')
@login_required  # 加了这个装饰器,未登录自动跳登录页
def cart():
    # current_user 就是当前登录用户对象
    user_id = current_user.id
    username = current_user.username
    # ...

模板里也能用:

html 复制代码
{% if current_user.is_authenticated %}
    <span>{{ current_user.nickname }}</span>
    <a href="/logout">退出登录</a>
{% else %}
    <a href="/login">登录</a>
    <a href="/register">注册</a>
{% endif %}

退出登录

一句话的事:

python 复制代码
from flask_login import logout_user

@app.route('/logout')
def logout():
    logout_user()
    flash('已退出登录', 'success')
    return redirect(url_for('login'))

logout_user() 会自动清理 session 和 cookie。


注册防刷:验证码 + 频率限制

毕设虽然不用考虑线上安全,但加上这些功能,答辩能展示你有安全意识。

图形验证码

用 captcha 库生成图形验证码,注册时必须输入正确才能提交。

python 复制代码
from captcha.image import ImageCaptcha
import random
import io

@app.route('/captcha')
def get_captcha():
    # 生成4位随机验证码
    code = ''.join(random.choices('0123456789abcdefghijklmnopqrstuvwxyz', k=4))
    
    # 存到 session
    session['captcha'] = code.lower()
    
    # 生成图片
    image = ImageCaptcha(width=120, height=40)
    img_bytes = image.generate(code)
    
    # 返回图片
    return send_file(io.BytesIO(img_bytes.read()), mimetype='image/png')

注册接口里加校验:

python 复制代码
captcha_input = request.form.get('captcha', '').lower()
if captcha_input != session.get('captcha'):
    flash('验证码错误', 'error')
    return render_template('register.html')

# 验证完清掉,防止重复使用
session.pop('captcha', None)

IP 频率限制

用 flask-limiter 限制同一个IP注册次数,比如一小时最多5次。

python 复制代码
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address

limiter = Limiter(
    app=app,
    key_func=get_remote_address,
    default_limits=[]
)

@app.route('/register', methods=['POST'])
@limiter.limit("5 per hour")  # 每小时最多5次
def register():
    # ...

防刷这块毕设不强制,但加上去有好处。被问到安全措施时,你手里有验证码、有频率限制,不至于干瞪眼。


登录注册模块,老师爱问这些

1. 密码为什么不能明文存?

答:你想啊,库一漏,明文密码全摊在那儿,而且大家习惯一个密码走天下,连累一堆网站。加盐哈希之后呢,漏了也还原不回原文。

2. 前后端校验都要做吗?前端校验能不能省?

答:不能省前端校验(用户体验问题),但前端校验不是安全防线(可以绕过)。后端校验才是真正的安全防线,必须做。数据库唯一约束是兜底,防止并发极端情况。三层各司其职。

答:Cookie 存在客户端浏览器,Session 存在服务端。Cookie 存的是 Session ID,通过 Session ID 去服务端取对应的用户信息。敏感信息不要存 Cookie,因为可以被篡改。Flask 的 session 是签名的客户端 Cookie,虽然不能改,但敏感信息还是建议存服务端。

4. 怎么防止暴力破解密码?

答:几个手段配合:密码错误次数限制(比如5次后锁定15分钟)、图形验证码、IP频率限制、强密码策略(长度+复杂度要求)。

5. Flask-Login 的 remember_me 是什么原理?

答:登录时传 remember=True,Flask-Login 会写一个长期有效的 remember cookie(默认365天),即使 session cookie 过期了,只要 remember cookie 还在,就能自动登录。底层存在 user 表的 remember_token 字段里。


登录注册看起来简单,其实细节很多。

密码加密、前后端校验、会话管理、防刷机制------每一块都有讲究。

登录注册该有的安全点基本都覆盖了------加密、双端校验、登录态、防刷,毕设这个深度够用了。


梅雅达编程笔记原创 · 首发 CSDN · 转载请注明出处

相关推荐
软件技术NINI3 天前
基于JavaWeb 汽车租聘
java·vue.js·spring boot·后端·python·毕业设计·毕设
星辰云-4 天前
基于机器学习的敦煌壁画修复数据分析系统
大数据·人工智能·机器学习·数据分析·毕设·文物修复·壁画修复
星辰云-4 天前
基于Spring Boot+Vue的高校校园二手物品交易平台(论文+源码+演示视频)
毕业设计·课程设计·论文笔记·毕设
Mr.D学长7 天前
毕业设计项目 基于机器视觉的车牌识别系统
python·毕业设计·毕设
Mr.D学长7 天前
毕业设计项目 基于python的答题卡识别评分系统
python·毕业设计·毕设
seconp10 天前
基于Spring Boot和Vue 3的智慧旅游网站:住宿预订、行程规划与千问接入
vue.js·spring boot·毕业设计·软件工程·课程设计·旅游·毕设
程序员阿黄12 天前
月圆人圆:中秋佳节传统文化站(月夜主题重设计版)项目全解析
javascript·html5·毕设
ym hyd 11117 天前
生鲜超市库存管理信息系统源码 Java+SpringBoot+Vue3 前后分离
java·开发语言·vue.js·spring boot·毕设
seconp18 天前
AI 时代怎么做计算机毕业设计?
人工智能·毕业设计·软件工程·课程设计·毕设