Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 75 additions & 2 deletions docs/03-authentication-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
┌─────────────────────────────┐
│ Layer 1: OAuth2 Login │ Spring Security OAuth2 Client
│ (一期 GitHub,可扩展) │ 授权码模式 (Authorization Code)
│ Layer 1b: Session Bootstrap│ 显式被动会话引导(默认关闭)
└─────────────┬───────────────┘
│ OAuth2User
▼
Expand Down Expand Up @@ -141,7 +142,78 @@ AuthenticationSuccessHandler:
② 重定向到前端页面 (可配置的 redirect_uri)
```

### 3.1 Spring Security 配置要点
### 3.1 统一 Session 建立约束

所有 Web 登录入口都必须通过统一的 `PlatformSessionService` 建立登录态,包括:

- 本地用户名密码登录
- OAuth 登录成功回调
- `POST /api/v1/auth/direct/login`
- `POST /api/v1/auth/session/bootstrap`
- 本地开发态 `MockAuthFilter`

统一约束如下:

- 统一写入 `platformPrincipal`
- 统一写入 `SPRING_SECURITY_CONTEXT`
- 统一通过 `HttpSession` 持久化,确保 Spring Session Redis 能无差别接管
- 交互式登录默认调用 `changeSessionId()`,降低 session fixation 风险
- 已由 Spring Security 完成认证的入口可以复用现有 `Authentication`,避免重复构造认证结果

这意味着未来私有版新增企业 SSO provider 时,只能扩展认证来源本身,不能绕开统一的 session 建立服务直接操作 Session。

## 3.3 Session Bootstrap 扩展点

为了兼容未来私有部署中的企业 SSO 被动登录,开源版预留显式会话引导协议:

- 接口:`POST /api/v1/auth/session/bootstrap`
- 用途:前端在同域场景下显式触发一次“读取外部会话并尝试换取 skillhub Session”的流程
- 默认状态:关闭,开源版不提供任何 `PassiveSessionAuthenticator` 实现
- 安全边界:默认不做全局自动登录 filter,避免匿名访问时隐式建会话、放大 CSRF 和审计复杂度

扩展接口如下:

```java
public interface PassiveSessionAuthenticator {
String providerCode();
Optional<PlatformPrincipal> authenticate(HttpServletRequest request);
}
```

约束如下:

- `authenticate()` 只负责验证外部被动会话并返回平台登录所需主体
- 是否允许启用该入口由 `skillhub.auth.session-bootstrap.enabled` 控制,默认 `false`
- 未启用时接口返回 `403`
- 启用但 provider 不受支持时返回 `400`
- 启用但请求中不存在有效外部会话时返回 `401`
- 成功时建立标准 Spring Security Session,并返回与 `/api/v1/auth/me` 一致的用户结构

## 3.4 Direct Authentication 扩展点

为兼容未来“前端收集用户名密码,后端调用企业 SSO / RPC 校验”的私有部署模式,开源版增加默认关闭的直连认证抽象:

```java
public interface DirectAuthProvider {
String providerCode();
PlatformPrincipal authenticate(DirectAuthRequest request);
}
```

对应公共协议:

- `POST /api/v1/auth/direct/login`

约束如下:

- 开源版默认关闭,由 `skillhub.auth.direct.enabled` 控制
- 关闭时返回 `403`
- provider 不受支持时返回 `400`
- provider 认证失败时沿用 provider 自身的认证异常语义
- 成功时建立标准 Session,并返回与 `/api/v1/auth/me` 一致的用户结构
- 现有 `/api/v1/auth/local/login` 保持不变,兼容层只是新增可选入口

### 3.5 Spring Security 配置要点

```java
@Configuration
Expand All @@ -168,7 +240,7 @@ public class SecurityConfig {
}
```

### 3.2 OAuth2 Provider 扩展设计
### 3.6 OAuth2 Provider 扩展设计

一期只实现 GitHub,但架构支持后续扩展:

Expand Down Expand Up @@ -417,6 +489,7 @@ Session 中存储以下字段:

统一约束:
- `/api/v1/auth/me`、`/api/v1/auth/providers` 等 JSON 响应必须统一使用 `code/msg/data/timestamp/requestId` 外层结构。
- `/api/v1/auth/session/bootstrap` 也必须遵守同一统一响应结构。
- `msg` 必须走 Spring Boot 标准 `MessageSource` i18n 机制。
- locale 必须通过请求上下文自动获取,不在 controller 中显式传递。
- 认证失败返回 `401`,但 JSON 外层结构仍保持一致,例如 `{"code":401,"msg":"需要先登录","data":null,...}`。
Expand Down
73 changes: 72 additions & 1 deletion docs/06-api-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ Public API 的可见性规则:
- `parsedMetadataJson`:`SKILL.md` frontmatter 的完整 JSON 序列化结果
- `manifestJson`:版本文件清单摘要 JSON

## 7.2 Auth API(OAuth2 登录相关)
## 7.2 Auth API(登录与会话相关)

| 方法 | 路径 | 说明 |
|------|------|------|
Expand All @@ -104,6 +104,9 @@ Public API 的可见性规则:
| GET | `/api/v1/auth/me` | 当前用户信息(未登录返回 401) |
| POST | `/api/v1/auth/logout` | 登出(清除 Session) |
| GET | `/api/v1/auth/providers` | 可用的 OAuth Provider 列表(前端渲染登录按钮用) |
| GET | `/api/v1/auth/methods` | 统一登录方式目录(密码/OAuth/direct/bootstrap 元数据) |
| POST | `/api/v1/auth/direct/login` | 显式走直连认证 provider 的兼容登录入口(默认关闭) |
| POST | `/api/v1/auth/session/bootstrap` | 显式尝试用外部被动会话换取 skillhub Session(默认关闭) |

`/api/v1/auth/providers` 响应示例:

Expand All @@ -121,6 +124,74 @@ Public API 的可见性规则:

前端根据此接口动态渲染登录按钮,新增 Provider 无需改前端代码。

`/api/v1/auth/methods` 返回统一登录方式目录。典型项包括:

- `PASSWORD`:现有本地账号密码登录
- `OAUTH_REDIRECT`:OAuth 跳转登录
- `DIRECT_PASSWORD`:默认关闭的直连认证兼容入口
- `SESSION_BOOTSTRAP`:默认关闭的被动会话引导入口

示例:

```json
{
"code": 0,
"msg": "获取成功",
"data": [
{
"id": "local-password",
"methodType": "PASSWORD",
"provider": "local",
"displayName": "Local Account",
"actionUrl": "/api/v1/auth/local/login"
},
{
"id": "oauth-github",
"methodType": "OAUTH_REDIRECT",
"provider": "github",
"displayName": "GitHub",
"actionUrl": "/oauth2/authorization/github"
}
],
"timestamp": "2026-03-12T06:00:00Z",
"requestId": "req-123"
}
```

`/api/v1/auth/session/bootstrap` 请求示例:

```json
{
"provider": "private-sso"
}
```

`/api/v1/auth/session/bootstrap` 协议约束:

- 开源版默认关闭,需显式开启 `skillhub.auth.session-bootstrap.enabled=true`
- 关闭时返回 `403`
- provider 不存在时返回 `400`
- 外部会话不存在或校验失败时返回 `401`
- 成功时返回与 `/api/v1/auth/me` 相同的用户结构,并建立标准 Session

`/api/v1/auth/direct/login` 请求示例:

```json
{
"provider": "private-sso",
"username": "alice",
"password": "secret"
}
```

`/api/v1/auth/direct/login` 协议约束:

- 开源版默认关闭,需显式开启 `skillhub.auth.direct.enabled=true`
- 关闭时返回 `403`
- provider 不存在时返回 `400`
- 成功时返回与 `/api/v1/auth/me` 相同的用户结构,并建立标准 Session
- `/api/v1/auth/local/login` 继续保留,作为现有本地账号入口

## 7.3 Authenticated API(需登录)

| 方法 | 路径 | 说明 |
Expand Down
34 changes: 33 additions & 1 deletion docs/08-frontend-architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,39 @@ window.location.href = "/oauth2/authorization/github"
2. 跳转到对应的 `authorizationUrl`
3. 回调后通过 `/api/v1/auth/me` 检测登录态

### 4.2 登录态检测
### 4.2 预留的被动会话引导

为未来私有部署下的企业 SSO 兼容,前端可在登录页或应用初始化阶段显式调用:

- `POST /api/v1/auth/session/bootstrap`

该接口在开源版默认关闭;私有版启用后,前端可在检测到用户未登录时主动调用一次,以尝试将外部 SSO Cookie 换成 skillhub Session。该流程必须保持显式触发,不默认依赖全局透明拦截器。

前端兼容接入层约束如下:

- 默认不启用,运行时配置不打开时,登录页和全局行为与开源版完全一致
- 账号密码登录兼容层与被动会话兼容层相互独立,可单独启用
- 启用后,登录页会出现一个“企业 SSO”兼容入口
- 启用密码兼容层后,登录页账号密码表单会改为调用通用直连认证接口
- 前端应优先消费 `/api/v1/auth/methods` 作为统一登录方式目录;`/api/v1/auth/providers` 仅保留兼容
- 可选自动尝试,但仍限定在登录页内执行,不在全站每次匿名访问时自动探测
- bootstrap 失败时应静默回退到现有本地登录和 OAuth 登录,不打断正常流程

前端运行时配置项:

- `SKILLHUB_WEB_AUTH_DIRECT_ENABLED`
- `SKILLHUB_WEB_AUTH_DIRECT_PROVIDER`
- `SKILLHUB_WEB_AUTH_SESSION_BOOTSTRAP_ENABLED`
- `SKILLHUB_WEB_AUTH_SESSION_BOOTSTRAP_PROVIDER`
- `SKILLHUB_WEB_AUTH_SESSION_BOOTSTRAP_AUTO`

推荐策略:

- 私有版密码直连:`auth_direct_enabled=true`,`auth_direct_provider=private-sso`
- 私有版初期:`enabled=true`,`provider=private-sso`,`auto=false`
- 验证稳定后:再评估是否切到 `auto=true`

### 4.3 登录态检测

```
页面加载 → GET /api/v1/auth/me
Expand Down
22 changes: 22 additions & 0 deletions docs/09-deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,28 @@ docker compose --env-file .env.release -f compose.release.yml up -d

