获取用户信息
使用访问令牌获取当前认证用户的详细信息。端点信息
GET/oauth2/userinfo
认证方式
Bearer 访问令牌,格式:
Bearer {access_token}
响应字段
用户唯一标识符(Subject),通常是用户ID
用户名
用户邮箱地址
用户所属的组列表
Show 数组项
Show 数组项
组名称,如 “admins”, “developers”, “trust_level_1” 等
用户显示名称(可选)
用户头像URL(可选)
用户档案页面URL(可选)
Copy
curl -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." \
https://conn.nodeloc.cc/oauth2/userinfo
Copy
{
"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"
}
使用场景
用户档案页面
Copy
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);
}
}
权限检查
Copy
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();
}
用户会话管理
Copy
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状态码 | 错误代码 | 说明 | 解决方案 |
---|---|---|---|
401 | invalid_token | 访问令牌无效或过期 | 使用刷新令牌重新获取 |
401 | token_expired | 访问令牌过期 | 刷新访问令牌 |
403 | insufficient_scope | 权限范围不足 | 重新授权获取更多权限 |
429 | rate_limit_exceeded | 请求过于频繁 | 稍后重试 |
错误处理最佳实践
Copy
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错误时立即清除缓存
- 用户主动登出时清除缓存
Copy
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();
}
}