Compare commits

..

12 Commits

Author SHA1 Message Date
litianxiang
74efef7c24 登出bug 2026-05-26 14:03:30 +08:00
litianxiang
0eb6426a99 买家端联调bug 2026-05-22 13:02:29 +08:00
litianxiang
743b2bbda9 买家端联调bug 2026-05-22 13:02:10 +08:00
litianxiang
9fc17d2940 test 2026-05-21 17:16:40 +08:00
litianxiang
41bb344e97 fix商品接口报错 2026-05-21 16:41:16 +08:00
litianxiang
cddd74db2a test 2026-05-21 15:55:14 +08:00
litianxiang
69dba26b73 test 2026-05-21 15:54:43 +08:00
litianxiang
206431ccb0 test 2026-05-21 15:49:03 +08:00
62f8219d30 更新 .gitea/workflows/master_gateway_build_manual.yaml 2026-05-21 15:11:52 +08:00
litianxiang
2daec6b123 买家端接口fegin适配 2026-05-18 14:54:18 +08:00
litianxiang
7e19ba4d06 登录鉴权按照Source判断id来自于何处 2026-05-13 09:40:31 +08:00
litianxiang
58951ff9b6 买家端需要的获取商家主页和模糊搜索接口 2026-05-11 16:40:45 +08:00
6 changed files with 68 additions and 18 deletions

View File

@@ -50,7 +50,7 @@ jobs:
VOLUME /tmp VOLUME /tmp
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN echo 'Asia/Shanghai' > /etc/timezone RUN echo 'Asia/Shanghai' > /etc/timezone
ADD ./*.jar /app.jar ADD ./target/*.jar /app.jar
ENTRYPOINT ["java","-jar","/app.jar"] ENTRYPOINT ["java","-jar","/app.jar"]
EOF EOF
echo "Dockerfile内容:" echo "Dockerfile内容:"

10
pom.xml
View File

@@ -62,17 +62,11 @@
<scope>runtime</scope> <scope>runtime</scope>
</dependency> </dependency>
<!-- Jackson --> <!-- Hutool -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<!-- Hutool (aligned with aida_seller 5.8.26) -->
<dependency> <dependency>
<groupId>cn.hutool</groupId> <groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId> <artifactId>hutool-all</artifactId>
<version>5.8.26</version> <version>5.8.23</version>
</dependency> </dependency>
<!-- Redis (for token blacklist) --> <!-- Redis (for token blacklist) -->

View File

@@ -19,5 +19,13 @@ public class GatewayAuthProperties {
private List<String> ignorePaths; private List<String> ignorePaths;
/**
* 可选认证路径token 有则解析并写入下游请求头,无则放行。
* 与 ignorePaths 的区别ignorePaths 完全跳过认证逻辑;
* optionalAuthPaths 仍然尝试解析 token有 token 时正常写入 X-User-Id / X-User-Info
* 无 token 时才放行,确保已登录用户的信息能正确传递。
*/
private List<String> optionalAuthPaths;
private boolean blacklistEnabled = true; private boolean blacklistEnabled = true;
} }

View File

