Skip to content

Commit 2759e6a

Browse files
authored
feat: add oms auth status (#453)
* feat: use session url * fix: launch failed * fix: launch failed * fix: frontend no login status * fix: get user * fix: add gateway route * fix: add gateway env OMS_AUTH_ENABLED * fix: modify gateway router * fix: modify gateway router * fix: logout host * fix: hide logout button
1 parent 999859d commit 2759e6a

File tree

13 files changed

+405
-44
lines changed

13 files changed

+405
-44
lines changed

backend/api-gateway/pom.xml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@
3333
<artifactId>spring-boot-starter-web</artifactId>
3434
<groupId>org.springframework.boot</groupId>
3535
</exclusion>
36+
<exclusion>
37+
<artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
38+
<groupId>org.springframework.ai</groupId>
39+
</exclusion>
3640
</exclusions>
3741
</dependency>
3842
<dependency>
@@ -57,6 +61,12 @@
5761
<dependency>
5862
<groupId>com.baomidou</groupId>
5963
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
64+
<exclusions>
65+
<exclusion>
66+
<groupId>org.springframework.boot</groupId>
67+
<artifactId>spring-boot-starter-web</artifactId>
68+
</exclusion>
69+
</exclusions>
6070
</dependency>
6171
<dependency>
6272
<groupId>com.baomidou</groupId>
@@ -109,6 +119,10 @@
109119
<artifactId>mockito-junit-jupiter</artifactId>
110120
<scope>test</scope>
111121
</dependency>
122+
<dependency>
123+
<groupId>org.apache.tomcat.embed</groupId>
124+
<artifactId>tomcat-embed-core</artifactId>
125+
</dependency>
112126
</dependencies>
113127

114128
<build>

backend/api-gateway/src/main/java/com/datamate/gateway/ApiGatewayApplication.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,22 @@
77
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
88
import org.springframework.context.annotation.Bean;
99
import org.springframework.context.annotation.ComponentScan;
10+
import org.springframework.context.annotation.FilterType;
11+
import org.springframework.context.annotation.ComponentScan.Filter;
1012

1113
/**
1214
* API Gateway & Auth Service Application
1315
* 统一的API网关和认证授权微服务
1416
* 提供路由、鉴权、限流等功能
1517
*/
1618
@SpringBootApplication
17-
@ComponentScan(basePackages = {"com.datamate"})
19+
@ComponentScan(
20+
basePackages = {"com.datamate"},
21+
excludeFilters = @Filter(
22+
type = FilterType.REGEX,
23+
pattern = "com\\.datamate\\.common\\.infrastructure\\.config\\..*"
24+
)
25+
)
1826
@MapperScan(basePackages = {"com.datamate.**.mapper"})
1927
public class ApiGatewayApplication {
2028

@@ -68,9 +76,10 @@ public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
6876
.filters(f -> f.stripPrefix(1).prefixPath("/api"))
6977
.uri("http://deer-flow-backend:8000"))
7078

71-
// 网关服务(用户)
79+
// 网关内部服务(用户)
80+
// 使用 no-op 触发 GlobalFilter 执行,然后由本地 Controller 处理
7281
.route("gateway", r -> r.path("/api/user/**")
73-
.uri("http://localhost:8080"))
82+
.uri("http://localhost:8080"))
7483

