Flask》》 Flask-OpenID 认证、 OpenID Connect (OIDC)

OpenID 停用了

Flask-OpenID 是一个用于集成OpenID 2.0 认证协议的 Flask 扩展。不过,要注意,这个协议和对应的扩展库已经过时了,现代的应用都转向了基于 OAuth 2.0 的 OpenID Connect (OIDC)

csharp 复制代码
# 安装 
pip install Flask-OpenID

## app.py
import os
from flask import Flask, render_template, request, redirect, url_for, session, flash
from flask_openid import OpenID

# --- 1. 应用配置和初始化 ---
app = Flask(__name__)
app.secret_key = 'your-secret-key-here' # 请务必修改为一个强随机字符串

# 设置临时文件存储路径的根目录
# BASE_DIR = os.path.abspath(os.path.dirname(__file__))
# oid = OpenID(app, os.path.join(BASE_DIR, 'tmp'))

oid = OpenID(app, './tmp', safe_roots=[]) # 简化路径写法,safe_roots用于安全验证

# --- 2. 辅助函数:在请求前加载用户 ---
@app.before_request
def lookup_current_user():
    """在每个请求前,根据 session 中的 openid 加载当前用户信息"""
    session.permanent = True
    # 假设我们用 g 对象来存储当前用户
    from flask import g
    g.user = None
    if 'openid' in session:
        # 这里简化处理,实际应从数据库查询
        # g.user = User.query.filter_by(openid=session['openid']).first()
        g.user = session['openid'] # 演示用,直接将openid作为用户标识

# --- 3. 登录视图 (核心逻辑) ---
@app.route('/login', methods=['GET', 'POST'])
@oid.loginhandler # 装饰器标记为登录处理器
def login():
    """处理用户的登录请求和OpenID回调"""
    # 如果用户已登录,直接重定向到下一页或主页
    if g.user is not None:
        return redirect(oid.get_next_url())
    
    if request.method == 'POST':
        # 获取用户在表单中输入的 OpenID 标识 URL
        openid_url = request.form.get('openid')
        if openid_url:
            # 尝试登录,并请求获取用户的邮箱和昵称
            return oid.try_login(openid_url, ask_for=['email', 'nickname'])
    
    # GET 请求或未提供 OpenID URL 时,显示登录表单
    return render_template('login.html', next=oid.get_next_url(), error=oid.fetch_error())

@oid.after_login
def create_or_login(resp):
    """OpenID 认证成功后,会回调此函数。resp 包含从 OpenID 提供者返回的用户信息"""
    session['openid'] = resp.identity_url
    # 从响应中提取用户信息 (如果提供并且用户授权了)
    user_email = resp.email if hasattr(resp, 'email') else ''
    user_nickname = resp.nickname if hasattr(resp, 'nickname') else ''
    
    # 这里应该进行数据库操作:根据 identity_url 查找或创建用户
    # 例如:
    # user = User.query.filter_by(openid=resp.identity_url).first()
    # if user is None:
    #     user = User(openid=resp.identity_url, email=user_email, nickname=user_nickname)
    #     db.session.add(user)
    #     db.session.commit()
    
    flash('登录成功!')
    # 登录成功后,跳转到登录前想访问的页面
    return redirect(oid.get_next_url())

# --- 4. 登出视图 ---
@app.route('/logout')
def logout():
    """清除 session 中的用户标识,退出登录"""
    session.pop('openid', None)
    flash('您已成功登出。')
    return redirect(url_for('login'))

# --- 5. 受保护的页面示例 ---
# 实际使用中,可以配合 Flask-Login 的 @login_required 装饰器来保护页面
@app.route('/')
def index():
    if g.user is None:
        return redirect(url_for('login'))
    return f'Hello, {g.user}! <br> <a href="/logout">登出</a>'


# templates/login.html
<!DOCTYPE html>
<html>
<head>
    <title>OpenID 登录示例</title>
</head>
<body>
    <h1>OpenID 登录</h1>
    <form action="" method="post">
        <input type="text" name="openid" value="https://your-openid-provider.com" />
        <input type="hidden" name="next" value="{{ next }}" />
        <input type="submit" value="登录" />
    </form>
    {% if error %}
        <h4>错误信息: {{ error }}</h4>
    {% endif %}
</body>
</html>


csharp 复制代码
浏览器 (User)                你的网站 (mysite.com)           OpenID 提供者 (openid.com)
    |                            |                                 |
    | GET /                      |                                 |
    |--------------------------->|                                 |
    | 302 to /login              |                                 |
    |<---------------------------|                                 |
    | GET /login                 |                                 |
    |--------------------------->|                                 |
    | 200 (login form)           |                                 |
    |<---------------------------|                                 |
    | POST /login (openid=url)   |                                 |
    |--------------------------->|                                 |
    |                            | oid.try_login() 发现提供者地址   |
    | 302 to openid.com/login    |                                 |
    |<---------------------------|                                 |
    | GET /login (at openid.com) |                                 |
    |------------------------------------------------------------->|
    |                                 |  (用户输入密码确认)          |
    |<-------------------------------------------------------------| 302 to mysite.com/login?openid_complete=...
    | GET /login?openid_complete=...|                               |
    |--------------------------->|                                 |
    |                            | @oid.loginhandler 检测回调       |
    |                            | 验证签名 -> 调用 @oid.after_login|
    |                            | 设置 session['openid']          |
    | 302 to / (next)            |                                 |
    |<---------------------------|                                 |
    | GET /                      |                                 |
    |--------------------------->|                                 |
    | 200 (Hello, user)          |                                 |
    |<---------------------------|                                 |
相关推荐
nnerddboy12 小时前
Rust教程04:引用、借用与切片
开发语言·后端·rust
Rain的Java大神之路13 小时前
线上接口负载满了如何解决
java·数据库·redis·后端·缓存·面试·架构
jerryinwuhan13 小时前
《Python数据预处理》第四章
开发语言·python
雨晨源码(同名B站)13 小时前
基于Python的网易云音乐评论数据情感化分析系统 音乐爬虫信息可视化 |SnowNLP评论情感分析
开发语言·hadoop·爬虫·python·信息可视化·毕业设计
阿童木写作13 小时前
跨境电商图片翻译工具推荐:跨马翻译批量处理视频字幕与抠图
python·音视频
weixin_4316004414 小时前
NestJS 入门(7):生命周期钩子——构造函数和 `OnModuleInit` 差在哪?
前端·后端·学习·node.js·nest.js
小强库计算机毕业设计14 小时前
基于 Spring Boot + Vue3 的校园管理系统
java·spring boot·后端·项目实战·管理系统·技术分享·校园管理系统实战
怪奇云呼军14 小时前
G.711、Opus 和重采样会拖慢识别吗?闪电智能VoiceAgent 的音频入口怎么选
java·人工智能·python·算法·云计算·音视频
ZC跨境爬虫14 小时前
LeetCode 27. 移除元素(双指针详解 + Java Python 多解法对比)
java·python·leetcode
Patrick在香港14 小时前
Python正则表达式实战:解析和验证香港身份证、车牌、电话格式
开发语言·python·正则表达式