Skip to main content
curl -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." \
  https://conn.nodeloc.cc/oauth2/userinfo
{
  "sub": "123",
  "username": "john_doe",
  "email": "john@example.com",
  "name": "John Doe",
  "groups": ["developers", "trust_level_2", "beta_testers"],
  "avatar_url": "https://conn.nodeloc.cc/user_avatar/nodeloc/john_doe/120/456.png",
  "profile_url": "https://conn.nodeloc.cc/u/john_doe"
}

获取用户信息

使用访问令牌获取当前认证用户的详细信息。

端点信息

GET /oauth2/userinfo

认证方式

Authorization
string
required
Bearer 访问令牌,格式:Bearer {access_token}

响应字段

sub
string
用户唯一标识符(Subject),通常是用户ID
username
string
用户名
email
string
用户邮箱地址
groups
array
用户所属的组列表
name
string
用户显示名称(可选)
avatar_url
string
用户头像URL(可选)
profile_url
string
用户档案页面URL(可选)
curl -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." \
  https://conn.nodeloc.cc/oauth2/userinfo
{
  "sub": "123",
  "username": "john_doe",
  "email": "john@example.com",
  "name": "John Doe",
  "groups": ["developers", "trust_level_2", "beta_testers"],
  "avatar_url": "https://conn.nodeloc.cc/user_avatar/nodeloc/john_doe/120/456.png",
  "profile_url": "https://conn.nodeloc.cc/u/john_doe"
}

使用场景

用户档案页面

async function loadUserProfile(accessToken) {
  try {
    const response = await fetch('https://conn.nodeloc.cc/oauth2/userinfo', {
      headers: { 'Authorization': `Bearer ${accessToken}` }
    });
    
    const user = await response.json();
    
    // 更新UI
    document.getElementById('username').textContent = user.username;
    document.getElementById('email').textContent = user.email;
    document.getElementById('avatar').src = user.avatar_url;
    
    // 显示用户组
    const groupsContainer = document.getElementById('groups');
    user.groups.forEach(group => {
      const badge = document.createElement('span');
      badge.className = 'badge';
      badge.textContent = group;
      groupsContainer.appendChild(badge);
    });
    
  } catch (error) {
    console.error('加载用户档案失败:', error);
  }
}

权限检查

function hasPermission(userGroups, requiredGroups) {
  return requiredGroups.some(group => userGroups.includes(group));
}

// 使用示例
const user = await getUserInfo(accessToken);

if (hasPermission(user.groups, ['admins', 'moderators'])) {
  // 显示管理员功能
  showAdminPanel();
} else if (hasPermission(user.groups, ['developers'])) {
  // 显示开发者功能
  showDeveloperTools();
}

用户会话管理

from flask import session, jsonify
import requests

def get_current_user():
    """获取当前登录用户信息"""
    access_token = session.get('access_token')
    if not access_token:
        return None
    
    try:
        response = requests.get(
            'https://conn.nodeloc.cc/oauth2/userinfo',
            headers={'Authorization': f'Bearer {access_token}'},
            timeout=10
        )
        
        if response.status_code == 200:
            return response.json()
        elif response.status_code == 401:
            # 令牌过期,清除会话
            session.clear()
            return None
        else:
            return None
            
    except requests.RequestException:
        return None

@app.route('/api/me')
def current_user():
    user = get_current_user()
    if user:
        return jsonify(user)
    else:
        return jsonify({'error': 'Not authenticated'}), 401

错误处理

常见错误代码

HTTP状态码错误代码说明解决方案
401invalid_token访问令牌无效或过期使用刷新令牌重新获取
401token_expired访问令牌过期刷新访问令牌
403insufficient_scope权限范围不足重新授权获取更多权限
429rate_limit_exceeded请求过于频繁稍后重试

错误处理最佳实践

async function getUserInfoWithRetry(accessToken, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const response = await fetch('https://conn.nodeloc.cc/oauth2/userinfo', {
        headers: { 'Authorization': `Bearer ${accessToken}` }
      });
      
      if (response.ok) {
        return await response.json();
      }
      
      if (response.status === 401) {
        // 令牌无效,不要重试
        throw new Error('Token invalid or expired');
      }
      
      if (response.status === 429 && attempt < maxRetries) {
        // 速率限制,等待后重试
        const delay = Math.pow(2, attempt) * 1000; // 指数退避
        await new Promise(resolve => setTimeout(resolve, delay));
        continue;
      }
      
      throw new Error(`HTTP ${response.status}: ${response.statusText}`);
      
    } catch (error) {
      if (attempt === maxRetries) {
        throw error;
      }
    }
  }
}

缓存考虑

用户信息缓存用户信息相对稳定,可以进行适当缓存以减少API调用:
  • 缓存时间建议:5-15分钟
  • 当收到401错误时立即清除缓存
  • 用户主动登出时清除缓存
class UserInfoCache {
  constructor(ttl = 300000) { // 5分钟TTL
    this.cache = new Map();
    this.ttl = ttl;
  }
  
  async getUserInfo(accessToken) {
    const cacheKey = this.hashToken(accessToken);
    const cached = this.cache.get(cacheKey);
    
    if (cached && Date.now() < cached.expiry) {
      return cached.data;
    }
    
    try {
      const userInfo = await this.fetchUserInfo(accessToken);
      this.cache.set(cacheKey, {
        data: userInfo,
        expiry: Date.now() + this.ttl
      });
      return userInfo;
    } catch (error) {
      // 如果是401错误,清除缓存
      if (error.status === 401) {
        this.cache.delete(cacheKey);
      }
      throw error;
    }
  }
  
  clearCache() {
    this.cache.clear();
  }
}

下一步