From b048718298d04f0346ffd4fb0b596565a6e74af5 Mon Sep 17 00:00:00 2001 From: matevip Date: Fri, 26 Jun 2026 10:07:46 +0800 Subject: [PATCH] =?UTF-8?q?fix(sso):=20inline=20FQN=E2=86=92import=20+=20h?= =?UTF-8?q?arden=20auto-create=20orphan=20rollback?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace inline fully-qualified names with top-of-file imports across SsoService / SsoStateService / FeishuSsoProvider (ObjectMapper, Autowired, Map.of, Date, DuplicateKeyException, Mac, URLEncoder) per code style. - createSsoUser: roll back the freshly inserted user on any non-duplicate identity-insert failure, preventing passwordless orphan accounts. The two inserts share no transaction — the method is self-invoked and the enclosing callback performs a network call, so a method-level @Transactional would not apply; an explicit rollback in the catch is the correct guard here. --- .../java/vip/mate/auth/sso/SsoService.java | 18 +++++++++++++++--- .../vip/mate/auth/sso/SsoStateService.java | 11 +++++++---- .../auth/sso/provider/FeishuSsoProvider.java | 3 ++- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/mateclaw-server/src/main/java/vip/mate/auth/sso/SsoService.java b/mateclaw-server/src/main/java/vip/mate/auth/sso/SsoService.java index d127570e..8d615715 100644 --- a/mateclaw-server/src/main/java/vip/mate/auth/sso/SsoService.java +++ b/mateclaw-server/src/main/java/vip/mate/auth/sso/SsoService.java @@ -2,8 +2,10 @@ package vip.mate.auth.sso; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.fasterxml.jackson.databind.ObjectMapper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DuplicateKeyException; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; @@ -46,9 +48,9 @@ public class SsoService { private final AuthService authService; private final SsoProperties ssoProperties; private final BCryptPasswordEncoder passwordEncoder; - private final com.fasterxml.jackson.databind.ObjectMapper objectMapper; + private final ObjectMapper objectMapper; /** Optional — audit may be null in narrow test contexts. */ - @org.springframework.beans.factory.annotation.Autowired(required = false) + @Autowired(required = false) private AuditEventService auditService; // ==================== authorize ==================== @@ -228,6 +230,16 @@ public class SsoService { 503, "SSO 登录遇到并发冲突, 请重试"); } return createSsoUser(providerId, info, true); + } catch (RuntimeException e) { + // Identity insert failed for a non-duplicate reason (e.g. transient DB error). + // The two inserts are not in a shared transaction — this method is self-invoked + // and the enclosing callback performs a network call, so a method-level + // @Transactional would not apply. Roll back the freshly inserted user here so we + // never leave a passwordless orphan account behind. + if (newUser.getId() != null) { + userMapper.deleteById(newUser.getId()); + } + throw e; } } @@ -240,7 +252,7 @@ public class SsoService { private void audit(String action, String provider, String externalId, Long userId) { if (auditService != null) { try { - String detail = objectMapper.writeValueAsString(java.util.Map.of( + String detail = objectMapper.writeValueAsString(Map.of( "provider", provider != null ? provider : "", "userId", userId != null ? userId : "null")); auditService.record(action, "sso", diff --git a/mateclaw-server/src/main/java/vip/mate/auth/sso/SsoStateService.java b/mateclaw-server/src/main/java/vip/mate/auth/sso/SsoStateService.java index 27139fa7..00e1e8ea 100644 --- a/mateclaw-server/src/main/java/vip/mate/auth/sso/SsoStateService.java +++ b/mateclaw-server/src/main/java/vip/mate/auth/sso/SsoStateService.java @@ -8,6 +8,7 @@ import io.jsonwebtoken.security.Keys; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; +import org.springframework.dao.DuplicateKeyException; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import vip.mate.auth.sso.model.SsoStateEntity; @@ -15,10 +16,12 @@ import vip.mate.auth.sso.provider.SsoUserInfo; import vip.mate.auth.sso.repository.SsoStateMapper; import vip.mate.exception.MateClawException; +import javax.crypto.Mac; import javax.crypto.SecretKey; import java.nio.charset.StandardCharsets; import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; +import java.util.Date; import java.util.Map; import java.util.UUID; @@ -126,8 +129,8 @@ public class SsoStateService { .claim("externalId", info.externalId()) .claim("unionId", info.unionId()) .claim("externalName", info.displayName()) - .issuedAt(new java.util.Date(now)) - .expiration(new java.util.Date(now + BIND_TOKEN_TTL_SECONDS * 1000L)) + .issuedAt(new Date(now)) + .expiration(new Date(now + BIND_TOKEN_TTL_SECONDS * 1000L)) .signWith(getSignKey()) .compact(); } @@ -166,7 +169,7 @@ public class SsoStateService { consumed.setCreatedAt(LocalDateTime.now()); try { stateMapper.insert(consumed); - } catch (org.springframework.dao.DuplicateKeyException e) { + } catch (DuplicateKeyException e) { throw new MateClawException("err.sso.bind_token_used", 400, "bind_token 已被使用, 请重新登录"); } @@ -213,7 +216,7 @@ public class SsoStateService { private String hmacSha256Hex(String input) { try { - javax.crypto.Mac mac = javax.crypto.Mac.getInstance("HmacSHA256"); + Mac mac = Mac.getInstance("HmacSHA256"); mac.init(getSignKey()); byte[] hash = mac.doFinal(input.getBytes(StandardCharsets.UTF_8)); StringBuilder sb = new StringBuilder(); diff --git a/mateclaw-server/src/main/java/vip/mate/auth/sso/provider/FeishuSsoProvider.java b/mateclaw-server/src/main/java/vip/mate/auth/sso/provider/FeishuSsoProvider.java index 9f123043..8605e502 100644 --- a/mateclaw-server/src/main/java/vip/mate/auth/sso/provider/FeishuSsoProvider.java +++ b/mateclaw-server/src/main/java/vip/mate/auth/sso/provider/FeishuSsoProvider.java @@ -7,6 +7,7 @@ import vip.mate.auth.sso.SsoProperties; import vip.mate.exception.MateClawException; import java.net.URI; +import java.net.URLEncoder; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; @@ -221,7 +222,7 @@ public class FeishuSsoProvider implements SsoProvider { private static String encode(String s) { try { - return java.net.URLEncoder.encode(s, "UTF-8"); + return URLEncoder.encode(s, "UTF-8"); } catch (Exception e) { return s; }