7584
// 其他后端服务
7685
.route("default", r -> r.path("/api/**")

backend/api-gateway/src/main/java/com/datamate/gateway/common/filter/AuthFilter.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import org.springframework.beans.factory.annotation.Value;
1212
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
1313
import org.springframework.cloud.gateway.filter.GlobalFilter;
14+
import org.springframework.core.Ordered;
1415
import org.springframework.core.io.buffer.DataBuffer;
1516
import org.springframework.http.HttpStatus;
1617
import org.springframework.http.MediaType;
@@ -29,7 +30,7 @@
2930
@Slf4j
3031
@Component
3132
@RequiredArgsConstructor
32-
public class AuthFilter implements GlobalFilter {
33+
public class AuthFilter implements GlobalFilter, Ordered {
3334
private static final String AUTH_HEADER = "Authorization";
3435

3536
private static final String TOKEN_PREFIX = "Bearer ";
@@ -95,4 +96,14 @@ private Mono<Void> sendUnauthorizedResponse(ServerWebExchange exchange) {
9596
DataBuffer buffer = response.bufferFactory().wrap(bytes);
9697
return response.writeWith(Mono.just(buffer));
9798
}
99+
100+
/**
101+
* JWT 认证优先级低于 SSO
102+
*
103+
* @return order value (2 = lower priority than SSO filter)
104+
*/
105+
@Override
106+
public int getOrder() {
107+
return 2;
108+
}
98109
}

backend/api-gateway/src/main/java/com/datamate/gateway/common/filter/OmsAuthFilter.java

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import org.springframework.beans.factory.annotation.Value;
55
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
66
import org.springframework.cloud.gateway.filter.GlobalFilter;
7+
import org.springframework.core.Ordered;
78
import org.springframework.http.HttpCookie;
89
import org.springframework.http.HttpStatus;
910
import org.springframework.http.server.reactive.ServerHttpRequest;
@@ -20,13 +21,13 @@
2021

2122
/**
2223
* OmsAuthFilter is a global filter that authenticates requests to the OMS service.
23-
*
24+
*
2425
* @author songyongtan
2526
* @date 2026-03-16
2627
*/
2728
@Slf4j
2829
@Component
29-
public class OmsAuthFilter implements GlobalFilter {
30+
public class OmsAuthFilter implements GlobalFilter, Ordered {
3031
private static final String USER_NAME_HEADER = "X-User-Name";
3132
private static final String USER_GROUP_ID_HEADER = "X-User-Group-Id";
3233
private static final String AUTH_TOKEN_KEY = "__Host-X-Auth-Token";
@@ -122,7 +123,7 @@ private String getRealIp(ServerHttpRequest request) {
122123

123124
/**
124125
* getToken gets the token value from cookies.
125-
*
126+
*
126127
* @param cookies the cookies map
127128
* @param tokenKey the token key
128129
* @return the token value
@@ -133,4 +134,14 @@ private String getToken(MultiValueMap<String, HttpCookie> cookies, String tokenK
133134
}
134135
return "";
135136
}
137+
138+
/**
139+
* SSO 认证优先级最高
140+
*
141+
* @return order value (1 = highest priority for auth filters)
142+
*/
143+
@Override
144+
public int getOrder() {
145+
return 1;
146+
}
136147
}

backend/api-gateway/src/main/java/com/datamate/gateway/infrastructure/client/impl/OmsServiceImpl.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222
/**
2323
* OmsServiceImpl is a service that interacts with the OMS service.
24-
*
24+
*
2525
* @author songyongtan
2626
* @date 2026-03-16
2727
*/
@@ -66,7 +66,6 @@ public String getUserNameFromOms(String authToken, String csrfToken, String real
6666

6767
CloseableHttpResponse response = httpClient.execute(httpPost);
6868
String responseBody = EntityUtils.toString(response.getEntity());
69-
log.info("response code: {}", response.getCode());
7069

7170
try {
7271
JSONObject jsonObject = JSON.parseObject(responseBody);
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package com.datamate.gateway.interfaces.dto;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Builder;
5+
import lombok.Data;
6+
import lombok.NoArgsConstructor;
7+
8+
/**
9+
* 用户信息响应
10+
*
11+
* 支持双认证模式:
12+
* - SSO: 通过 OMS 单点登录
13+
* - JWT: 通过本地 JWT Token 认证
14+
* - NONE: 未登录
15+
*/
16+
@Data
17+
@Builder
18+
@NoArgsConstructor
19+
@AllArgsConstructor
20+
public class UserResponse {
21+
/**
22+
* 用户名
23+
*/
24+
private String username;
25+
26+
/**
27+
* 邮箱(JWT 模式可用)
28+
*/
29+
private String email;
30+
31+
/**
32+
* 用户组 ID(SSO 模式可用)
33+
*/
34+
private String groupId;
35+
36+
/**
37+
* 是否已认证
38+
*/
39+
private Boolean authenticated;
40+
41+
/**
42+
* 认证模式
43+
*/
44+
private String authMode; // "SSO" | "JWT" | "NONE"
45+
}

backend/api-gateway/src/main/java/com/datamate/gateway/interfaces/rest/UserController.java

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,28 @@
44
import com.datamate.common.infrastructure.common.Response;
55
import com.datamate.common.infrastructure.exception.CommonErrorCode;
66
import com.datamate.gateway.application.UserApplicationService;
7+
import com.datamate.gateway.domain.service.UserService;
8+
import com.datamate.gateway.infrastructure.client.OmsExtensionService;
9+
import com.datamate.gateway.infrastructure.client.OmsService;
710
import com.datamate.gateway.interfaces.dto.LoginRequest;
811
import com.datamate.gateway.interfaces.dto.LoginResponse;
912
import com.datamate.gateway.interfaces.dto.RegisterRequest;
13+
import com.datamate.gateway.interfaces.dto.UserResponse;
1014
import jakarta.validation.Valid;
1115
import lombok.RequiredArgsConstructor;
1216
import lombok.extern.slf4j.Slf4j;
17+
import org.apache.commons.lang3.StringUtils;
1318
import org.springframework.http.HttpStatus;
1419
import org.springframework.http.ResponseEntity;
20+
import org.springframework.http.server.reactive.ServerHttpRequest;
21+
import org.springframework.util.MultiValueMap;
1522
import org.springframework.validation.annotation.Validated;
23+
import org.springframework.web.bind.annotation.GetMapping;
1624
import org.springframework.web.bind.annotation.PostMapping;
1725
import org.springframework.web.bind.annotation.RequestBody;
1826
import org.springframework.web.bind.annotation.RequestMapping;
1927
import org.springframework.web.bind.annotation.RestController;
28+
import org.springframework.http.HttpCookie;
2029

2130
/**
2231
* UserController
@@ -30,6 +39,45 @@
3039
@RequiredArgsConstructor
3140
public class UserController {
3241
private final UserApplicationService userApplicationService;
42+
private final UserService userService;
43+
private final OmsService omsService;
44+
private final OmsExtensionService omsExtensionService;
45+
46+
private static final String AUTH_TOKEN_KEY = "__Host-X-Auth-Token";
47+
private static final String CSRF_TOKEN_KEY = "__Host-X-Csrf-Token";
48+
49+
/**
50+
* 从 cookies 中获取 token 值
51+
*/
52+
private String getToken(MultiValueMap<String, HttpCookie> cookies, String tokenKey) {
53+
if (cookies.containsKey(tokenKey)) {
54+
return cookies.getFirst(tokenKey).getValue();
55+
}
56+
return "";
57+
}
58+
59+
/**
60+
* 获取真实 IP 地址
61+
*/
62+
private String getRealIp(ServerHttpRequest request) {
63+
String ip = request.getHeaders().getFirst("X-Real-IP");
64+
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
65+
ip = request.getHeaders().getFirst("X-Forwarded-For");
66+
}
67+
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
68+
ip = request.getHeaders().getFirst("Proxy-Client-IP");
69+
}
70+
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
71+
ip = request.getHeaders().getFirst("WL-Proxy-Client-IP");
72+
}
73+
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
74+
ip = request.getRemoteAddress() != null ? request.getRemoteAddress().getAddress().getHostAddress() : "";
75+
}
76+
if (ip != null && ip.contains(",")) {
77+
ip = ip.split(",")[0].trim();
78+
}
79+
return ip != null ? ip : "";
80+
}
3381

3482
@PostMapping("/login")
3583
@IgnoreResponseWrap
@@ -48,4 +96,84 @@ public ResponseEntity<Response<LoginResponse>> register(@Valid @RequestBody Regi
4896
.orElseGet(() -> ResponseEntity.status(HttpStatus.BAD_REQUEST)
4997
.body(Response.error(CommonErrorCode.SIGNUP_ERROR)));
5098
}
99+
100+
/**
101+
* 获取当前登录用户信息(支持双模式)
102+
* 优先级:
103+
* 1. SSO 模式:从 cookies 读取 OMS token 并调用 OMS 服务验证
104+
* 2. JWT 模式:检查 Authorization Bearer Token
105+
* 3. 未登录:返回 authenticated=false
106+
*
107+
* @param request HTTP 请求
108+
* @return 用户信息(包含认证模式)
109+
*/
110+
@GetMapping("/me")
111+
public Response<UserResponse> getCurrentUser(ServerHttpRequest request) {
112+
log.info("=== /api/user/me called ===");
113+
114+
// 优先检查 SSO 模式(从 cookies 读取 OMS token)
115+
MultiValueMap<String, HttpCookie> cookies = request.getCookies();
116+
String authToken = getToken(cookies, AUTH_TOKEN_KEY);
117+
String csrfToken = getToken(cookies, CSRF_TOKEN_KEY);
118+
119+
log.info("Cookies present - __Host-X-Auth-Token: {}, __Host-X-Csrf-Token: {}",
120+
StringUtils.isNotBlank(authToken), StringUtils.isNotBlank(csrfToken));
121+
122+
if (StringUtils.isNotBlank(authToken)) {
123+
try {
124+
// 获取真实 IP
125+
String realIp = getRealIp(request);
126+
log.info("Calling OMS service with realIp: {}", realIp);
127+
128+
// 调用 OMS 服务验证
129+
String username = omsService.getUserNameFromOms(authToken, csrfToken, realIp);
130+
if (StringUtils.isNotBlank(username)) {
131+
log.info("SSO mode: user={}", username);
132+
133+
// 获取用户组 ID(可能为 null)
134+
String groupId = null;
135+
try {
136+
groupId = omsExtensionService.getUserGroupId(username);
137+
log.info("User groupId: {}", groupId);
138+
} catch (Exception e) {
139+
log.warn("Failed to get user group ID: {}", e.getMessage());
140+
}
141+
142+
return Response.ok(UserResponse.builder()
143+
.username(username)
144+
.groupId(groupId)
145+
.authenticated(true)
146+
.authMode("SSO")
147+
.build());
148+
} else {
149+
log.warn("OMS service returned null username");
150+
}
151+
} catch (Exception e) {
152+
log.error("SSO authentication failed", e);
153+
}
154+
}
155+
156+
// 检查独立登录模式(JWT Token)
157+
String authHeader = request.getHeaders().getFirst("Authorization");
158+
if (authHeader != null && authHeader.startsWith("Bearer ")) {
159+
String token = authHeader.substring(7);
160+
String username = userService.validateToken(token);
161+
162+
if (StringUtils.isNotBlank(username)) {
163+
log.info("JWT mode: user={}", username);
164+
return Response.ok(UserResponse.builder()
165+
.username(username)
166+
.authenticated(true)
167+
.authMode("JWT")
168+
.build());
169+
}
170+
}
171+
172+
// 未登录
173+
log.debug("User not authenticated");
174+
return Response.ok(UserResponse.builder()
175+
.authenticated(false)
176+
.authMode("NONE")
177+
.build());
178+
}
51179
}

backend/api-gateway/src/main/resources/application.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
spring:
22
main:
33
allow-circular-references: true
4+
allow-bean-definition-overriding: true
45
application:
56
name: datamate-gateway # 必须设置应用名
67

deployment/helm/datamate/values.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,8 @@ gateway:
169169
value: "default-insecure-key-change-in-production"
170170
- name: datamate.jwt.enable
171171
value: *DATAMATE_JWT_ENABLE
172+
- name: OMS_AUTH_ENABLED
173+
value: "false"
172174
volumes:
173175
- *logVolume
174176
volumeMounts:

0 commit comments

Comments
 (0)