Flask中使用HTTPBasicAuth如何跳转登录页而非弹出登录框
Flask HTTPBasicAuth替换原生登录弹窗为自定义登录页解决方案
问题根源
- HTTPBasicAuth默认在校验失败时返回的响应会携带
WWW-Authenticate: Basic头,浏览器检测到该头就会自动弹出原生登录框 - 你给登录路由加了
@auth.login_required装饰器,用户未登录时访问首页会直接触发权限校验,还没加载自定义登录页就触发了弹窗 - HTTPBasicAuth本身是无状态协议,默认不会留存登录状态,需要配合Flask的session实现登录态持久化
具体修改步骤
1. 核心逻辑调整
- 配置Flask加密密钥,用于session存储
- 移除登录路由的
@auth.login_required装饰器,登录页面本身需公开可访问 - 修改密码校验逻辑,优先读取session中的已登录信息,存在则直接放行
- 修改权限错误处理逻辑,删除
WWW-Authenticate头,未登录时直接重定向到登录页,不触发浏览器弹窗 - 登录验证通过后将用户信息写入session,实现登录态留存
- 登出接口清除session中的登录信息
- 修正登录表单的提交地址,
url_for参数为视图函数名而非路由路径
2. 修改后完整后端代码
#!flask/bin/python from flask import Flask, jsonify, abort, request, make_response, url_for, render_template, session, redirect from flask_httpauth import HTTPBasicAuth app = Flask(__name__) # 配置session加密密钥,生产环境请替换为随机复杂字符串 app.config['SECRET_KEY'] = 'your_custom_secret_key_here' auth = HTTPBasicAuth() # 登录路由不要加@auth.login_required装饰器,需要公开访问 @app.route('/', methods=['GET','POST']) def login(): # 已登录用户直接跳首页 if 'user_email' in session: return render_template('index.html') templateToReturn = 'login.html' if request.method == 'POST': username = request.values.get('email') password = request.values.get('password') if verifyAuthentication(username, password): # 校验通过写入session,留存登录状态 session['user_email'] = username templateToReturn = 'index.html' return render_template(templateToReturn) @auth.verify_password def verify_password(email, password): # 优先校验session中已保存的登录态 if 'user_email' in session: return session['user_email'] # 无session时校验传入的账号密码 if verifyAuthentication(email, password): return email return False def verifyAuthentication(email, password): knownUsers = {'p1@gmail.com': 'pass', 'p2@yahoo.com': 'pass'} authenticated = False if email in knownUsers: if knownUsers[email] == password: authenticated = True return authenticated @app.route('/logout') def logout(): # 登出清除session session.pop('user_email', None) return render_template('logout.html') @auth.error_handler def unauthorized(): # 移除WWW-Authenticate头,避免浏览器弹原生登录框,直接跳登录页 resp = make_response(redirect(url_for('login'))) resp.headers.pop('WWW-Authenticate', None) return resp @app.errorhandler(400) def bad_request(error): return '<!DOCTYPE html><html><body><div style="text-align: center;">Bad request</div></body></html>' @app.errorhandler(404) def not_found(error): return '<!DOCTYPE html><html><body><div style="text-align: center;">Page not found</div></body></html>' if __name__ == '__main__': app.run(debug=True, host="0.0.0.0")
3. 修改后登录表单代码
<!-- 修正url_for参数,填写视图函数名login而非路由路径/ --> <form action="{{ url_for('login') }}" method="POST"> <div class="row"> <div class="input-field col s12"> <input id="email" name="email" type="email" class="validate"> <label for="email">Email</label> </div> </div> <div class="row"> <div class="input-field col s12"> <input id="password" name="password" type="password" class="validate"> <label for="password">Password</label> </div> </div> <button type="submit" id="login" class="waves-effect waves-light btn blue darken-1" style="width:100%;">Login</button> <br> </form>
注意事项
- 其他需要权限控制的路由正常添加
@auth.login_required装饰器即可,未登录时会自动跳转到自定义登录页 - 生产环境不要把用户账号密码硬编码在代码中,建议存储在数据库且密码做哈希加密处理
- 可根据需求配置session过期时间,实现自动登出功能
内容的提问来源于stack exchange,提问作者Nav
相关产品推荐
相关产品推荐