## 7 配置管理

前端运行时配置通过 `web/runtime-config.js.template` 注入。与认证兼容层相关的新变量如下:

- `SKILLHUB_WEB_AUTH_DIRECT_ENABLED`
- 是否在前端打开账号密码兼容接入层
- 默认应为 `false`
- `SKILLHUB_WEB_AUTH_DIRECT_PROVIDER`
- 前端调用 `/api/v1/auth/direct/login` 时使用的 provider,例如 `private-sso`
- `SKILLHUB_WEB_AUTH_SESSION_BOOTSTRAP_ENABLED`
- 是否在前端打开企业 SSO 被动会话兼容入口
- 默认应为 `false`
- `SKILLHUB_WEB_AUTH_SESSION_BOOTSTRAP_PROVIDER`
- 前端调用 `/api/v1/auth/session/bootstrap` 时使用的 provider,例如 `private-sso`
- `SKILLHUB_WEB_AUTH_SESSION_BOOTSTRAP_AUTO`
- 是否在登录页加载后自动尝试一次 bootstrap
- 建议私有版初期保持 `false`

注意:

- 前端密码兼容层打开之前,后端仍必须同步打开 `skillhub.auth.direct.enabled=true`
- 前端开关打开之前,后端仍必须同步打开 `skillhub.auth.session-bootstrap.enabled=true`
- 前后端任一侧未开启,都不会破坏原有登录方式;只会使该兼容入口不可用或不显示

开发环境:

- 本地命令与 `docker-compose.yml`
Expand Down
Loading