当前位置: 首页 > news >正文

多组件 flask 项目

下面创建一个完整的Flask项目,包含flask_login、flask_session、flask_cors和flasgger等组件。这个项目将包含用户认证、API文档、跨域支持等功能。

功能特性

  • 用户认证系统 (Flask-Login)

    • 用户登录/注册
    • 会话管理
    • 登录状态保护
  • 会话管理 (Flask-Session)

    • 文件系统会话存储
    • 会话数据持久化
  • 跨域支持 (Flask-CORS)

    • 支持前后端分离
    • API跨域访问
  • API文档 (Flasgger)

    • Swagger UI界面
    • 自动生成API文档
    • 交互式API测试

完整代码

app.py

from flask import Flask, request, jsonify, session, render_template_string
from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user
from flask_session import Session
from flask_cors import CORS
from flasgger import Swagger, swag_from
from werkzeug.security import generate_password_hash, check_password_hash
import os
from datetime import datetime# 创建Flask应用
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key-here'
app.config['SESSION_TYPE'] = 'filesystem'
app.config['SESSION_PERMANENT'] = False
app.config['SESSION_USE_SIGNER'] = True# 初始化扩展
Session(app)
CORS(app)
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'login'
login_manager.login_message = '请先登录'# Swagger配置
swagger_config = {"headers": [],"specs": [{"endpoint": 'apispec_1',"route": '/apispec_1.json',"rule_filter": lambda rule: True,"model_filter": lambda tag: True,}],"static_url_path": "/flasgger_static","swagger_ui": True,"specs_route": "/docs/"
}swagger_template = {"swagger": "2.0","info": {"title": "Flask练手项目API","description": "一个包含用户认证、会话管理的完整Flask项目","version": "1.0.0"},"host": "localhost:5000","basePath": "/","schemes": ["http"],"securityDefinitions": {"Bearer": {"type": "apiKey","name": "Authorization","in": "header"}}
}swagger = Swagger(app, config=swagger_config, template=swagger_template)# 模拟用户数据库
users_db = {'admin': {'id': 1,'username': 'admin','password_hash': generate_password_hash('admin123'),'email': 'admin@example.com','created_at': datetime.now()},'user1': {'id': 2,'username': 'user1','password_hash': generate_password_hash('user123'),'email': 'user1@example.com','created_at': datetime.now()}
}# 用户类
class User(UserMixin):def __init__(self, user_data):self.id = user_data['id']self.username = user_data['username']self.email = user_data['email']self.password_hash = user_data['password_hash']self.created_at = user_data['created_at']def check_password(self, password):return check_password_hash(self.password_hash, password)def to_dict(self):return {'id': self.id,'username': self.username,'email': self.email,'created_at': self.created_at.isoformat()}@login_manager.user_loader
def load_user(user_id):for username, user_data in users_db.items():if user_data['id'] == int(user_id):return User(user_data)return None# 首页
@app.route('/')
def index():"""首页---tags:- 首页responses:200:description: 返回首页HTML"""html_template = """<!DOCTYPE html><html><head><title>Flask练手项目</title><meta charset="utf-8"><style>body { font-family: Arial, sans-serif; margin: 40px; }.container { max-width: 800px; margin: 0 auto; }.btn { padding: 10px 20px; margin: 5px; text-decoration: none; background: #007bff; color: white; border-radius: 5px; }.btn:hover { background: #0056b3; }.info { background: #f8f9fa; padding: 20px; border-radius: 5px; margin: 20px 0; }</style></head><body><div class="container"><h1>Flask练手项目</h1><div class="info"><h3>项目功能:</h3><ul><li>用户认证系统 (Flask-Login)</li><li>会话管理 (Flask-Session)</li><li>跨域支持 (Flask-CORS)</li><li>API文档 (Flasgger)</li></ul></div>{% if current_user.is_authenticated %}<p>欢迎回来,{{ current_user.username }}!</p><a href="/profile" class="btn">个人资料</a><a href="/logout" class="btn">退出登录</a>{% else %}<p>请登录以访问更多功能</p><a href="/login" class="btn">登录</a><a href="/register" class="btn">注册</a>{% endif %}<div style="margin-top: 30px;"><a href="/docs/" class="btn">API文档</a><a href="/api/users" class="btn">用户列表API</a></div></div></body></html>"""return render_template_string(html_template)# 登录页面
@app.route('/login', methods=['GET', 'POST'])
def login():"""用户登录---tags:- 用户认证parameters:- name: usernamein: formDatatype: stringrequired: truedescription: 用户名- name: passwordin: formDatatype: stringrequired: truedescription: 密码responses:200:description: 登录成功400:description: 登录失败"""login_template = """<!DOCTYPE html><html><head><title>登录</title><meta charset="utf-8"><style>body { font-family: Arial, sans-serif; margin: 40px; }.container { max-width: 400px; margin: 0 auto; }.form-group { margin: 15px 0; }label { display: block; margin-bottom: 5px; }input { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 5px; }.btn { width: 100%; padding: 10px; background: #007bff; color: white; border: none; border-radius: 5px; cursor: pointer; }.btn:hover { background: #0056b3; }.error { color: red; margin: 10px 0; }.info { background: #e7f3ff; padding: 15px; border-radius: 5px; margin: 20px 0; }</style></head><body><div class="container"><h2>用户登录</h2><div class="info"><strong>测试账号:</strong><br>用户名: admin, 密码: admin123<br>用户名: user1, 密码: user123</div>{% if error %}<div class="error">{{ error }}</div>{% endif %}<form method="POST"><div class="form-group"><label>用户名:</label><input type="text" name="username" required></div><div class="form-group"><label>密码:</label><input type="password" name="password" required></div><button type="submit" class="btn">登录</button></form><p style="text-align: center; margin-top: 20px;"><a href="/">返回首页</a> | <a href="/register">注册账号</a></p></div></body></html>"""if request.method == 'POST':if request.is_json:data = request.get_json()username = data.get('username')password = data.get('password')else:username = request.form.get('username')password = request.form.get('password')if not username or not password:if request.is_json:return jsonify({'error': '用户名和密码不能为空'}), 400return render_template_string(login_template, error='用户名和密码不能为空')user_data = users_db.get(username)if user_data and check_password_hash(user_data['password_hash'], password):user = User(user_data)login_user(user)session['user_id'] = user.idif request.is_json:return jsonify({'message': '登录成功','user': user.to_dict()})return render_template_string(success_template, message='登录成功!', redirect_url='/')else:if request.is_json:return jsonify({'error': '用户名或密码错误'}), 400return render_template_string(login_template, error='用户名或密码错误')return render_template_string(login_template)# 注册页面
@app.route('/register', methods=['GET', 'POST'])
def register():"""用户注册---tags:- 用户认证parameters:- name: usernamein: formDatatype: stringrequired: truedescription: 用户名- name: passwordin: formDatatype: stringrequired: truedescription: 密码- name: emailin: formDatatype: stringrequired: truedescription: 邮箱responses:200:description: 注册成功400:description: 注册失败"""register_template = """<!DOCTYPE html><html><head><title>注册</title><meta charset="utf-8"><style>body { font-family: Arial, sans-serif; margin: 40px; }.container { max-width: 400px; margin: 0 auto; }.form-group { margin: 15px 0; }label { display: block; margin-bottom: 5px; }input { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 5px; }.btn { width: 100%; padding: 10px; background: #28a745; color: white; border: none; border-radius: 5px; cursor: pointer; }.btn:hover { background: #218838; }.error { color: red; margin: 10px 0; }</style></head><body><div class="container"><h2>用户注册</h2>{% if error %}<div class="error">{{ error }}</div>{% endif %}<form method="POST"><div class="form-group"><label>用户名:</label><input type="text" name="username" required></div><div class="form-group"><label>邮箱:</label><input type="email" name="email" required></div><div class="form-group"><label>密码:</label><input type="password" name="password" required></div><button type="submit" class="btn">注册</button></form><p style="text-align: center; margin-top: 20px;"><a href="/">返回首页</a> | <a href="/login">已有账号?登录</a></p></div></body></html>"""if request.method == 'POST':if request.is_json:# 处理 JSON 请求data = request.get_json()username = data.get('username')password = data.get('password')email = data.get('email')  # 注册时使用else:# 处理表单请求username = request.form.get('username')password = request.form.get('password')email = request.form.get('email')  # 注册时使用if not all([username, password, email]):error_msg = '所有字段都是必填的'if request.is_json:return jsonify({'error': error_msg}), 400return render_template_string(register_template, error=error_msg)if username in users_db:error_msg = '用户名已存在'if request.is_json:return jsonify({'error': error_msg}), 400return render_template_string(register_template, error=error_msg)# 创建新用户new_user_id = max([user['id'] for user in users_db.values()]) + 1users_db[username] = {'id': new_user_id,'username': username,'password_hash': generate_password_hash(password),'email': email,'created_at': datetime.now()}success_msg = '注册成功!请登录'if request.is_json:return jsonify({'message': success_msg})return render_template_string(success_template, message=success_msg, redirect_url='/login')return render_template_string(register_template)# 退出登录
@app.route('/logout')
@login_required
def logout():"""用户退出登录---tags:- 用户认证responses:200:description: 退出成功"""logout_user()session.clear()if request.is_json:return jsonify({'message': '退出登录成功'})success_template = """<!DOCTYPE html><html><head><title>退出成功</title><meta charset="utf-8"><meta http-equiv="refresh" content="2;url=/"><style>body { font-family: Arial, sans-serif; margin: 40px; text-align: center; }.success { color: green; font-size: 18px; }</style></head><body><div class="success"><h2>退出登录成功!</h2><p>2秒后自动跳转到首页...</p><a href="/">立即跳转</a></div></body></html>"""return render_template_string(success_template)# 个人资料页面
@app.route('/profile')
@login_required
def profile():"""用户个人资料---tags:- 用户信息security:- Bearer: []responses:200:description: 返回用户信息schema:type: objectproperties:id:type: integerusername:type: stringemail:type: stringcreated_at:type: string"""if request.is_json:return jsonify(current_user.to_dict())profile_template = """<!DOCTYPE html><html><head><title>个人资料</title><meta charset="utf-8"><style>body { font-family: Arial, sans-serif; margin: 40px; }.container { max-width: 600px; margin: 0 auto; }.profile-card { background: #f8f9fa; padding: 20px; border-radius: 10px; }.btn { padding: 10px 20px; margin: 5px; text-decoration: none; background: #007bff; color: white; border-radius: 5px; }.btn:hover { background: #0056b3; }</style></head><body><div class="container"><h2>个人资料</h2><div class="profile-card"><p><strong>用户ID:</strong> {{ current_user.id }}</p><p><strong>用户名:</strong> {{ current_user.username }}</p><p><strong>邮箱:</strong> {{ current_user.email }}</p><p><strong>注册时间:</strong> {{ current_user.created_at.strftime('%Y-%m-%d %H:%M:%S') }}</p></div><div style="margin-top: 20px;"><a href="/" class="btn">返回首页</a><a href="/logout" class="btn">退出登录</a></div></div></body></html>"""return render_template_string(profile_template)# API路由
@app.route('/api/users', methods=['GET'])
def get_users():"""获取用户列表---tags:- APIresponses:200:description: 用户列表schema:type: arrayitems:type: objectproperties:id:type: integerusername:type: stringemail:type: stringcreated_at:type: string"""users_list = []for user_data in users_db.values():user = User(user_data)users_list.append(user.to_dict())return jsonify({'users': users_list,'total': len(users_list)})@app.route('/api/session-info')
def session_info():"""获取会话信息---tags:- APIresponses:200:description: 会话信息"""return jsonify({'session_id': session.get('_id', 'No session'),'user_authenticated': current_user.is_authenticated,'user_info': current_user.to_dict() if current_user.is_authenticated else None,'session_data': dict(session)})# 成功页面模板
success_template = """
<!DOCTYPE html>
<html>
<head><title>操作成功</title><meta charset="utf-8"><meta http-equiv="refresh" content="3;url={{ redirect_url }}"><style>body { font-family: Arial, sans-serif; margin: 40px; text-align: center; }.success { color: green; font-size: 18px; }</style>
</head>
<body><div class="success"><h2>{{ message }}</h2><p>3秒后自动跳转...</p><a href="{{ redirect_url }}">立即跳转</a></div>
</body>
</html>
"""if __name__ == '__main__':app.run(debug=True, host='0.0.0.0', port=5000)

