You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Azure Web应用部署24小时后SSO失效,数据库令牌过期报错

令牌过期导致SSO失效问题排查与解决

问题根源分析

错误提示显示数据库登录用的<token-identified principal>令牌过期,核心原因有两点:

  1. SQLAlchemy连接池复用旧连接:应用启动时创建的数据库连接池会长期复用连接,这些连接携带的令牌过期后不会自动更新。
  2. 全局变量的进程隔离问题:Azure Web Apps是多进程运行环境,get_fresh_token中的全局变量_cached_token和_token_expiry无法在进程间共享,导致部分进程的令牌未及时刷新。

具体解决方案

1. 修改数据库连接初始化逻辑

确保每次创建数据库连接时都使用最新的令牌,而非复用初始化时的旧令牌。示例代码:

import urllib.parse
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

def init_db_connection(app):
    fresh_token = get_fresh_token()
    if not fresh_token:
        raise Exception("无法获取有效的数据库访问令牌")
    
    # 动态构建包含最新令牌的连接字符串
    conn_str = (
        f"DRIVER={{ODBC Driver 17 for SQL Server}};"
        f"SERVER={app.config['DB_SERVER']};"
        f"DATABASE={app.config['DB_NAME']};"
        f"UID=AnyString;"
        f"PWD={fresh_token};"
        f"Authentication=ActiveDirectoryPassword"
    )
    encoded_conn_str = urllib.parse.quote_plus(conn_str)
    
    # 配置SQLAlchemy引擎,加入连接池回收策略
    app.config['SQLALCHEMY_DATABASE_URI'] = f"mssql+pyodbc:///?odbc_connect={encoded_conn_str}"
    app.config['SQLALCHEMY_ENGINE_OPTIONS'] = {
        'pool_recycle': 3500,  # 3500秒后自动回收连接(小于令牌默认有效期3600秒)
        'pool_pre_ping': True  # 获取连接前自动校验连接有效性
    }
    
    db.init_app(app)

2. 替换全局变量为应用缓存

避免多进程环境下令牌不一致问题,改用Flask缓存存储令牌:

from flask_caching import Cache
from datetime import datetime, timedelta
import requests

# 初始化缓存(生产环境建议用Redis缓存)
cache = Cache(config={
    'CACHE_TYPE': 'SimpleCache',
    'CACHE_DEFAULT_TIMEOUT': 3500  # 提前5分钟过期
})

def get_fresh_token():
    cached_token = cache.get('db_access_token')
    token_expiry = cache.get('db_token_expiry')
    
    # 校验令牌是否仍有效
    if cached_token and token_expiry and datetime.now() < token_expiry - timedelta(minutes=5):
        return cached_token
        
    # 请求新令牌
    token_url = f'https://login.microsoftonline.com/{TENANT_ID}/oauth2/v2.0/token'
    token_data = {
        'grant_type': 'client_credentials',
        'client_id': CLIENT_ID,
        'client_secret': CLIENT_SECRET,
        'scope': RESOURCE_URL + '/.default'
    }
    
    try:
        token_response = requests.post(token_url, data=token_data)
        token_response.raise_for_status()
        response_json = token_response.json()
        
        token = response_json.get('access_token')
        expires_in = int(response_json.get('expires_in', 3600))
        token_expiry = datetime.now() + timedelta(seconds=expires_in)
        
        # 更新缓存
        cache.set('db_access_token', token, timeout=expires_in - 300)
        cache.set('db_token_expiry', token_expiry)
        
        return token
    except Exception as e:
        logger.error(f"获取令牌失败: {e}")
        return None

3. 优化回调函数中的数据库连接调用

移除回调函数中重复的init_db_connection调用,确保每次请求都使用最新的连接配置:

@bp.route('/callback')
def callback():
    # ... 原有逻辑省略 ...
    
    try:
        # 确保使用最新令牌初始化数据库连接
        init_db_connection(current_app)
        return redirect(url_for('main'))
    except Exception as e:
        logger.error(f"认证错误: {str(e)}", exc_info=True)
        flash(f'认证错误: {str(e)}', 'error')
        return redirect(url_for('auth.login'))

验证步骤

  1. 部署修改后的应用到Azure Web Apps
  2. 观察24小时后是否再出现令牌过期错误
  3. 查看日志确认令牌刷新逻辑正常执行

内容的提问来源于stack exchange,提问作者KNG

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 03:09:51