身份验证让你可以运行私有注册表,控制谁可以访问你的组件,并为不同的团队或用户提供不同的内容。本指南展示了常见的身份验证模式以及如何设置它们。
🌐 Authentication lets you run private registries, control who can access your components, and give different teams or users different content. This guide shows common authentication patterns and how to set them up.
身份验证支持以下用例:
🌐 Authentication enables these use cases:
- 私有组件:保持你的业务逻辑和内部组件安全
- 针对团队的资源:给不同的团队不同的组件
- 访问控制:限制谁可以看到敏感或实验性组件
- 使用分析:查看贵组织中谁在使用哪些组件
- 许可:控制谁可以获得高级或许可组件
常见认证模式
🌐 Common Authentication Patterns
基于令牌的认证
🌐 Token-Based Authentication
最常用的方法是使用Bearer令牌或API密钥:
🌐 The most common approach uses Bearer tokens or API keys:
{
"registries": {
"@private": {
"url": "https://registry.company.com/{name}.json",
"headers": {
"Authorization": "Bearer ${REGISTRY_TOKEN}"
}
}
}
}在环境变量中设置你的令牌:
🌐 Set your token in environment variables:
REGISTRY_TOKEN=your_secret_token_hereAPI 密钥认证
🌐 API Key Authentication
一些注册表在头部使用 API 密钥:
🌐 Some registries use API keys in headers:
{
"registries": {
"@company": {
"url": "https://api.company.com/registry/{name}.json",
"headers": {
"X-API-Key": "${API_KEY}",
"X-Workspace-Id": "${WORKSPACE_ID}"
}
}
}
}查询参数认证
🌐 Query Parameter Authentication
对于更简单的设置,请使用查询参数:
🌐 For simpler setups, use query parameters:
{
"registries": {
"@internal": {
"url": "https://registry.company.com/{name}.json",
"params": {
"token": "${ACCESS_TOKEN}"
}
}
}
}这会创建:https://registry.company.com/button.json?token=your_token
🌐 This creates: https://registry.company.com/button.json?token=your_token
服务器端实现
🌐 Server-Side Implementation
以下是如何向你的注册表服务器添加身份验证的方法:
🌐 Here's how to add authentication to your registry server:
Next.js API 路由示例
🌐 Next.js API Route Example
import { NextRequest, NextResponse } from "next/server"
export async function GET(
request: NextRequest,
{ params }: { params: { name: string } }
) {
// Get token from Authorization header.
const authHeader = request.headers.get("authorization")
const token = authHeader?.replace("Bearer ", "")
// Or from query parameters.
const queryToken = request.nextUrl.searchParams.get("token")
// Check if token is valid.
if (!isValidToken(token || queryToken)) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 })
}
// Check if token can access this component.
if (!hasAccessToComponent(token, params.name)) {
return NextResponse.json({ error: "Forbidden" }, { status: 403 })
}
// Return the component.
const component = await getComponent(params.name)
return NextResponse.json(component)
}
function isValidToken(token: string | null) {
// Add your token validation logic here.
// Check against database, JWT validation, etc.
return token === process.env.VALID_TOKEN
}
function hasAccessToComponent(token: string, componentName: string) {
// Add role-based access control here.
// Check if token can access specific component.
return true // Your logic here.
}Express.js 示例
🌐 Express.js Example
app.get("/registry/:name.json", (req, res) => {
const token = req.headers.authorization?.replace("Bearer ", "")
if (!isValidToken(token)) {
return res.status(401).json({ error: "Unauthorized" })
}
const component = getComponent(req.params.name)
if (!component) {
return res.status(404).json({ error: "Component not found" })
}
res.json(component)
})高级身份验证模式
🌐 Advanced Authentication Patterns
基于团队的访问
🌐 Team-Based Access
给不同的团队不同的组件:
🌐 Give different teams different components:
async function GET(request: NextRequest) {
const token = extractToken(request)
const team = await getTeamFromToken(token)
// Get components for this team.
const components = await getComponentsForTeam(team)
return NextResponse.json(components)
}用户个性化注册表
🌐 User-Personalized Registries
根据用户的偏好提供组件:
🌐 Give users components based on their preferences:
async function GET(request: NextRequest) {
const user = await authenticateUser(request)
// Get user's style and framework preferences.
const preferences = await getUserPreferences(user.id)
// Get personalized component version.
const component = await getPersonalizedComponent(params.name, preferences)
return NextResponse.json(component)
}临时访问令牌
🌐 Temporary Access Tokens
使用即将到期的令牌以提高安全性:
🌐 Use expiring tokens for better security:
interface TemporaryToken {
token: string
expiresAt: Date
scope: string[]
}
async function validateTemporaryToken(token: string) {
const tokenData = await getTokenData(token)
if (!tokenData) return false
if (new Date() > tokenData.expiresAt) return false
return true
}多注册表身份验证
🌐 Multi-Registry Authentication
使用 命名空间注册表,你可以设置多个具有不同身份验证的注册表:
🌐 With namespaced registries, you can set up multiple registries with different authentication:
{
"registries": {
"@public": "https://public.company.com/{name}.json",
"@internal": {
"url": "https://internal.company.com/{name}.json",
"headers": {
"Authorization": "Bearer ${INTERNAL_TOKEN}"
}
},
"@premium": {
"url": "https://premium.company.com/{name}.json",
"headers": {
"X-License-Key": "${LICENSE_KEY}"
}
}
}
}这让你能够:
🌐 This lets you:
- 混合公共和私有注册表
- 每个注册表使用不同的认证
- 按访问级别组织组件
安全最佳实践
🌐 Security Best Practices
使用环境变量
🌐 Use Environment Variables
绝不要将令牌提交到版本控制中。始终使用环境变量:
🌐 Never commit tokens to version control. Always use environment variables:
REGISTRY_TOKEN=your_secret_token_here
API_KEY=your_api_key_here然后在 components.json 中引用它们:
🌐 Then reference them in components.json:
{
"registries": {
"@private": {
"url": "https://registry.company.com/{name}.json",
"headers": {
"Authorization": "Bearer ${REGISTRY_TOKEN}"
}
}
}
}使用 HTTPS
🌐 Use HTTPS
始终使用 HTTPS URL 访问注册表,以保护传输中的令牌:
🌐 Always use HTTPS URLs for registries to protect your tokens in transit:
{
"@secure": "https://registry.company.com/{name}.json" // ✅
"@insecure": "http://registry.company.com/{name}.json" // ❌
}添加速率限制
🌐 Add Rate Limiting
保护你的注册表免受滥用:
🌐 Protect your registry from abuse:
import rateLimit from "express-rate-limit"
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 requests per windowMs
})
app.use("/registry", limiter)旋转令牌
🌐 Rotate Tokens
定期更换访问令牌:
🌐 Change access tokens regularly:
// Create new token with expiration.
function generateToken() {
const token = crypto.randomBytes(32).toString("hex")
const expiresAt = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000) // 30 days.
return { token, expiresAt }
}日志访问
🌐 Log Access
跟踪注册表访问以进行安全和分析:
🌐 Track registry access for security and analytics:
async function logAccess(request: Request, component: string, userId: string) {
await db.accessLog.create({
timestamp: new Date(),
userId,
component,
ip: request.ip,
userAgent: request.headers["user-agent"],
})
}测试认证
🌐 Testing Authentication
在本地测试你的认证注册表:
🌐 Test your authenticated registry locally:
# Test with curl.
curl -H "Authorization: Bearer your_token" \
https://registry.company.com/button.json
# Test with the CLI.
REGISTRY_TOKEN=your_token npx shadcn@latest add @private/button错误处理
🌐 Error Handling
shadcn CLI 能够优雅地处理身份验证错误:
🌐 The shadcn CLI handles authentication errors gracefully:
- 401 未授权:令牌无效或缺失
- 403 禁止访问:令牌没有此资源的权限
- 429 请求过多:超出速率限制
自定义错误消息
🌐 Custom Error Messages
你的注册服务器可以在响应正文中返回自定义错误消息,CLI 将向用户显示这些消息:
🌐 Your registry server can return custom error messages in the response body, and the CLI will display them to users:
// Registry server returns custom error
return NextResponse.json(
{
error: "Unauthorized",
message:
"Your subscription has expired. Please renew at company.com/billing",
},
{ status: 403 }
)用户将看到:
🌐 The user will see:
Your subscription has expired. Please renew at company.com/billing这有助于提供特定情境的指导:
🌐 This helps provide context-specific guidance:
// Different error messages for different scenarios
if (!token) {
return NextResponse.json(
{
error: "Unauthorized",
message:
"Authentication required. Set REGISTRY_TOKEN in your .env.local file",
},
{ status: 401 }
)
}
if (isExpiredToken(token)) {
return NextResponse.json(
{
error: "Unauthorized",
message: "Token expired. Request a new token at company.com/tokens",
},
{ status: 401 }
)
}
if (!hasTeamAccess(token, component)) {
return NextResponse.json(
{
error: "Forbidden",
message: `Component '${component}' is restricted to the Design team`,
},
{ status: 403 }
)
}下一步
🌐 Next Steps
要设置多个注册表和高级模式的身份验证,请参阅 命名空间注册表 文档。它涵盖了:
🌐 To set up authentication with multiple registries and advanced patterns, see the Namespaced Registries documentation. It covers:
- 设置多个经过身份验证的注册表
- 为每个命名空间使用不同的身份验证
- 跨注册表依赖解析
- 高级身份验证模式