面向开发者的实用指南,帮助构建与 NBS 平台集成的 Web 或客户端应用程序。
NBS 平台为第三方应用提供三项核心服务:
开发者通过两个 JavaScript 库进行集成:
| 库 | 类 | 用途 |
|---|---|---|
auth.js |
NBSAuth |
SSO 认证、令牌管理、会话处理 |
nbs-file-client.js |
NBSFileClient |
文件上传、下载、删除、列表 |
注意: 请勿使用
nbs-platform-client.js— 该库仅用于平台管理操作,不适用于第三方应用集成。
审计日志无需 JS 库 — 只需简单的 REST API 调用即可。
提交应用注册申请,需提供以下信息:
| 信息项 | 说明 | 是否必填 |
|---|---|---|
| 应用名称 | 应用的显示名称 | 必填 |
| 应用描述 | 简要说明应用功能 | 必填 |
| 分类 | data-insights / task-management / workflow-automation / seller-engagement-content-generation |
必填 |
| 入口 URL | 用户访问应用的地址,如 http://localhost:3000、https://your-app.harmony.a2z.com/。注册时可留空,部署后再更新。 |
可选 |
| 访问模式 | public(所有已认证用户)或 whitelist(仅受邀用户) |
必填 |
| 初始管理员 | 用户 ID / 登录别名列表 | 必填 |
平台管理员在注册表中创建您的应用,并提供:
- appId — kebab-case 格式的标识符(例如 my-cool-app),用于所有 API 调用
- 实验环境 API 端点 URL — 用于集成测试
注意: 生产环境 API 端点将在您准备上线时提供。
在测试之前,需要配置用户:
- 应用管理员(Owner) 可以通过应用管理控制台自行管理用户 — 无需平台管理员参与
- Public 模式: 所有已认证用户均可访问。您仍可为特定用户分配 owner 和 manager 角色以获得更高权限。
- Whitelist 模式: 应用管理员通过应用管理控制台添加用户。每个用户获得一个角色:owner、manager 或 member。
根据您的应用架构(浏览器端或客户端/服务器端),集成所需的服务。请参阅以下详细指南:
提示: 您的 API 端点应可配置(例如通过
config.json或环境变量),以便在实验环境和生产环境之间轻松切换。
使用实验环境 API 端点进行测试: - SSO 登录/登出流程 - 文件上传、下载、列表 - 审计日志记录 - 基于角色的访问(使用 owner、manager、member 账户测试)
两种部署选项:
| 选项 | 方式 | 适用场景 |
|---|---|---|
| A:平台托管 | 将源代码提供给平台团队。他们会部署到平台 S3 存储桶的 apps/{appId}/ 目录下,通过 CloudFront 提供服务。 |
简单的静态 Web 应用,无自定义后端 |
| B:自托管 | 在您自己的基础设施上托管(Harmony、EC2 等)。将入口 URL 提供给平台管理员以更新应用注册表。 | 有自定义后端、需求复杂的应用 |
模块作用: SSO(单点登录)集成使您的应用能够利用 NBS 平台的统一身份认证体系,用户无需为每个应用单独注册和登录。通过集成 SSO,您的应用可以获取用户身份信息(姓名、邮箱、角色等),并基于角色控制功能访问权限。
适用场景: - 需要识别用户身份的任何 Web 或客户端应用 - 需要基于角色(owner/manager/member)展示不同功能的应用 - 需要调用 NBS 文件服务或审计服务的应用(这些服务要求 SSO 认证) - 后端自动化服务需要以服务身份调用 NBS API(M2M 场景)
NBS 支持两种认证路径,适用于不同场景:
| 认证路径 | 令牌类型 | 使用场景 | 适用对象 |
|---|---|---|---|
| Federate OAuth 2.0 PKCE | Federate JWT (RS256) | 交互式用户会话 — 用户通过浏览器登录 | Web 应用、SPA、任何用户点击"登录"的 UI |
| Cognito M2M (Client Credentials) | Cognito Access Token (RS256) | 服务间调用 — 无人工用户参与 | 后端服务、定时任务、CI/CD 流水线、自动化脚本 |
适用于用户通过浏览器交互登录的 Web 应用。auth.js 提供了 NBSAuth 类,处理完整的 OAuth 2.0 PKCE 流程。
平台托管应用(部署在 /apps/{appId}/ 下):使用平台统一回调地址 /callback.html。这样无需为每个子应用在 Federate 中单独注册回调 URL — 所有平台托管应用只需一个回调地址。
自托管应用(部署在自己的域名上):使用自己页面的 URL 作为回调地址,并在 Federate 中注册。
<script src="./auth.js"></script>
<script>
async function initApp() {
// 1. 加载配置
const resp = await fetch('./config.json');
const config = await resp.json();
// 2. 创建认证实例
const auth = new NBSAuth({
apiEndpoint: config.api_gateway_url,
redirectUri: window.location.origin + '/callback.html', // 平台统一回调地址
appId: 'your-app-id',
appName: 'Your App Name'
});
// 3. 初始化(从后端获取 OAuth 配置)
await auth.initialize();
// 4. 检查认证状态(令牌通过 sessionStorage 从平台回调页共享)
if (!auth.isAuthenticated()) {
await auth.login(); // 重定向到 OAuth → 平台回调 → 返回本页面
return;
}
// 5. 验证此应用的令牌(服务器端验证)
const userInfo = await auth.verifyToken('your-app-id');
// 5. 检查认证状态
if (!auth.isAuthenticated()) {
await auth.login(); // 重定向到 OAuth 登录页面
return;
}
// 6. 验证此应用的令牌(服务器端验证)
const userInfo = await auth.verifyToken('your-app-id');
// userInfo: { sub, appId, userRole, fullname, email, effectiveRole, activeDelegations }
// 7. 应用就绪
console.log('Welcome', userInfo.fullname);
}
initApp();
</script>
为了提供更好的用户体验,使用登录遮罩层在认证过程中显示状态信息:
<script src="./auth.js"></script>
<!-- 登录遮罩层 -->
<div id="ssoOverlay" style="display:none; position:fixed; inset:0; background:rgba(245,245,247,0.97);
backdrop-filter:blur(20px); z-index:99999; align-items:center; justify-content:center; flex-direction:column;">
<div style="text-align:center; max-width:400px; padding:48px 40px; background:white;
border-radius:24px; box-shadow:0 8px 40px rgba(0,0,0,0.12);">
<div style="font-size:48px; margin-bottom:16px;">🔐</div>
<h2 style="font-size:24px; font-weight:600; margin-bottom:8px;">Your App Name</h2>
<p id="ssoStatusMsg" style="font-size:16px; color:#515154; margin-bottom:32px;">Verifying identity...</p>
<button id="ssoLoginBtn" onclick="window._auth && window._auth.login()" style="display:none;
width:100%; background:#0071e3; color:white; border:none; border-radius:12px;
padding:16px 24px; font-size:16px; font-weight:600; cursor:pointer;">
Sign In with SSO
</button>
</div>
</div>
<script>
const APP_ID = 'your-app-id';
const APP_NAME = 'Your App Name';
function showOverlay(msg, showBtn) {
const overlay = document.getElementById('ssoOverlay');
overlay.style.display = 'flex';
document.getElementById('ssoStatusMsg').textContent = msg;
document.getElementById('ssoLoginBtn').style.display = showBtn ? 'block' : 'none';
}
function hideOverlay() {
document.getElementById('ssoOverlay').style.display = 'none';
}
document.addEventListener('DOMContentLoaded', async () => {
try {
showOverlay('Loading configuration...', false);
const resp = await fetch('./config.json');
const config = await resp.json();
const auth = new NBSAuth({
apiEndpoint: config.api_gateway_url,
redirectUri: window.location.origin + '/callback.html', // 平台统一回调地址
appId: APP_ID,
appName: APP_NAME
});
window._auth = auth;
await auth.initialize();
if (auth.isAuthenticated()) {
showOverlay('Verifying identity...', false);
try {
const userInfo = await auth.verifyToken(APP_ID);
hideOverlay();
onAppReady(auth, userInfo, config);
} catch (e) {
if (e.message === 'TOKEN_EXPIRED') {
await auth.refreshAccessToken();
const userInfo = await auth.verifyToken(APP_ID);
hideOverlay();
onAppReady(auth, userInfo, config);
} else if (e.message === 'FORBIDDEN_ACCESS') {
showOverlay('You do not have access to this app.', false);
return;
} else {
throw e;
}
}
} else {
showOverlay('Please sign in to continue.', true);
return;
}
} catch (err) {
console.error('SSO init failed:', err);
showOverlay('Initialization failed. Please refresh.', true);
}
});
function onAppReady(auth, userInfo, config) {
console.log('Welcome', userInfo.fullname, '| Role:', userInfo.userRole);
// 您的应用逻辑从这里开始
}
</script>
const auth = new NBSAuth({
apiEndpoint: 'https://...', // 必填 — API Gateway 端点 URL
redirectUri: '...', // 必填 — OAuth 重定向 URI(平台托管应用使用 '/callback.html')
appId: 'my-app', // 可选 — 用于登录追踪的应用 ID
appName: 'My App', // 可选 — 用于登录追踪的应用名称
usePersistentStorage: false // 可选 — true = localStorage,false = sessionStorage(默认)
});
| Method | Returns | 描述 |
|---|---|---|
initialize() |
Promise<void> |
从 /auth/config 获取 OAuth 配置。启动时调用一次。 |
login() |
Promise<void> |
将浏览器重定向到 OAuth 授权页面(PKCE 流程)。 |
handleCallback() |
Promise<boolean> |
用授权码交换令牌。成功返回 true。 |
isAuthenticated() |
boolean |
检查存储中是否存在令牌(不进行验证)。 |
verifyToken(appId) |
Promise<Object> |
调用 /auth/verify-token — 从后端返回可信的 user_info。推荐的应用访问检查方式。 |
getValidAccessToken() |
Promise<string> |
返回有效的访问令牌。如果令牌将在 5 分钟内过期,自动刷新。 |
getAccessToken() |
string\|null |
返回当前访问令牌,不检查是否需要刷新。 |
getUserInfo() |
Object\|null |
返回上次令牌交换/验证时缓存的 user_info 对象。 |
refreshAccessToken() |
Promise<Object> |
手动刷新访问令牌。使用互斥锁防止并发刷新。 |
isTokenExpired(bufferSeconds) |
boolean |
如果令牌将在 bufferSeconds 秒内过期,返回 true。 |
ensureValidSession(appId) |
Promise<boolean> |
组合检查:验证 + 自动刷新 + 失败时自动重定向。 |
logout() |
void |
清除存储中的所有令牌并重定向到登录页面。 |
verifyToken 响应对象{
sub: 'user-id', // 用户标识符
appId: 'my-app', // 验证令牌所针对的应用
userRole: 'member', // 在此应用中的角色:'owner'、'manager' 或 'member'
fullname: 'John Doe', // 显示名称
email: 'john@example.com', // 电子邮件地址
effectiveRole: 'member', // 有效角色(考虑委托关系)
activeDelegations: [] // 活跃的委托条目
}
redirectUri 平台托管应用(部署在 /apps/{appId}/ 下)应设为 window.location.origin + '/callback.html'。平台统一回调页面处理 OAuth 授权码交换并重定向回您的应用。这意味着所有平台托管应用只需在 Federate 中注册一个回调 URL。自托管应用(不同域名)请使用自己的回调 URL。handleCallback() — 使用统一回调模式时,平台的 /callback.html 负责授权码交换。您的应用只需检查 isAuthenticated() 并调用 verifyToken()。sessionStorage,前缀为 midway_。设置 usePersistentStorage: true 可改用 localStorage。verifyToken(appId) 是检查应用访问权限的推荐方式。它在服务器端验证令牌并返回用户在特定应用中的角色。对于跨应用进入(用户从 App Store 携带其他应用的令牌进入),verifyToken 会自动记录登录事件。verifyToken 的错误码: NO_TOKEN、TOKEN_EXPIRED、FORBIDDEN_ACCESS、SYSTEM_ERROR。适用于有后端服务器(Node.js、Python、Java 等)的应用,服务器直接验证 Federate JWT 并代表用户调用 NBS API。
流程:
浏览器 → 您的后端 → NBS API(携带 Bearer 令牌)
工作原理:
NBSAuth,或使用您自己的 OAuth 流程)# Python 示例 — 从后端调用 NBS API
import requests
NBS_API = "https://your-api-endpoint.execute-api.region.amazonaws.com/prod"
def list_files(access_token, app_id, directory=""):
resp = requests.get(
f"{NBS_API}/apps/{app_id}/files",
headers={"Authorization": f"Bearer {access_token}"},
params={"directory": directory}
)
resp.raise_for_status()
return resp.json()
def log_audit(access_token, app_id, operation, details=None):
resp = requests.post(
f"{NBS_API}/apps/{app_id}/audit/log",
headers={
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json"
},
json={
"operation": operation,
"status": "success",
"operationDetails": details or {}
}
)
resp.raise_for_status()
return resp.json()
要点:
- NBS API Gateway Lambda Authorizer 会验证 Federate JWT — 您的后端无需自行验证令牌
- 令牌中的 sub 声明标识用户身份
- 所有 NBS API 端点接受 Authorization: Bearer <federate_access_token>
- 令牌刷新由您的后端负责 — 当访问令牌过期时,使用刷新令牌调用 POST /auth/refresh-token
适用于自动化服务、定时任务、CI/CD 流水线,或任何无人工用户参与的场景。
何时使用 M2M: - 后端批处理(例如每晚生成报表) - CI/CD 流水线将构建产物上传到 NBS 文件存储 - 监控系统的自动化审计日志记录 - 服务间集成,您的后端在无用户会话的情况下调用 NBS API
前提条件:
- 联系平台管理团队获取 Cognito M2M 客户端凭证(client_id + client_secret)
- M2M 客户端已在与 NBS 平台关联的 Cognito User Pool 中注册
步骤 1 — 获取 M2M 访问令牌:
import requests
COGNITO_TOKEN_ENDPOINT = "https://your-cognito-domain.auth.region.amazoncognito.com/oauth2/token"
CLIENT_ID = "your-m2m-client-id"
CLIENT_SECRET = "your-m2m-client-secret"
def get_m2m_token():
resp = requests.post(
COGNITO_TOKEN_ENDPOINT,
data={
"grant_type": "client_credentials",
"client_id": CLIENT_ID,
"client_secret": CLIENT_SECRET,
"scope": "openid"
},
headers={"Content-Type": "application/x-www-form-urlencoded"}
)
resp.raise_for_status()
return resp.json()["access_token"]
步骤 2 — 使用 auth_source=cognito_m2m 调用 NBS API:
NBS_API = "https://your-api-endpoint.execute-api.region.amazonaws.com/prod"
def upload_file_m2m(token, app_id, directory, filename, content_type):
"""通过 M2M 认证获取预签名上传 URL"""
resp = requests.post(
f"{NBS_API}/apps/{app_id}/files/upload?auth_source=cognito_m2m",
headers={
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
},
json={
"directory": directory,
"filename": filename,
"contentType": content_type
}
)
resp.raise_for_status()
return resp.json() # 包含 uploadUrl、requiredHeaders、s3Key
def log_audit_m2m(token, app_id, user_id, operation, details=None):
"""代表用户记录审计事件(M2M 必须在请求体中提供 userId)"""
resp = requests.post(
f"{NBS_API}/apps/{app_id}/audit/log?auth_source=cognito_m2m",
headers={
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
},
json={
"operation": operation,
"status": "success",
"operationDetails": details or {},
"userId": user_id # M2M 必填 — 标识最终用户
}
)
resp.raise_for_status()
return resp.json()
Federate 认证与 M2M 认证的主要区别:
| 方面 | Federate(用户) | Cognito M2M(服务) |
|---|---|---|
| 查询参数 | 无(默认) | ?auth_source=cognito_m2m |
| 用户身份 | 从令牌 sub 中提取 |
必须在请求体中提供 userId(用于审计) |
| 令牌来源 | OAuth 2.0 PKCE 流程(浏览器) | Client Credentials Grant(服务器) |
| 令牌刷新 | POST /auth/refresh-token |
从 Cognito 请求新令牌 |
| 使用场景 | 交互式用户会话 | 自动化/后台处理 |
模块作用: 文件服务为您的应用提供安全的、按应用隔离的文件存储能力。每个应用拥有独立的文件空间,支持基于角色的目录访问控制、文件上传/下载/删除、软删除与恢复、分页列表和通配符搜索。所有文件操作通过 S3 Presigned URL 完成,无需直接访问 S3。
适用场景:
- 需要为用户提供个人文件存储空间的应用(如文档管理、报表系统)
- 需要团队共享文件的协作应用(通过 .public/ 目录)
- 需要 Owner 分发文件给团队成员的管理应用
- 需要批量文件处理的自动化服务(通过 M2M 认证)
nbs-file-client.js 提供了 NBSFileClient 类,用于所有文件操作。它需要一个 NBSAuth 实例进行认证。
<script src="./auth.js"></script>
<script src="./nbs-file-client.js"></script>
<script>
// SSO 初始化完成后:
const fileClient = new NBSFileClient(config.api_gateway_url, auth);
</script>
const fileClient = new NBSFileClient(apiEndpoint, authClient);
// apiEndpoint — API Gateway 端点 URL(字符串)
// authClient — 一个 NBSAuth 实例(必须已初始化并完成认证)
| Method | Returns | 描述 |
|---|---|---|
getUserApps() |
Promise<{ apps, role, totalApps }> |
列出用户可访问的所有应用。 |
uploadFile(appId, path, file) |
Promise<{ key, message }> |
上传文件。path 格式:"directory/filename" 或 "filename"。 |
downloadFile(appId, path) |
Promise<Blob> |
下载文件。返回 Blob 对象。 |
deleteFile(appId, path) |
Promise<{ message }> |
软删除文件。 |
listFiles(appId, options) |
Promise<Object> |
列出文件,支持过滤和分页。 |
listTeamSharedFiles(appId) |
Promise<Array> |
列出 .public/ 目录中的文件。 |
getUserRole(appId) |
Promise<string\|null> |
返回 'owner'、'manager'、'member' 或 null。 |
listFiles 选项await fileClient.listFiles('my-app', {
directory: 'user1', // 目录路径(默认:根目录)
recursive: false, // 递归列出所有文件(仅 owner)
search: '*report*.xlsx', // 通配符搜索模式
includeDeleted: false, // 包含已软删除的文件(仅 owner)
maxKeys: 1000, // 每页最大结果数
continuationToken: null // 上一次响应的分页令牌
});
每个应用在 S3 中的目录布局如下:
{appId}/
├── .private/ # 仅 Owner — 完全读写权限
├── .public/ # 所有角色 — 读写权限
└── {userId}/ # 个人目录
# owner:可查看所有用户目录
# member:仅可查看自己的目录
const fileInput = document.getElementById('fileInput');
const file = fileInput.files[0];
const userId = auth.getUserInfo().sub;
try {
const result = await fileClient.uploadFile('my-app', `${userId}/${file.name}`, file);
console.log('Uploaded:', result.key);
} catch (err) {
console.error('Upload failed:', err.message);
}
async function downloadFile(appId, filePath, downloadName) {
const blob = await fileClient.downloadFile(appId, filePath);
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = downloadName;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
// 用法
await downloadFile('my-app', `${userId}/report.xlsx`, 'report.xlsx');
// 列出用户自己目录中的文件
const result = await fileClient.listFiles('my-app', { directory: userId });
console.log('Files:', result.files);
console.log('Folders:', result.folders);
// 分页列表
let token = null;
do {
const page = await fileClient.listFiles('my-app', {
directory: userId,
maxKeys: 100,
continuationToken: token
});
console.log('Page:', page.files);
token = page.isTruncated ? page.nextContinuationToken : null;
} while (token);
当平台启用了 IP 限制功能时,presigned URL 会被锁定到客户端的 IP 地址(CIDR /24 范围)。这可以防止 URL 被分享——为某个用户生成的 presigned URL 如果从其他 IP 访问会返回 403。
浏览器端应用: 无需额外处理。平台会自动从请求中提取客户端 IP 并限制 presigned URL。
客户端/服务器应用: 您的后端必须将终端用户的真实 IP 转发到 NBS API,以确保 presigned URL 被限制到正确的 IP。通过传递 X-Forwarded-For 头实现:
# Python 后端示例 — 将客户端 IP 转发到 NBS API
def get_download_url(user_token, app_id, directory, filename, client_ip):
resp = requests.post(
f"{NBS_API}/apps/{app_id}/files/download",
headers={
"Authorization": f"Bearer {user_token}",
"Content-Type": "application/json",
"X-Forwarded-For": client_ip # 转发终端用户的真实 IP
},
json={"directory": directory, "filename": filename}
)
return resp.json()["downloadUrl"]
如果您的后端有自己的 API Gateway 并附加了 Lambda Authorizer: API Gateway 可能会在 authorizer 和主 Lambda 之间修改 X-Forwarded-For 头。为确保原始客户端 IP 被保留,让您的 authorizer 在返回的 context 中传递原始 IP(例如 originalXFF 和 originalSourceIp 字段)。NBS 平台自带的 authorizer Lambda 已经实现了这一点——如果您使用的是 NBS authorizer Lambda,则无需额外操作。
关键规则: 请求链路中的每个代理或网关都必须通过
X-Forwarded-For转发原始客户端 IP。如果任何一个环节丢弃了这个头,presigned URL 将被限制到错误的 IP,终端用户的下载将返回 403 错误。
所有 NBSFileClient 方法自动处理令牌过期。收到 401 响应时,客户端会:
1. 调用 auth.refreshAccessToken() 获取新令牌
2. 使用新令牌重试原始请求一次
您的应用代码中无需手动编写重试逻辑。
模块作用: 审计服务为您的应用提供结构化的操作日志记录能力,用于合规审计和问题追溯。所有审计记录统一存储在 NBS 平台的审计日志表中,平台管理员可通过审计控制台查询和分析。
适用场景: - 需要记录用户关键操作(如查看报表、导出数据、修改配置)的合规应用 - 需要追踪文件访问记录的安全敏感应用 - 需要从后端服务记录操作日志的自动化系统(通过 M2M 认证)
审计服务是一个 REST API — 无需 JS 库。直接使用 fetch 调用即可。
POST /apps/{appId}/audit/log
在请求头中包含访问令牌作为 Bearer 令牌:
Authorization: Bearer <access_token>
{
"operation": "view_report",
"status": "success",
"operationDetails": { "reportId": "R-001", "format": "pdf" },
"filePath": "user1/reports/Q1.pdf",
"errorMessage": ""
}
| 字段 | 必填 | 约束 |
|---|---|---|
operation |
是 | 正则:^[a-zA-Z0-9_:./-]{1,100}$ |
status |
否 | 可选值:success、failed、error、partial。默认:success |
operationDetails |
否 | JSON 对象,序列化后最大 4 KB |
filePath |
否 | 最大 500 个字符,不含控制字符 |
errorMessage |
否 | 最大 1000 个字符 |
/**
* 为当前用户记录审计事件。
*
* @param {NBSAuth} auth - 已认证的 NBSAuth 实例
* @param {string} apiEndpoint - API Gateway URL
* @param {string} appId - 您的应用 ID
* @param {string} operation - 操作名称(例如 'view_report'、'export_data')
* @param {Object} [details] - 可选的操作详情
* @param {string} [status] - 可选的状态(默认:'success')
*/
async function logAudit(auth, apiEndpoint, appId, operation, details = {}, status = 'success') {
const token = await auth.getValidAccessToken();
const response = await fetch(`${apiEndpoint}/apps/${appId}/audit/log`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
operation,
status,
operationDetails: details
})
});
if (!response.ok) {
console.error('Audit log failed:', response.status);
}
}
// 记录成功的报表查看
await logAudit(auth, apiEndpoint, 'my-app', 'view_report', {
reportId: 'R-001',
format: 'pdf'
});
// 记录失败的导出
await logAudit(auth, apiEndpoint, 'my-app', 'export_data', {
format: 'csv',
recordCount: 5000
}, 'failed');
// 记录带文件路径的操作
const token = await auth.getValidAccessToken();
await fetch(`${apiEndpoint}/apps/my-app/audit/log`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
operation: 'download_file',
status: 'success',
filePath: 'user1/reports/Q1-2024.xlsx',
operationDetails: { fileSize: '2.4MB' }
})
});
{
"message": "Audit log recorded",
"userId": "user1",
"appId": "my-app",
"operation": "view_report",
"timestamp": "2024-01-15T08:30:00.000Z"
}
| 模式 | 行为 | 配置 |
|---|---|---|
| Public | 所有已认证的 NBS 用户均可访问应用。用户默认获得 member 角色。应用管理员仍可为特定用户分配 owner 和 manager 角色以获得更高权限。 |
平台管理员在注册时将访问模式设置为 public。 |
| Whitelist | 仅明确添加的用户可以访问。未授权用户将收到 403 FORBIDDEN_ACCESS 错误。 |
平台管理员将访问模式设置为 whitelist。应用管理员(owner)通过应用管理控制台管理用户 — 日常用户管理无需平台管理员参与。 |
verifyToken() 会对不在列表中的用户抛出 FORBIDDEN_ACCESS 错误。请在 UI 中妥善处理此情况。| 角色 | 文件访问权限 | 描述 |
|---|---|---|
owner |
完全访问所有目录,包括 .private/ |
应用管理员 — 可查看所有用户目录,全局管理文件 |
manager |
除 .private/ 外的所有目录 |
团队负责人 — 可查看所有用户目录,但无法访问私有存储 |
member |
仅限自己的 {userId}/ 目录和 .public/ |
普通用户 — 个人存储和共享文件 |
角色由平台管理员或应用 owner 分配。角色信息通过 verifyToken() 的 userRole 字段返回。
| 环境 | 用途 | API 端点 |
|---|---|---|
| 实验环境 | 集成测试、开发 | 由平台团队提供 |
| 生产环境 | 正式用户、真实数据 | 由平台团队提供 |
通过更新 config.json 切换环境:
{
"api_gateway_url": "https://EXPERIMENTAL-ENDPOINT.execute-api.region.amazonaws.com/prod"
}
→ 切换到生产环境:
{
"api_gateway_url": "https://PRODUCTION-ENDPOINT.execute-api.region.amazonaws.com/prod"
}
提示: 保留单独的
config-experimental.json和config-production.json文件,在部署时将相应文件复制为config.json。
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 401 Unauthorized | 令牌过期或无效 | NBSAuth 会自动刷新令牌。如果问题持续,调用 auth.login() 重新认证。 |
| 403 Forbidden | 用户不在白名单中 | 联系平台管理员将用户添加到应用白名单。 |
| CORS 错误 | 您的域名不在允许的来源列表中 | 联系平台管理员将您的域名添加到 API Gateway CORS 配置中。 |
verifyToken 抛出 FORBIDDEN_ACCESS |
应用处于 whitelist 模式且用户无权限 | 显示"无访问权限"提示。用户需由平台管理员或应用 owner 添加。 |
verifyToken 抛出 TOKEN_EXPIRED |
访问令牌已过期 | 调用 auth.refreshAccessToken() 然后重试 verifyToken()。 |
verifyToken 抛出 SYSTEM_ERROR |
后端错误或网络问题 | 短暂延迟后重试。如果问题持续,检查 API 端点配置和网络连接。 |
| 文件上传失败 | 路径格式不正确或权限不足 | 确保路径格式为 "directory/filename"。Member 只能上传到自己的 {userId}/ 目录或 .public/。 |
config.json 加载失败 |
路径错误或 CORS 问题 | 确保 config.json 与 HTML 文件在同一目录下。检查浏览器开发者工具的网络标签页。 |
我们提供了一个完整的 SDK 参考包(位于 bs-sdk/ 目录),包含可运行的示例应用,替代内联代码示例。
nbs-sdk/
├── README.md # 快速开始指南
├── KIRO_PROMPTS.md # AI 辅助集成提示词(见下方)
├── lib/ # 共享 JS 库(复制到您的项目)
│ ├── auth.js # NBSAuth — OAuth 2.0 PKCE 认证
│ └── nbs-file-client.js # NBSFileClient — 文件操作
├── browser-app/ # 浏览器端 (BS) 示例
│ ├── config.json # API 端点配置
│ └── index.html # 完整演示:SSO + 文件 + 审计
└── server-app/ # 客户端/服务器 (CS) 示例
├── config.json # API 端点配置
├── index.html # 前端:SSO 登录,调用后端
└── server.py # Python 后端:Federate 中转 + M2M
单个 HTML 文件演示了三项集成(SSO + 文件 + 审计),完全在浏览器中运行。试用方法:
前端 + Python 后端演示两种模式: - Federate 中转: 前端做 SSO 登录,将令牌传给后端,后端中转到 NBS API - M2M(机器对机器): 后端使用自己的 Cognito 凭证调用 NBS API,无需用户会话
试用方法: 1. 更新两个 config.json 的 API 端点 2. 设置环境变量:NBS_API_URL、COGNITO_TOKEN_ENDPOINT、COGNITO_CLIENT_ID、COGNITO_CLIENT_SECRET 3. 运行:pip install requests flask flask-cors && python server.py 4. 打开 http://localhost:3000 访问前端
SDK 包含 KIRO_PROMPTS.md,提供了可直接使用的 Kiro(或其他 AI 编程助手)提示词。在编辑器中打开相关示例文件,然后复制粘贴对应的提示词:
| 提示词 | 场景 | 参考文件 |
|---|---|---|
| Prompt 1 | 在 Web 应用中集成 Federate SSO(浏览器端) | lib/auth.js, rowser-app/index.html |
| Prompt 2 | 在客户端/服务器应用中集成 Federate SSO | lib/auth.js, server-app/index.html, server-app/server.py |
| Prompt 3 | 集成 Cognito M2M 认证(服务间通信) | server-app/server.py |
| Prompt 4 | 集成 NBS 文件服务 | lib/nbs-file-client.js, rowser-app/index.html |
| Prompt 5 | 集成 NBS 审计服务 | rowser-app/index.html, server-app/server.py |
| Prompt 6 | 全量集成(SSO + 文件 + 审计) | rowser-app/ 或 server-app/ 全部文件 |
提示: 在发送提示词之前,先在编辑器中打开上表列出的参考文件,这样 Kiro 就能获取生成准确代码所需的上下文。记得将提示词中的 {YOUR_APP_ID} 等占位符替换为您的实际值。
config.json → { "api_gateway_url": "https://..." }
auth.js → NBSAuth 类(SSO、令牌、会话)
nbs-file-client.js → NBSFileClient 类(文件操作)
POST /apps/{appId}/audit/log → 审计日志(REST,无需库)
最小集成清单:
| 步骤 | 操作 | 必要性 |
|---|---|---|
| 1 | 复制 auth.js + config.json |
必须 |
| 2 | 初始化 NBSAuth,处理回调,验证令牌 |
必须 |
| 3 | 复制 nbs-file-client.js,创建 NBSFileClient |
可选(需要文件服务时) |
| 4 | 调用审计 REST API 记录操作日志 | 可选(需要审计时) |
| 5 | 在实验环境中测试 | 必须 |
| 6 | 切换到生产环境端点并上线 | 必须 |