Skip to content

Commit cf2cbf3

Browse files
#AI commit# feat: hadoop用户禁止登录管理台 (#5380)
Co-authored-by: v-kkhuang <420895376@qq.com>
1 parent 7b130d9 commit cf2cbf3

7 files changed

Lines changed: 343 additions & 9 deletions

File tree

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
# 阶段2:技术设计方案
2+
3+
## 1. 设计概述
4+
5+
### 1.1 设计目标
6+
在现有登录拦截逻辑基础上进行增强,将登录来源判断方式从 request body 的 `source` 字段改为 HTTP Header 的 `webLogin` 字段。
7+
8+
### 1.2 设计原则
9+
- **最小改动**: 复用现有拦截逻辑,仅修改来源判断方式
10+
- **向后兼容**: 默认功能关闭,不影响现有系统
11+
- **可配置性**: 支持配置开关和系统用户前缀列表
12+
13+
## 2. 架构设计
14+
15+
### 2.1 组件关系图
16+
17+
```
18+
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
19+
│ Web Frontend │────>│ Gateway Server │────>│ Backend API │
20+
│ │ │ │ │ │
21+
│ Header: │ │ UserRestful │ │ │
22+
│ webLogin=true │ │ ↓ │ │ │
23+
└─────────────────┘ │ tryLogin() │ └─────────────────┘
24+
│ ↓ │
25+
│ isWebLogin() │
26+
│ ↓ │
27+
│ checkSystemUser │
28+
└─────────────────┘
29+
```
30+
31+
### 2.2 处理流程
32+
33+
```
34+
┌─────────────────────────────────────────────────────────────────┐
35+
│ 登录请求处理流程 │
36+
├─────────────────────────────────────────────────────────────────┤
37+
│ │
38+
│ ┌──────────┐ ┌───────────────┐ ┌────────────────────┐ │
39+
│ │ 接收请求 │───>│ 获取用户名密码 │───>│ 检查功能开关是否开启 │ │
40+
│ └──────────┘ └───────────────┘ └─────────┬──────────┘ │
41+
│ │ │
42+
│ ┌─────────────┴─────────────┐ │
43+
│ │ 开关状态? │ │
44+
│ └─────────────┬─────────────┘ │
45+
│ 关闭 │ │ 开启 │
46+
│ ▼ ▼ │
47+
│ ┌─────────────┐ ┌─────────────────┐ │
48+
│ │ 继续正常登录 │ │ 从Header获取 │ │
49+
│ └─────────────┘ │ webLogin标识 │ │
50+
│ └────────┬────────┘ │
51+
│ │ │
52+
│ ┌─────────────┴───────────┐ │
53+
│ │ webLogin == "true"? │ │
54+
│ └─────────────┬───────────┘ │
55+
│ false │ │ true │
56+
│ ▼ ▼ │
57+
│ ┌─────────────┐ ┌───────────────┐ │
58+
│ │ 继续正常登录 │ │ 检查用户名前缀 │ │
59+
│ └─────────────┘ └───────┬───────┘ │
60+
│ │ │
61+
│ ┌───────────────┴─────────┐ │
62+
│ │ 匹配系统用户前缀? │ │
63+
│ └───────────────┬─────────┘ │
64+
│ 否 │ │ 是 │
65+
│ ▼ ▼ │
66+
│ ┌─────────────┐ ┌─────────────┐ │
67+
│ │ 继续正常登录 │ │ 返回错误信息 │ │
68+
│ └─────────────┘ │ 拒绝登录 │ │
69+
│ └─────────────┘ │
70+
└─────────────────────────────────────────────────────────────────┘
71+
```
72+
73+
## 3. 详细设计
74+
75+
### 3.1 配置项修改
76+
77+
**文件**: `GatewayConfiguration.scala`
78+
79+
| 配置项 | 当前值 | 修改后 |
80+
|--------|--------|--------|
81+
| PROHIBIT_LOGIN_PREFIX | `hduser,shduser` | `hadoop,hduser,shduser` |
82+
83+
**新增配置项**: 无需新增,复用现有配置
84+
85+
### 3.2 代码修改
86+
87+
**文件**: `UserRestful.scala`
88+
89+
#### 3.2.1 新增方法: isWebLogin
90+
91+
```scala
92+
private val WEB_LOGIN_HEADER = "webLogin"
93+
94+
private def isWebLogin(gatewayContext: GatewayContext): Boolean = {
95+
val headers = gatewayContext.getRequest.getHeaders
96+
val webLoginValues = headers.get(WEB_LOGIN_HEADER)
97+
if (webLoginValues != null && webLoginValues.nonEmpty) {
98+
"true".equalsIgnoreCase(webLoginValues.head)
99+
} else {
100+
false // 默认为false
101+
}
102+
}
103+
```
104+
105+
#### 3.2.2 修改tryLogin方法
106+
107+
**现有代码**:
108+
```scala
109+
if (
110+
GatewayConfiguration.PROHIBIT_LOGIN_SWITCH.getValue &&
111+
(!getRequestSource(gatewayContext).equals("client"))
112+
) {
113+
PROHIBIT_LOGIN_PREFIX.split(",").foreach { prefix =>
114+
if (userName.toLowerCase().startsWith(prefix)) {
115+
return Message.error("System users are prohibited from logging in(系统用户禁止登录)!")
116+
}
117+
}
118+
}
119+
```
120+
121+
**修改后**:
122+
```scala
123+
if (
124+
GatewayConfiguration.PROHIBIT_LOGIN_SWITCH.getValue &&
125+
isWebLogin(gatewayContext)
126+
) {
127+
PROHIBIT_LOGIN_PREFIX.split(",").foreach { prefix =>
128+
if (userName.toLowerCase().startsWith(prefix)) {
129+
return Message.error("System users are prohibited from logging in(系统用户禁止登录)!")
130+
}
131+
}
132+
}
133+
```
134+
135+
## 4. 接口设计
136+
137+
### 4.1 登录接口变更
138+
139+
**接口**: POST /api/rest_j/v1/user/login
140+
141+
**新增Header**:
142+
| Header | 类型 | 必填 | 默认值 | 说明 |
143+
|--------|------|------|--------|------|
144+
| webLogin | String | 否 | false | Web页面登录标识 |
145+
146+
**请求示例**:
147+
```http
148+
POST /api/rest_j/v1/user/login HTTP/1.1
149+
Host: gateway.linkis.com
150+
Content-Type: application/json
151+
webLogin: true
152+
153+
{
154+
"userName": "testuser",
155+
"password": "xxx"
156+
}
157+
```
158+
159+
**错误响应** (系统用户被拦截):
160+
```json
161+
{
162+
"method": "/api/rest_j/v1/user/login",
163+
"status": 1,
164+
"message": "System users are prohibited from logging in(系统用户禁止登录)!"
165+
}
166+
```
167+
168+
## 5. 前端配合要求
169+
170+
前端在Web页面调用登录接口时,需要在HTTP请求header中添加:
171+
```javascript
172+
headers: {
173+
'webLogin': 'true'
174+
}
175+
```
176+
177+
## 6. 配置示例
178+
179+
### 6.1 linkis.properties
180+
181+
```properties
182+
# 开启系统用户禁止登录功能
183+
linkis.system.user.prohibit.login.switch=true
184+
185+
# 系统用户前缀列表(逗号分隔)
186+
linkis.system.user.prohibit.login.prefix=hadoop,hduser,shduser
187+
```
188+
189+
## 7. 兼容性说明
190+
191+
| 场景 | 行为 |
192+
|------|------|
193+
| 旧前端(无webLogin header) | 默认webLogin=false,不拦截,正常登录 |
194+
| 客户端登录(无webLogin header) | 默认webLogin=false,不拦截,正常登录 |
195+
| 新前端(webLogin=true) + 普通用户 | 正常登录 |
196+
| 新前端(webLogin=true) + 系统用户 | 拦截,返回错误 |
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
# 阶段1:需求分析文档
2+
3+
## 1. 需求概述
4+
5+
### 1.1 背景
6+
根据安全要求,Linkis管理台需要禁止系统用户(如hadoop、hduser、shduser等)通过Web页面登录,以降低安全风险。
7+
8+
### 1.2 目标
9+
- 拦截系统用户的Web页面登录请求
10+
- 不影响客户端(client)及其他渠道的登录
11+
- 提供配置开关和系统用户前缀配置
12+
13+
## 2. 功能需求
14+
15+
### 2.1 登录拦截逻辑
16+
17+
| 编号 | 功能点 | 描述 | 优先级 |
18+
|------|--------|------|--------|
19+
| FR-001 | webLogin标识传递 | 前端在HTTP header中传递`webLogin`标识 | P0 |
20+
| FR-002 | webLogin标识获取 | 后端从header获取标识,默认值为`false` | P0 |
21+
| FR-003 | 系统用户拦截 | 当webLogin=true时,拦截系统用户前缀匹配的用户 | P0 |
22+
| FR-004 | 非Web渠道放行 | webLogin=false或未传时不进行拦截 | P0 |
23+
24+
### 2.2 错误提示
25+
26+
| 编号 | 功能点 | 描述 | 优先级 |
27+
|------|--------|------|--------|
28+
| FR-005 | 统一错误信息 | 拦截时返回"系统用户禁止登录" | P0 |
29+
30+
### 2.3 配置管理
31+
32+
| 编号 | 功能点 | 描述 | 优先级 |
33+
|------|--------|------|--------|
34+
| FR-006 | 功能开关 | `linkis.system.user.prohibit.login.switch` 控制功能开启/关闭 | P0 |
35+
| FR-007 | 系统用户前缀 | `linkis.system.user.prohibit.login.prefix` 配置系统用户前缀列表 | P0 |
36+
37+
## 3. 非功能需求
38+
39+
### 3.1 兼容性
40+
- 现有客户端登录方式不受影响
41+
- 配置项需向后兼容
42+
43+
### 3.2 安全性
44+
- 拦截逻辑不可绕过
45+
- webLogin标识仅用于识别登录来源,不用于认证
46+
47+
### 3.3 可配置性
48+
- 功能可通过配置开关完全关闭
49+
- 系统用户前缀列表可动态配置
50+
51+
## 4. 数据字典
52+
53+
### 4.1 配置项
54+
55+
| 配置项 | 类型 | 默认值 | 说明 |
56+
|--------|------|--------|------|
57+
| linkis.system.user.prohibit.login.switch | Boolean | false | 禁止系统用户登录功能开关 |
58+
| linkis.system.user.prohibit.login.prefix | String | hadoop,hduser,shduser | 系统用户前缀列表,逗号分隔 |
59+
60+
### 4.2 HTTP Header
61+
62+
| Header名称 | 类型 | 默认值 | 说明 |
63+
|------------|------|--------|------|
64+
| webLogin | String | false | Web页面登录标识,true表示来自Web页面 |
65+
66+
## 5. 用例分析
67+
68+
### 5.1 正常场景
69+
70+
#### UC-001: 普通用户Web登录
71+
- **前置条件**: 功能开关开启
72+
- **输入**: 用户名=testuser, webLogin=true
73+
- **预期**: 登录成功
74+
75+
#### UC-002: 系统用户Client登录
76+
- **前置条件**: 功能开关开启
77+
- **输入**: 用户名=hadoop, webLogin=false
78+
- **预期**: 登录成功
79+
80+
### 5.2 异常场景
81+
82+
#### UC-003: 系统用户Web登录
83+
- **前置条件**: 功能开关开启
84+
- **输入**: 用户名=hadoop, webLogin=true
85+
- **预期**: 登录失败,返回"系统用户禁止登录"
86+
87+
#### UC-004: hduser用户Web登录
88+
- **前置条件**: 功能开关开启
89+
- **输入**: 用户名=hduser01, webLogin=true
90+
- **预期**: 登录失败,返回"系统用户禁止登录"
91+
92+
### 5.3 边界场景
93+
94+
#### UC-005: 功能开关关闭
95+
- **前置条件**: 功能开关关闭
96+
- **输入**: 用户名=hadoop, webLogin=true
97+
- **预期**: 登录成功(不进行拦截)
98+
99+
#### UC-006: webLogin未传递
100+
- **前置条件**: 功能开关开启
101+
- **输入**: 用户名=hadoop, header中无webLogin
102+
- **预期**: 登录成功(默认webLogin=false)
103+
104+
## 6. 影响范围分析
105+
106+
### 6.1 代码改动范围
107+
108+
| 文件 | 改动类型 | 改动内容 |
109+
|------|---------|---------|
110+
| GatewayConfiguration.scala | 修改 | 更新PROHIBIT_LOGIN_PREFIX默认值 |
111+
| UserRestful.scala | 修改 | 修改登录拦截逻辑,从header获取webLogin |
112+
113+
### 6.2 风险评估
114+
115+
| 风险 | 等级 | 缓解措施 |
116+
|------|------|---------|
117+
| 影响正常用户登录 | 低 | 功能开关默认关闭 |
118+
| 前端未传webLogin | 低 | 默认值为false,不拦截 |
119+
| 配置错误导致无法登录 | 中 | 提供配置示例和文档 |

