# 创建Web蓝图 from flask import Blueprint, render_template, request, redirect, url_for, session, flash, jsonify, current_app from flask_login import login_user, logout_user, login_required, current_user from app.models.admin import Admin from app import db web_bp = Blueprint('web', __name__) user_bp = Blueprint('user', __name__, url_prefix='/index') @web_bp.route('/') def index(): """首页 - 重定向到前端主页""" return redirect(url_for('user.user_index')) @web_bp.route('/login', methods=['GET', 'POST']) def login(): """登录页面""" try: if request.method == 'POST': current_app.logger.info(f"收到登录请求 - IP: {request.remote_addr}, User-Agent: {request.headers.get('User-Agent')}") # 检查CSRF令牌 - 至少验证请求来源 csrf_token = request.form.get('csrf_token') origin = request.headers.get('Origin') referer = request.headers.get('Referer') x_requested_with = request.headers.get('X-Requested-With') # 如果缺少CSRF令牌,至少验证请求来源 if not csrf_token: current_app.logger.warning(f"登录请求缺少CSRF令牌 - IP: {request.remote_addr}") # 对于AJAX请求,至少检查X-Requested-With头 if request.headers.get('X-Requested-With') == 'XMLHttpRequest': # AJAX请求必须来自同源或可信源 if not origin and not referer: current_app.logger.warning("可疑的AJAX请求:缺少Origin和Referer头") return jsonify({ 'success': False, 'message': '请求验证失败,请刷新页面后重试' }), 400 # 对于表单提交,检查Referer elif not referer: current_app.logger.warning("可疑的表单提交:缺少Referer头") flash('请求验证失败,请刷新页面后重试', 'error') return render_template('login.html') username = request.form.get('username', '').strip() password = request.form.get('password', '').strip() current_app.logger.info(f"登录尝试 - 用户名: {username}, 来源IP: {request.remote_addr}") if not username or not password: # 对于AJAX请求,返回JSON错误 if request.headers.get('X-Requested-With') == 'XMLHttpRequest': return jsonify({ 'success': False, 'message': '请输入用户名和密码' }), 400 # 对于普通表单提交,使用flash消息 flash('请输入用户名和密码', 'error') return render_template('login.html') try: # 查找用户(排除已软删除的用户) admin = Admin.query.filter_by(username=username, is_deleted=0).first() # 验证用户和密码 if not admin: current_app.logger.warning(f"登录失败 - 用户不存在: {username}") failure_message = '用户名或密码错误' elif not admin.is_active: current_app.logger.warning(f"登录失败 - 账号未激活: {username}") failure_message = '账号未激活' else: # 验证密码 password_valid = admin.check_password(password) if password_valid: # 登录成功 current_app.logger.info(f"登录成功 - 用户名: {username}, IP: {request.remote_addr}") login_user(admin, remember=True) # 更新最后登录信息 try: admin.update_last_login(request.remote_addr) except Exception as e: current_app.logger.error(f"更新最后登录信息失败: {str(e)}") # 如果是AJAX请求,返回JSON if request.headers.get('X-Requested-With') == 'XMLHttpRequest': import secrets token = secrets.token_urlsafe(32) return jsonify({ 'success': True, 'token': token, 'user': { 'username': admin.username, 'role': admin.role, 'is_super_admin': admin.is_super_admin() }, 'redirect': url_for('web.dashboard') }) # 普通请求,重定向 next_page = request.args.get('next') if next_page: return redirect(next_page) return redirect(url_for('web.dashboard')) else: current_app.logger.warning(f"登录失败 - 密码错误: {username}") failure_message = '用户名或密码错误' # 登录失败,返回错误信息 if request.headers.get('X-Requested-With') == 'XMLHttpRequest': return jsonify({ 'success': False, 'message': failure_message }), 401 flash(failure_message, 'error') except Exception as e: current_app.logger.error(f"登录过程中发生错误 - 用户名: {username}, 错误: {str(e)}") if request.headers.get('X-Requested-With') == 'XMLHttpRequest': return jsonify({ 'success': False, 'message': '登录过程中发生错误,请稍后重试' }), 500 flash('登录过程中发生错误,请稍后重试', 'error') return render_template('login.html') return render_template('login.html') except Exception as e: current_app.logger.error(f"登录路由发生严重错误: {str(e)}", exc_info=True) if request.headers.get('X-Requested-With') == 'XMLHttpRequest': return jsonify({ 'success': False, 'message': '服务器内部错误,请联系管理员' }), 500 flash('服务器内部错误,请联系管理员', 'error') return render_template('login.html') @web_bp.route('/logout') @login_required def logout(): """退出登录""" logout_user() flash('已退出登录', 'info') return redirect(url_for('web.login')) @web_bp.route('/dashboard') @login_required def dashboard(): """仪表板""" return render_template('dashboard.html') @web_bp.route('/favicon.ico') def favicon(): """Favicon 处理 - 返回空响应避免404错误""" from flask import Response return Response(status=204) # No Content # 导入视图函数 from . import views, user_views