""" Casdoor 认证工具模块 用于处理 Casdoor OAuth/OIDC 认证流程 """ import requests import urllib.parse from flask import session, current_app import secrets class CasdoorAuth: """Casdoor 认证处理类""" def __init__(self, app=None): self.app = app if app: self.init_app(app) def init_app(self, app): """初始化应用配置""" self.endpoint = app.config.get('CASDOOR_ENDPOINT') self.client_id = app.config.get('CASDOOR_CLIENT_ID') self.client_secret = app.config.get('CASDOOR_CLIENT_SECRET') self.organization_name = app.config.get('CASDOOR_ORGANIZATION_NAME', 'built-in') self.application_name = app.config.get('CASDOOR_APPLICATION_NAME', 'app-built-in') self.redirect_uri = app.config.get('CASDOOR_REDIRECT_URI') def get_authorization_url(self, state=None): """ 生成 Casdoor 授权 URL Args: state: 状态参数,用于防止 CSRF 攻击 Returns: str: 授权 URL """ if not state: state = secrets.token_urlsafe(32) session['casdoor_state'] = state params = { 'client_id': self.client_id, 'response_type': 'code', 'redirect_uri': self.redirect_uri, 'scope': 'openid profile email phone', 'state': state } auth_url = f"{self.endpoint}/login/oauth/authorize" return f"{auth_url}?{urllib.parse.urlencode(params)}" def get_token(self, code): """ 使用授权码获取访问令牌 Args: code: 授权码 Returns: dict: 包含 access_token 和 id_token 的字典 """ token_url = f"{self.endpoint}/api/login/oauth/access_token" data = { 'grant_type': 'authorization_code', 'client_id': self.client_id, 'client_secret': self.client_secret, 'code': code, 'redirect_uri': self.redirect_uri } try: response = requests.post(token_url, data=data) response.raise_for_status() return response.json() except requests.exceptions.RequestException as e: try: current_app.logger.error(f"获取 Casdoor token 失败: {str(e)}") except: pass # 如果无法记录日志,忽略错误 return None def get_user_info(self, access_token): """ 使用访问令牌获取用户信息 Args: access_token: 访问令牌 Returns: dict: 用户信息字典 """ user_info_url = f"{self.endpoint}/api/userinfo" headers = { 'Authorization': f'Bearer {access_token}' } try: response = requests.get(user_info_url, headers=headers) response.raise_for_status() return response.json() except requests.exceptions.RequestException as e: try: current_app.logger.error(f"获取 Casdoor 用户信息失败: {str(e)}") except: pass # 如果无法记录日志,忽略错误 return None def verify_state(self, state): """ 验证 state 参数,防止 CSRF 攻击 Args: state: 从回调中获取的 state 参数 Returns: bool: 验证是否通过 """ stored_state = session.get('casdoor_state') if stored_state and stored_state == state: session.pop('casdoor_state', None) return True return False # 创建全局实例 casdoor_auth = CasdoorAuth()