流程图

在这里插入图片描述

环境搭建

uv init
uv venv
uv pip install flask flask_login flask_session flask_cors flasgger
uv run app.py

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

http://www.lryc.cn/news/571374.html

相关文章:

  • 数据库新选择?KingbaseES在线体验详解
  • Patch Position Embedding (PPE) 在医疗 AI 中的应用编程分析
  • 工业 AI Agent:智能化转型的核心驱动力
  • 计算机网络学习笔记:TCP流控、拥塞控制
  • taro小程序如何实现新用户引导功能?
  • 【数据结构】图论实战:DAG空间压缩术——42%存储优化实战解析
  • AI大模型初识(一):AI大模型的底层原理与技术演进
  • 数据库系统概论(二十)数据库恢复技术
  • Linux Kernel崩溃分析的法宝:Kdump+Crash(上)
  • 暴雨服务器成功中标洪湖市政府框架采购项目
  • 汽车 CDC威胁分析与风险评估
  • 解锁VSCode:从入门到精通的全攻略
  • ArcGIS Pro无插件加载(无偏移)天地图!一次添加长久使用
  • 【机器人学】2-5.七自由度机器人逆解-SRS型机器人【附MATLAB代码】
  • React19源码系列之Hooks (useEffect、useLayoutEffect、useInsertionEffect)
  • 电阻、电容、电感
  • 单片机 - STM32读取GPIO某一位时为什么不能直接与1判断为高电平?
  • 力扣面试题 17.05. 字母与数字
  • SpringBoot 通过集成 Flink CDC 来实时追踪 MySql 数据变动
  • 基于高性能的光频域反射(OFDR)分布式光纤传感解决方案
  • 爬虫技术:从基础到高级,探索数据抓取的奥秘
  • 深度融合数智化,百胜软件联合华为云加速零售行业转型升级
  • 【Manus第三篇-Prompt优化】两周实战,一套注意力视角的prompt优化框架,真的有用!
  • 【笔记】MSYS2 的 MinGW64 环境中正确安装 Python 相关环境管理工具 (Poetry、Virtualenv、Pipenv 和 UV)
  • 复现 apache HTTPD 换行解析漏洞(CVE-2017-15715)
  • ABP vNext + Sentry + ELK Stack:打造高可用异常跟踪与日志可视化平台
  • STM32的内部RC与外部晶振电路
  • python打卡day52
  • C++ 学习 多线程 2025年6月17日18:41:30
  • 插入排序C语言版