Skip to content
Closed
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
196 changes: 196 additions & 0 deletions docs/1.18.0/design/系统用户禁止登录改造_设计.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
# 阶段2:技术设计方案

## 1. 设计概述

### 1.1 设计目标
在现有登录拦截逻辑基础上进行增强,将登录来源判断方式从 request body 的 `source` 字段改为 HTTP Header 的 `webLogin` 字段。

### 1.2 设计原则
- **最小改动**: 复用现有拦截逻辑,仅修改来源判断方式
- **向后兼容**: 默认功能关闭,不影响现有系统
- **可配置性**: 支持配置开关和系统用户前缀列表

## 2. 架构设计

### 2.1 组件关系图

```
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Web Frontend │────>│ Gateway Server │────>│ Backend API │
│ │ │ │ │ │
│ Header: │ │ UserRestful │ │ │
│ webLogin=true │ │ ↓ │ │ │
└─────────────────┘ │ tryLogin() │ └─────────────────┘
│ ↓ │
│ isWebLogin() │
│ ↓ │
│ checkSystemUser │
└─────────────────┘
```

### 2.2 处理流程

```
┌─────────────────────────────────────────────────────────────────┐
│ 登录请求处理流程 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ ┌───────────────┐ ┌────────────────────┐ │
│ │ 接收请求 │───>│ 获取用户名密码 │───>│ 检查功能开关是否开启 │ │
│ └──────────┘ └───────────────┘ └─────────┬──────────┘ │
│ │ │
│ ┌─────────────┴─────────────┐ │
│ │ 开关状态? │ │
│ └─────────────┬─────────────┘ │
│ 关闭 │ │ 开启 │
│ ▼ ▼ │
│ ┌─────────────┐ ┌─────────────────┐ │
│ │ 继续正常登录 │ │ 从Header获取 │ │
│ └─────────────┘ │ webLogin标识 │ │
│ └────────┬────────┘ │
│ │ │
│ ┌─────────────┴───────────┐ │
│ │ webLogin == "true"? │ │
│ └─────────────┬───────────┘ │
│ false │ │ true │
│ ▼ ▼ │
│ ┌─────────────┐ ┌───────────────┐ │
│ │ 继续正常登录 │ │ 检查用户名前缀 │ │
│ └─────────────┘ └───────┬───────┘ │
│ │ │
│ ┌───────────────┴─────────┐ │
│ │ 匹配系统用户前缀? │ │
│ └───────────────┬─────────┘ │
│ 否 │ │ 是 │
│ ▼ ▼ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ 继续正常登录 │ │ 返回错误信息 │ │
│ └─────────────┘ │ 拒绝登录 │ │
│ └─────────────┘ │
└─────────────────────────────────────────────────────────────────┘
```

## 3. 详细设计

### 3.1 配置项修改

**文件**: `GatewayConfiguration.scala`

| 配置项 | 当前值 | 修改后 |
|--------|--------|--------|
| PROHIBIT_LOGIN_PREFIX | `hduser,shduser` | `hadoop,hduser,shduser` |

**新增配置项**: 无需新增,复用现有配置

### 3.2 代码修改

**文件**: `UserRestful.scala`

#### 3.2.1 新增方法: isWebLogin

```scala
private val WEB_LOGIN_HEADER = "webLogin"

private def isWebLogin(gatewayContext: GatewayContext): Boolean = {
val headers = gatewayContext.getRequest.getHeaders
val webLoginValues = headers.get(WEB_LOGIN_HEADER)
if (webLoginValues != null && webLoginValues.nonEmpty) {
"true".equalsIgnoreCase(webLoginValues.head)
} else {
false // 默认为false
}
}
```

#### 3.2.2 修改tryLogin方法

**现有代码**:
```scala
if (
GatewayConfiguration.PROHIBIT_LOGIN_SWITCH.getValue &&
(!getRequestSource(gatewayContext).equals("client"))
) {
PROHIBIT_LOGIN_PREFIX.split(",").foreach { prefix =>
if (userName.toLowerCase().startsWith(prefix)) {
return Message.error("System users are prohibited from logging in(系统用户禁止登录)!")
}
}
}
```

**修改后**:
```scala
if (
GatewayConfiguration.PROHIBIT_LOGIN_SWITCH.getValue &&
isWebLogin(gatewayContext)
) {
PROHIBIT_LOGIN_PREFIX.split(",").foreach { prefix =>
if (userName.toLowerCase().startsWith(prefix)) {
return Message.error("System users are prohibited from logging in(系统用户禁止登录)!")
}
}
}
```