@@ -65,24 +65,43 @@ public class GlobalAuthWebFilter implements WebFilter, Ordered {
return chain.filter(exchange); return chain.filter(exchange);
} }
// 2. 白名单直接放行 // 2. 白名单直接放行(完全跳过认证)
if (isIgnoredPath(path)) { if (isIgnoredPath(path)) {
return chain.filter(exchange); return chain.filter(exchange);
} }
// 3. 提取 token // 3. 可选认证路径token 有则解析,无则放行
if (isOptionalAuthPath(path)) {
String rawHeader = exchange.getRequest().getHeaders()
.getFirst(authProperties.getJwtTokenHeader());
if (StrUtil.isBlank(rawHeader)) {
// 无 token直接放行不写任何 header
return chain.filter(exchange);
}
// 有 token正常走解析流程复用下面的验证逻辑
return processTokenWithAuthCheck(exchange, chain, rawHeader);
}
// 4. 其他路径:必须有 token
String rawHeader = exchange.getRequest().getHeaders() String rawHeader = exchange.getRequest().getHeaders()
.getFirst(authProperties.getJwtTokenHeader()); .getFirst(authProperties.getJwtTokenHeader());
if (StrUtil.isBlank(rawHeader)) { if (StrUtil.isBlank(rawHeader)) {
return writeUnauthorized(exchange, AuthConstants.MSG_MISSING_TOKEN); return writeUnauthorized(exchange, AuthConstants.MSG_MISSING_TOKEN);
} }
return processTokenWithAuthCheck(exchange, chain, rawHeader);
}
/**
* 统一的 token 解析与认证流程:解析 JWT → 黑名单检查 → 写入下游 header。
* 专供可选认证路径中有 token 的情况,以及普通路径的鉴权。
*/
private Mono<Void> processTokenWithAuthCheck(ServerWebExchange exchange, WebFilterChain chain, String rawHeader) {
String token = rawHeader; String token = rawHeader;
if (rawHeader.startsWith(authProperties.getJwtTokenPrefix())) { if (rawHeader.startsWith(authProperties.getJwtTokenPrefix())) {
token = rawHeader.substring(authProperties.getJwtTokenPrefix().length()); token = rawHeader.substring(authProperties.getJwtTokenPrefix().length());
} }
// 4. JWT 签名验证 // JWT 签名验证
Claims claims; Claims claims;
try { try {
claims = parseToken(token); claims = parseToken(token);
@@ -91,7 +110,7 @@ public class GlobalAuthWebFilter implements WebFilter, Ordered {
return writeUnauthorized(exchange, AuthConstants.MSG_TOKEN_EXPIRED); return writeUnauthorized(exchange, AuthConstants.MSG_TOKEN_EXPIRED);
} }
// 5. 解析用户信息 // 解析用户信息
AuthPrincipalVo principal; AuthPrincipalVo principal;
try { try {
principal = objectMapper.readValue(claims.getSubject(), AuthPrincipalVo.class); principal = objectMapper.readValue(claims.getSubject(), AuthPrincipalVo.class);
@@ -104,7 +123,7 @@ public class GlobalAuthWebFilter implements WebFilter, Ordered {
return writeUnauthorized(exchange, AuthConstants.MSG_INVALID_TOKEN); return writeUnauthorized(exchange, AuthConstants.MSG_INVALID_TOKEN);
} }
// 6. 黑名单检查(仅当启用时) // 黑名单检查
if (authProperties.isBlacklistEnabled()) { if (authProperties.isBlacklistEnabled()) {
String blacklistKey = AuthConstants.BLACKLIST_PREFIX + principal.getId(); String blacklistKey = AuthConstants.BLACKLIST_PREFIX + principal.getId();
return redisTemplate.hasKey(blacklistKey).flatMap(isBlacklisted -> { return redisTemplate.hasKey(blacklistKey).flatMap(isBlacklisted -> {
@@ -151,6 +170,18 @@ public class GlobalAuthWebFilter implements WebFilter, Ordered {
return false; return false;
} }
private boolean isOptionalAuthPath(String requestUri) {
if (authProperties.getOptionalAuthPaths() == null) {
return false;
}
for (String pattern : authProperties.getOptionalAuthPaths()) {
if (pathMatcher.match(pattern, requestUri)) {
return true;
}
}
return false;
}
private Claims parseToken(String token) { private Claims parseToken(String token) {
SecretKey key = buildSigningKey(); SecretKey key = buildSigningKey();
return Jwts.parser() return Jwts.parser()

View File

@@ -41,8 +41,11 @@ public class LogoutBlacklistWebFilter implements WebFilter {
return chain.filter(exchange); return chain.filter(exchange);
} }
// 从请求头读取 X-User-Id(内部调用,不需要鉴权 // 优先从请求头读取 X-User-Id,其次从 query param 读取 userId兼容不同客户端调用方式
String userId = exchange.getRequest().getHeaders().getFirst(AuthConstants.USER_ID_HEADER); String userId = exchange.getRequest().getHeaders().getFirst(AuthConstants.USER_ID_HEADER);
if (userId == null || userId.isBlank()) {
userId = exchange.getRequest().getQueryParams().getFirst("userId");
}
if (userId == null || userId.isBlank()) { if (userId == null || userId.isBlank()) {
return writeResponse(exchange, HttpStatus.BAD_REQUEST, "{\"code\":400,\"message\":\"userId required\"}"); return writeResponse(exchange, HttpStatus.BAD_REQUEST, "{\"code\":400,\"message\":\"userId required\"}");
} }
@@ -50,12 +53,13 @@ public class LogoutBlacklistWebFilter implements WebFilter {
String blacklistKey = AuthConstants.BLACKLIST_PREFIX + userId; String blacklistKey = AuthConstants.BLACKLIST_PREFIX + userId;
// 黑名单 TTL 设为 7 天(与 JWT 有效期保持一致) // 黑名单 TTL 设为 7 天(与 JWT 有效期保持一致)
String finalUserId = userId;
return redisTemplate.opsForValue() return redisTemplate.opsForValue()
.set(blacklistKey, "1") .set(blacklistKey, "1")
.then(redisTemplate.expire(blacklistKey, Duration.ofDays(7))) .then(redisTemplate.expire(blacklistKey, Duration.ofDays(7)))
.then(writeResponse(exchange, HttpStatus.OK, "{\"code\":200,\"message\":\"ok\"}")) .then(writeResponse(exchange, HttpStatus.OK, "{\"code\":200,\"message\":\"ok\"}"))
.onErrorResume(e -> { .onErrorResume(e -> {
log.error("Failed to add token to blacklist, userId={}", userId, e); log.error("Failed to add token to blacklist, userId={}", finalUserId, e);
return writeResponse(exchange, HttpStatus.INTERNAL_SERVER_ERROR, return writeResponse(exchange, HttpStatus.INTERNAL_SERVER_ERROR,
"{\"code\":500,\"message\":\"internal error\"}"); "{\"code\":500,\"message\":\"internal error\"}");
}); });

View File

@@ -50,6 +50,12 @@ spring:
uri: http://18.167.251.121:9994 uri: http://18.167.251.121:9994
predicates: predicates:
- Path=/python/** - Path=/python/**
- id: aida-buyer
uri: lb://aida-buyer
predicates:
- Path=/buyer/**
filters:
- StripPrefix=1
# ---------- Knife4j 网关聚合配置 ---------- # ---------- Knife4j 网关聚合配置 ----------
knife4j: knife4j:
gateway: gateway:
@@ -67,6 +73,11 @@ knife4j:
service-name: aida-seller service-name: aida-seller
context-path: /seller context-path: /seller
order: 2 order: 2
- name: 买家端服务 (Buyer)
url: /buyer/v3/api-docs
service-name: aida-buyer
context-path: /buyer
order: 3
# ---------- Gateway JWT 认证gateway 独有) ---------- # ---------- Gateway JWT 认证gateway 独有) ----------
gateway: gateway:
@@ -144,5 +155,7 @@ gateway:
- /aida/api/stripe/trade/notify - /aida/api/stripe/trade/notify
# Notification # Notification
- /notification/** - /notification/**
# buyer
logging: - /buyer/account/**
- /buyer/designer/shop/**
- /buyer/designer/search