Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
3efb075
feat(api-gateway): implement OMS authentication filter with comprehen…
loveTsong Mar 12, 2026
09d7236
refactor(api-gateway): optimize OMS auth filter to apply configuratio…
loveTsong Mar 12, 2026
b350c99
refactor(api-gateway): change OMS auth token retrieval from header to…
loveTsong Mar 12, 2026
abc0b54
refactor(api-gateway): support config oms.gateway.url with env
loveTsong Mar 12, 2026
6786db5
fix(api-gateway): config omsservice url
loveTsong Mar 12, 2026
785e1fe
fix(api-gateway): return UNAUTHORIZED when get user fail
loveTsong Mar 12, 2026
f8795e6
feat(api-gateway): ignore ssl verify
loveTsong Mar 17, 2026
1c6ffc6
refactor: extract getUserNameFromOms method to improve code readability
loveTsong Mar 17, 2026
0439494
feat: add real IP retrieval and set to request header
loveTsong Mar 17, 2026
ba2c0b8
refactor(api-gateway): extract OMS authentication logic to service la…
loveTsong Mar 17, 2026
1f18523
feat(api-gateway): add OmsExtensionService for user group ID retrieva…
loveTsong Mar 17, 2026
a44bb83
fix(api-gateway): fix static inner class ResultVo compilation error b…
loveTsong Mar 17, 2026
4ff287b
refactor(api-gateway): adjust IP retrieval priority order and revert …
loveTsong Mar 17, 2026
d64c6b2
feat: implement querying group-id
MoeexT Mar 19, 2026
f16987b
fix: deserialize resource-group
MoeexT Mar 24, 2026
432b07c
feat: add switch to enable real-ip forwarding
MoeexT Mar 25, 2026
481e887
feat: add switch to enable real-ip forwarding
MoeexT Mar 26, 2026
32acc54
feat: use session url
MoeexT Mar 26, 2026
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
25 changes: 24 additions & 1 deletion backend/api-gateway/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@
<artifactId>postgresql</artifactId>
</dependency>

<!-- Apache HttpClient -->
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
</dependency>

<!-- 使用新版本的 JJWT -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
Expand All @@ -82,10 +88,27 @@
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId> <!-- 或 jjwt-gson -->
<artifactId>jjwt-jackson</artifactId>
<version>${jjwt.version}</version>
<scope>runtime</scope>
</dependency>

<!-- Test dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.datamate.gateway.common.config;

import lombok.extern.slf4j.Slf4j;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
import org.apache.hc.client5.http.ssl.NoopHostnameVerifier;
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactoryBuilder;
import org.apache.hc.client5.http.ssl.TrustAllStrategy;
import org.apache.hc.core5.ssl.SSLContextBuilder;
import org.springframework.stereotype.Component;

import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;