## 4. 接口设计

### 4.1 登录接口变更

**接口**: POST /api/rest_j/v1/user/login

**新增Header**:
| Header | 类型 | 必填 | 默认值 | 说明 |
|--------|------|------|--------|------|
| webLogin | String | 否 | false | Web页面登录标识 |

**请求示例**:
```http
POST /api/rest_j/v1/user/login HTTP/1.1
Host: gateway.linkis.com
Content-Type: application/json
webLogin: true

{
"userName": "testuser",
"password": "xxx"
}
```

**错误响应** (系统用户被拦截):
```json
{
"method": "/api/rest_j/v1/user/login",
"status": 1,
"message": "System users are prohibited from logging in(系统用户禁止登录)!"
}
```

## 5. 前端配合要求

前端在Web页面调用登录接口时,需要在HTTP请求header中添加:
```javascript
headers: {
'webLogin': 'true'
}
```

## 6. 配置示例

### 6.1 linkis.properties

```properties
# 开启系统用户禁止登录功能
linkis.system.user.prohibit.login.switch=true

# 系统用户前缀列表(逗号分隔)
linkis.system.user.prohibit.login.prefix=hadoop,hduser,shduser
```

## 7. 兼容性说明

| 场景 | 行为 |
|------|------|
| 旧前端(无webLogin header) | 默认webLogin=false,不拦截,正常登录 |
| 客户端登录(无webLogin header) | 默认webLogin=false,不拦截,正常登录 |
| 新前端(webLogin=true) + 普通用户 | 正常登录 |
| 新前端(webLogin=true) + 系统用户 | 拦截,返回错误 |
119 changes: 119 additions & 0 deletions docs/1.18.0/requirements/系统用户禁止登录改造_需求.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# 阶段1:需求分析文档

## 1. 需求概述

### 1.1 背景
根据安全要求,Linkis管理台需要禁止系统用户(如hadoop、hduser、shduser等)通过Web页面登录,以降低安全风险。

### 1.2 目标
- 拦截系统用户的Web页面登录请求
- 不影响客户端(client)及其他渠道的登录
- 提供配置开关和系统用户前缀配置

## 2. 功能需求

### 2.1 登录拦截逻辑

| 编号 | 功能点 | 描述 | 优先级 |
|------|--------|------|--------|
| FR-001 | webLogin标识传递 | 前端在HTTP header中传递`webLogin`标识 | P0 |
| FR-002 | webLogin标识获取 | 后端从header获取标识,默认值为`false` | P0 |
| FR-003 | 系统用户拦截 | 当webLogin=true时,拦截系统用户前缀匹配的用户 | P0 |
| FR-004 | 非Web渠道放行 | webLogin=false或未传时不进行拦截 | P0 |

### 2.2 错误提示

| 编号 | 功能点 | 描述 | 优先级 |
|------|--------|------|--------|
| FR-005 | 统一错误信息 | 拦截时返回"系统用户禁止登录" | P0 |

### 2.3 配置管理

| 编号 | 功能点 | 描述 | 优先级 |
|------|--------|------|--------|
| FR-006 | 功能开关 | `linkis.system.user.prohibit.login.switch` 控制功能开启/关闭 | P0 |
| FR-007 | 系统用户前缀 | `linkis.system.user.prohibit.login.prefix` 配置系统用户前缀列表 | P0 |

## 3. 非功能需求

### 3.1 兼容性
- 现有客户端登录方式不受影响
- 配置项需向后兼容

### 3.2 安全性
- 拦截逻辑不可绕过
- webLogin标识仅用于识别登录来源,不用于认证

### 3.3 可配置性
- 功能可通过配置开关完全关闭
- 系统用户前缀列表可动态配置

## 4. 数据字典

### 4.1 配置项

| 配置项 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| linkis.system.user.prohibit.login.switch | Boolean | false | 禁止系统用户登录功能开关 |
| linkis.system.user.prohibit.login.prefix | String | hadoop,hduser,shduser | 系统用户前缀列表,逗号分隔 |

### 4.2 HTTP Header

| Header名称 | 类型 | 默认值 | 说明 |
|------------|------|--------|------|
| webLogin | String | false | Web页面登录标识,true表示来自Web页面 |

## 5. 用例分析

### 5.1 正常场景

#### UC-001: 普通用户Web登录
- **前置条件**: 功能开关开启
- **输入**: 用户名=testuser, webLogin=true
- **预期**: 登录成功

#### UC-002: 系统用户Client登录
- **前置条件**: 功能开关开启
- **输入**: 用户名=hadoop, webLogin=false
- **预期**: 登录成功