‎linkis-spring-cloud-services/linkis-service-gateway/linkis-gateway-core/src/main/scala/org/apache/linkis/gateway/config/GatewayConfiguration.scala‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ object GatewayConfiguration {
138138
val PROHIBIT_LOGIN_SWITCH = CommonVars("linkis.system.user.prohibit.login.switch", false)
139139

140140
val PROHIBIT_LOGIN_PREFIX =
141-
CommonVars("linkis.system.user.prohibit.login.prefix", "hduser,shduser").getValue.toLowerCase()
141+
CommonVars("linkis.system.user.prohibit.login.prefix", "hduser,shduser,hadoop").getValue
142+
.toLowerCase()
142143

143144
}

‎linkis-spring-cloud-services/linkis-service-gateway/linkis-gateway-core/src/main/scala/org/apache/linkis/gateway/security/UserRestful.scala‎

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -307,10 +307,13 @@ abstract class UserPwdAbstractUserRestful extends AbstractUserRestful with Loggi
307307
return Message.error("Password can not be blank(密码不能为空)!")
308308
}
309309

310-
if (
311-
GatewayConfiguration.PROHIBIT_LOGIN_SWITCH.getValue && (!getRequestSource(gatewayContext)
312-
.equals("client"))
313-
) {
310+
// 从header获取webLogin标识,默认为false
311+
val headers = gatewayContext.getRequest.getHeaders
312+
val webLoginHeaders = headers.getOrDefault("webLogin", Array("false"))
313+
val webLogin = java.lang.Boolean.parseBoolean(webLoginHeaders.head)
314+
// 如果是web登录,检查是否为系统用户(包括hadoop用户)
315+
if (GatewayConfiguration.PROHIBIT_LOGIN_SWITCH.getValue && webLogin) {
316+
// 检查是否为系统用户(包括hadoop用户)
314317
PROHIBIT_LOGIN_PREFIX.split(",").foreach { prefix =>
315318
if (userName.toLowerCase().startsWith(prefix)) {
316319
return Message.error("System users are prohibited from logging in(系统用户禁止登录)!")

‎linkis-spring-cloud-services/linkis-service-gateway/linkis-gateway-httpclient-support/src/main/scala/org/apache/linkis/httpclient/dws/authentication/StaticAuthenticationStrategy.scala‎

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,18 +72,15 @@ class StaticAuthenticationStrategy(override protected val sessionMaxAliveTime: L
7272
case userPwd: UserPwdAction =>
7373
action.addRequestPayload("userName", userPwd.getUser)
7474
action.addRequestPayload("password", userPwd.getPassword.getOrElse(pwd))
75-
action.addRequestPayload("source", "client")
7675
case userAction: UserAction =>
7776
action.addRequestPayload("userName", userAction.getUser)
7877
action.addRequestPayload("password", pwd)
79-
action.addRequestPayload("source", "client")
8078
case _ =>
8179
if (StringUtils.isBlank(getClientConfig.getAuthTokenKey)) {
8280
throw new AuthenticationFailedException(AUTHTOKENVALUE_BE_EXISTS.getErrorDesc)
8381
}
8482
action.addRequestPayload("userName", getClientConfig.getAuthTokenKey)
8583
action.addRequestPayload("password", pwd)
86-
action.addRequestPayload("source", "client")
8784
}
8885
action
8986
}

‎linkis-web/.env‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@ VUE_APP_MN_CONFIG_SOCKET=/ws/api/entrance/connect
55
VUE_APP_VERSION=1.8.0
66
# Enable or disable Python Module feature (true/false)
77
VUE_APP_ENABLE_PYTHON_MODULE=false
8+
VUE_APP_DISALLOW_LOGIN=true
9+
VUE_APP_DISALLOW_LOGIN_PREFIX=hduser,shduser,hadoop

0 commit comments

Comments
 (0)