mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-13 11:13:43 +08:00
fix(sso): inline FQN→import + harden auto-create orphan rollback
- 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.
This commit is contained in:
parent
03a6d61131
commit
b048718298
@ -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",
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user