bigywater.tech
Flask · 72

Flask-Login 登录流程

Flask-Login 把「已登录」存在哪里?每个请求做了什么?remember me 是怎么在 session 丢失后恢复登录的?这篇用 test_client 把整条流程的 cookie 与 session 内容打印出来。

3 次
3 个访问 current_user 的请求里 user_loader 被调用的次数
_fresh
remember 恢复的会话是 False,fresh_login_required 页面返回 401
strong
session_protection 设成 strong 后换 User-Agent 就被登出

未登录与 next

实测 · 本机执行输出
  GET /secret → 302 | Location = /login?next=%2Fsecret
  GET / → hello anonymous
  current_user 的类型(未登录): {'anon': True, 'auth': False, 'cls': 'AnonymousUserMixin', 'id': None}

上一篇:Extension 初始化时机;下一篇:Flask-WTF 表单验证。

login_user 写了什么

实测 · 本机执行输出
  POST /login?next=/secret → 302 | Location = /secret
  session 里的键: {'_flashes': [('message', 'Please log in to access this page.')], '_fresh': True, '_id': '03f095a1c03fdce8fe2ea94737ef9d62c456faf66d67aac9e9d7b2f6548211aab71d5ce5c550e74c31a055e110b5ce1429cfcf4eb860fe2c460a0c6f830c7f59', '_user_id': '1'}
  登录后 GET /secret → secret for ann | whoami: {'anon': False, 'auth': True, 'cls': 'User', 'id': '1'}
  session cookie 的明文(base64 解码,不需要密钥): {'_flashes': [{' t': ['message', 'Please log in to access this page.']}], '_fresh': True, '_id': '03f095a1c0…', '_user_id': '1'}

user_loader

实测 · 本机执行输出
  3 个请求 user_loader 调用了 3 次(每个访问 current_user 的请求一次)
  未登录请求访问 current_user:user_loader 调用 0 次(session 里没有 id,不调用)

remember me

实测 · 本机执行输出
  Set-Cookie: ['remember_token=2|bc53cbe8e638…', 'session=.eJwljjEOAyEMw…'] | remember_token 的属性: ['Expires=Fri, 02 Oct 2026 04:52:49 GMT; HttpOnly; Path=/']
  remember_token 的值形如 用户id|哈希: 2 | bc53cbe8e638…
  把 session cookie 删掉(只剩 remember_token)后访问 /whoami → {'anon': False, 'auth': True, 'cls': 'User', 'id': '2'}
  恢复之后 session 里 _fresh = False
  从 remember cookie 恢复的会话访问 fresh_login_required 页面 → 401 None

登出

实测 · 本机执行输出
  Set-Cookie: ['remember_token=', 'session=']
  登出后 whoami: False

session_protection

实测 · 本机执行输出
  basic   同一个 session 换 User-Agent 后 whoami 是否仍登录: True(原 UA 下 True)
  strong  同一个 session 换 User-Agent 后 whoami 是否仍登录: False(原 UA 下 True)

这里只验证了「换 User-Agent 后是否仍登录」这一个变化;basic 下的 fresh 标记变化没有单独验证。

API 的未授权处理

实测 · 本机执行输出
  自定义 unauthorized_handler → 401 {'error': 'login required'}

分步演示

Flask-Login 登录流程
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Python 3.14.7 / Flask 3.1.3 / Werkzeug 3.1.9 / Jinja2 3.1.6 / SQLAlchemy 2.1.1 / gunicorn 26.2.0(都装在自己的虚拟环境里);大多数实验用 Flask 的 test_client(直接调用 WSGI 应用,不经过网络),需要真实 HTTP 时另起 gunicorn 或开发服务器(端口 9600 以上,本机 127.0.0.1);计时用 time.perf_counter,数字每次运行会有小幅抖动。Flask-Login 0.6.3
    • 没有验证:basic 模式下标识变化后 _fresh 的具体变化、换 IP 触发 session_protection、Flask-Login 与 Flask-Session 等服务端 session 搭配、remember cookie 的 duration 配置项与 refresh 机制、多设备同时登录
    京ICP备2026067502号-1京公网安备11010502063487号