/**
* SslIgnoreHttpClientFactory is a factory that creates a CloseableHttpClient that ignores SSL errors.
*
* @author songyongtan
* @date 2026-03-16
*/
@Slf4j
@Component
public class SslIgnoreHttpClientFactory {
public CloseableHttpClient getHttpClient()
throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
SSLConnectionSocketFactory sslSocketFactory = SSLConnectionSocketFactoryBuilder.create()
.setSslContext(SSLContextBuilder.create()
.setProtocol("TLSv1.2")
.loadTrustMaterial(TrustAllStrategy.INSTANCE)
.build())
.setHostnameVerifier(NoopHostnameVerifier.INSTANCE)
.build();
PoolingHttpClientConnectionManager connManager =
PoolingHttpClientConnectionManagerBuilder.create().setSSLSocketFactory(sslSocketFactory).build();
return HttpClients.custom().setConnectionManager(connManager).build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package com.datamate.gateway.common.filter;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;

import com.datamate.gateway.infrastructure.client.OmsExtensionService;
import com.datamate.gateway.infrastructure.client.OmsService;

import reactor.core.publisher.Mono;

import java.util.Objects;

/**
* OmsAuthFilter is a global filter that authenticates requests to the OMS service.
*
* @author songyongtan
* @date 2026-03-16
*/
@Slf4j
@Component
public class OmsAuthFilter implements GlobalFilter {
private static final String USER_NAME_HEADER = "X-User-Name";
private static final String USER_GROUP_ID_HEADER = "X-User-Group-Id";
private static final String AUTH_TOKEN_KEY = "__Host-X-Auth-Token";
private static final String CSRF_TOKEN_KEY = "__Host-X-Csrf-Token";

private final Boolean omsAuthEnable;
private final OmsService omsService;
private final OmsExtensionService omsExtensionService;

/**
* OmsAuthFilter constructor.
*
* @param omsAuthEnable whether OMS authentication is enabled
* @param omsService OMS service client
* @param omsExtensionService OMS extension service client
*/
public OmsAuthFilter(
@Value("${oms.auth.enabled:false}") Boolean omsAuthEnable,
OmsService omsService,
OmsExtensionService omsExtensionService) {
log.info("OmsAuthFilter is apply, omsAuthEnable: {}", omsAuthEnable);
this.omsAuthEnable = omsAuthEnable;
this.omsService = omsService;
this.omsExtensionService = omsExtensionService;
}

/**
* filter processes the request and adds authentication headers.
*
* @param exchange the server web exchange
* @param chain the gateway filter chain
* @return Mono<Void> completion signal
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
if (!this.omsAuthEnable) {
return chain.filter(exchange);
}
ServerHttpRequest request = exchange.getRequest();
String uri = request.getURI().getPath();
log.info("Oms auth filter uri: {}", uri);

try {
MultiValueMap<String, HttpCookie> cookies = request.getCookies();
String authToken = getToken(cookies, AUTH_TOKEN_KEY);
String csrfToken = getToken(cookies, CSRF_TOKEN_KEY);
String realIp = getRealIp(request);

String userName = this.omsService.getUserNameFromOms(authToken, csrfToken, realIp);
if (userName == null) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
log.error("Authentication failed: Token is null or invalid.");
return exchange.getResponse().setComplete();
}
log.info("Current oms username is: {}", userName);
ServerHttpRequest newRequest = request.mutate()
.header(USER_NAME_HEADER, userName)
.build();

return chain.filter(exchange.mutate().request(newRequest).build());
} catch (Exception e) {
log.error("Exception occurred during POST request: {}", e.getMessage(), e);
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
}

/**
* getRealIp gets the real IP address from the request.
*
* @param request the HTTP request
* @return the real IP address
*/
private String getRealIp(ServerHttpRequest request) {
String ip = request.getHeaders().getFirst("X-Real-IP");
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeaders().getFirst("X-Forwarded-For");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeaders().getFirst("Proxy-Client-IP");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeaders().getFirst("WL-Proxy-Client-IP");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddress() != null ? request.getRemoteAddress().getAddress().getHostAddress() : "";
}
if (ip != null && ip.contains(",")) {
ip = ip.split(",")[0].trim();
}
return ip != null ? ip : "";
}

/**
* getToken gets the token value from cookies.
*
* @param cookies the cookies map
* @param tokenKey the token key
* @return the token value
*/
private String getToken(MultiValueMap<String, HttpCookie> cookies, String tokenKey) {
if (cookies.containsKey(tokenKey)) {
return Objects.requireNonNull(cookies.getFirst(tokenKey)).getValue();
}
return "";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.datamate.gateway.infrastructure.client;

/**
* OmsExtensionService is a service interface for OMS extension operations.
*
* @author songyongtan
* @date 2026-03-17
*/
public interface OmsExtensionService {
/**
* getUserGroupId gets the user group ID by user name.
*
* @param userName the user name
* @return the user group ID
*/
String getUserGroupId(String userName);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.datamate.gateway.infrastructure.client;

import java.io.IOException;

/**
* OmsService is a service that interacts with the OMS service.
*
* @author songyongtan
* @date 2026-03-16
*/
public interface OmsService {
/**
* getUserNameFromOms gets the user name from the OMS service.
*
* @param authToken the auth token
* @param csrfToken the csrf token
* @param realIp the real ip
* @return the user name
* @throws IOException if an error occurs
*/
String getUserNameFromOms(String authToken, String csrfToken, String realIp);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright (c) Huawei Technologies Co., Ltd. 2026-2026. All rights reserved.
*/

package com.datamate.gateway.infrastructure.client.dto;

import lombok.Getter;

import java.time.LocalDateTime;

/**
* 资源组信息
*/
@Getter
public class ResourceGroup {
private String id;

private String parentId;

private String name;

private String description;

private LocalDateTime createTime;

private LocalDateTime updateTime;

private boolean isDeleted;

private boolean isBuiltin;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright (c) Huawei Technologies Co., Ltd. 2026-2026. All rights reserved.
*/

package com.datamate.gateway.infrastructure.client.dto;

import lombok.Getter;

/**
* oms-extension统一返回包装类
*
* @param code 状态码
* @param msg 消息
* @param data 数据
* @param <T> 数据类
*/
public record Resp<T>(String code, String msg, T data) {
public static final String SUCCESS = "0";

/**
* 成功
* @param data 数据
* @return 响应体
* @param <T> 数据类型
*/
public static <T> Resp<T> ok(T data) {
return new Resp<>(SUCCESS, "success", data);
}

/**
* 成功
*
* @return 响应体
* @param <T> 数据类型
*/
public static <T> Resp<T> ok() {
return Resp.ok(null);
}

/**
* 失败返回
*
* @param code 状态码
* @param message 错误信息
* @return 响应体
* @param <T> 数据类型
*/
public static <T> Resp<T> error(String code, String message) {
return new Resp<>(code, message, null);
}
}
Loading
Loading