### 5.2 异常场景

#### UC-003: 系统用户Web登录
- **前置条件**: 功能开关开启
- **输入**: 用户名=hadoop, webLogin=true
- **预期**: 登录失败,返回"系统用户禁止登录"

#### UC-004: hduser用户Web登录
- **前置条件**: 功能开关开启
- **输入**: 用户名=hduser01, webLogin=true
- **预期**: 登录失败,返回"系统用户禁止登录"

### 5.3 边界场景

#### UC-005: 功能开关关闭
- **前置条件**: 功能开关关闭
- **输入**: 用户名=hadoop, webLogin=true
- **预期**: 登录成功(不进行拦截)

#### UC-006: webLogin未传递
- **前置条件**: 功能开关开启
- **输入**: 用户名=hadoop, header中无webLogin
- **预期**: 登录成功(默认webLogin=false)

## 6. 影响范围分析

### 6.1 代码改动范围

| 文件 | 改动类型 | 改动内容 |
|------|---------|---------|
| GatewayConfiguration.scala | 修改 | 更新PROHIBIT_LOGIN_PREFIX默认值 |
| UserRestful.scala | 修改 | 修改登录拦截逻辑,从header获取webLogin |

### 6.2 风险评估

| 风险 | 等级 | 缓解措施 |
|------|------|---------|
| 影响正常用户登录 | 低 | 功能开关默认关闭 |
| 前端未传webLogin | 低 | 默认值为false,不拦截 |
| 配置错误导致无法登录 | 中 | 提供配置示例和文档 |
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ object GatewayConfiguration {
val PROHIBIT_LOGIN_SWITCH = CommonVars("linkis.system.user.prohibit.login.switch", false)

val PROHIBIT_LOGIN_PREFIX =
CommonVars("linkis.system.user.prohibit.login.prefix", "hduser,shduser").getValue.toLowerCase()
CommonVars("linkis.system.user.prohibit.login.prefix", "hduser,shduser,hadoop").getValue
.toLowerCase()

}
Original file line number Diff line number Diff line change
Expand Up @@ -307,10 +307,13 @@ abstract class UserPwdAbstractUserRestful extends AbstractUserRestful with Loggi
return Message.error("Password can not be blank(密码不能为空)!")
}

if (
GatewayConfiguration.PROHIBIT_LOGIN_SWITCH.getValue && (!getRequestSource(gatewayContext)
.equals("client"))
) {
// 从header获取webLogin标识,默认为false
val headers = gatewayContext.getRequest.getHeaders
val webLoginHeaders = headers.getOrDefault("webLogin", Array("false"))
val webLogin = java.lang.Boolean.parseBoolean(webLoginHeaders.head)
// 如果是web登录,检查是否为系统用户(包括hadoop用户)
if (GatewayConfiguration.PROHIBIT_LOGIN_SWITCH.getValue && webLogin) {
// 检查是否为系统用户(包括hadoop用户)
PROHIBIT_LOGIN_PREFIX.split(",").foreach { prefix =>
if (userName.toLowerCase().startsWith(prefix)) {
return Message.error("System users are prohibited from logging in(系统用户禁止登录)!")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,18 +72,15 @@ class StaticAuthenticationStrategy(override protected val sessionMaxAliveTime: L
case userPwd: UserPwdAction =>
action.addRequestPayload("userName", userPwd.getUser)
action.addRequestPayload("password", userPwd.getPassword.getOrElse(pwd))
action.addRequestPayload("source", "client")
case userAction: UserAction =>
action.addRequestPayload("userName", userAction.getUser)
action.addRequestPayload("password", pwd)
action.addRequestPayload("source", "client")
case _ =>
if (StringUtils.isBlank(getClientConfig.getAuthTokenKey)) {
throw new AuthenticationFailedException(AUTHTOKENVALUE_BE_EXISTS.getErrorDesc)
}
action.addRequestPayload("userName", getClientConfig.getAuthTokenKey)
action.addRequestPayload("password", pwd)
action.addRequestPayload("source", "client")
}
action
}
Expand Down
2 changes: 2 additions & 0 deletions linkis-web/.env
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ VUE_APP_VERSION=1.8.0
# Enable or disable Python Module feature (true/false)
# Set to false if you cannot access @webank internal npm packages
VUE_APP_ENABLE_PYTHON_MODULE=false
VUE_APP_DISALLOW_LOGIN=true
VUE_APP_DISALLOW_LOGIN_PREFIX=hduser,shduser,hadoop
Loading
Loading