Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

import org.sejongisc.backend.auth.entity.AuthProvider;
import org.sejongisc.backend.auth.entity.UserOauthAccount;
import org.sejongisc.backend.user.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;
import java.util.UUID;

public interface UserOauthAccountRepository extends JpaRepository<UserOauthAccount, UUID> {
Optional<UserOauthAccount> findByProviderAndProviderUid(AuthProvider provider, String providerUid);
boolean existsByProviderAndUser(AuthProvider provider, User user);
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

import java.util.List;
import java.util.Map;
import java.util.Optional;

@Slf4j
@Service
Expand All @@ -32,10 +33,8 @@ public class CustomOAuth2UserService implements OAuth2UserService <OAuth2UserReq

@Override
public OAuth2User loadUser(OAuth2UserRequest req) throws OAuth2AuthenticationException {
// log.info("[CustomOAuth2UserService] loadUser START");

OAuth2UserService<OAuth2UserRequest, OAuth2User> delegate =
new DefaultOAuth2UserService();

OAuth2UserService<OAuth2UserRequest, OAuth2User> delegate = new DefaultOAuth2UserService();
OAuth2User oAuth2User = delegate.loadUser(req);

String provider = req.getClientRegistration().getRegistrationId(); // google, kakao, github
Expand All @@ -45,7 +44,6 @@ public OAuth2User loadUser(OAuth2UserRequest req) throws OAuth2AuthenticationExc
String email;
String name;

// log.info("[OAuth2] Provider = {}", provider);
if (log.isDebugEnabled()) {
log.debug("[OAuth2] Attributes = {}", attrs);
}
Expand All @@ -59,60 +57,89 @@ public OAuth2User loadUser(OAuth2UserRequest req) throws OAuth2AuthenticationExc
case "kakao" -> {
providerUid = attrs.get("id").toString();
Map<String, Object> kakaoAccount = (Map<String, Object>) attrs.get("kakao_account");
email = (String) kakaoAccount.get("email"); // null 가능
Map<String, Object> profile = (Map<String, Object>) kakaoAccount.get("profile");
name = (String) profile.get("nickname");
email = kakaoAccount == null ? null : (String) kakaoAccount.get("email"); // null 가능
Map<String, Object> profile = kakaoAccount == null ? null : (Map<String, Object>) kakaoAccount.get("profile");
name = profile == null ? null : (String) profile.get("nickname");
}
case "github" -> {
providerUid = attrs.get("id").toString();
email = (String) attrs.get("email");
name = (String) attrs.get("login"); // GitHub은 login이 닉네임
email = (String) attrs.get("email"); // GitHub은 null 자주 나옴(권한/공개설정)
name = (String) attrs.get("login");
}
default -> throw new RuntimeException("지원하지 않는 provider: " + provider);
}

// log.info("provider={}, providerUid={}, email={}, name={}", provider, providerUid, email, name);

final String fProviderUid = providerUid;
final String fEmail = email;
final String fName = name;
final AuthProvider fAuthProvider = AuthProvider.valueOf(provider.toUpperCase());

User user = oauthAccountRepository
.findByProviderAndProviderUid(AuthProvider.from(provider), providerUid)
.map(UserOauthAccount::getUser)
.orElseGet(() -> {
User newUser = User.builder()
.email(email)
.name(name)
.role(Role.TEAM_MEMBER)
.build();
User saved = userRepository.save(newUser);
AuthProvider authProvider = AuthProvider.from(provider);

// 1) (provider, providerUid)로 먼저 찾는다 (기존 OAuth 로그인 사용자)
Optional<UserOauthAccount> oauthLinkOpt =
oauthAccountRepository.findByProviderAndProviderUid(authProvider, providerUid);

User user;

if (oauthLinkOpt.isPresent()) {
user = oauthLinkOpt.get().getUser();
} else {
// 2) 링크가 없으면 email로 기존 로컬/회원가입 유저를 찾는다 (핵심!)
Optional<User> userByEmailOpt = Optional.empty();
if (email != null && !email.isBlank()) {
userByEmailOpt = userRepository.findUserByEmail(email);
}

if (userByEmailOpt.isPresent()) {
// 3) 기존 유저가 있으면: 새 유저 만들지 말고 OAuth 링크만 추가
user = userByEmailOpt.get();

// (선택) 이미 같은 provider로 링크가 있으면 중복 저장 방지
// repository에 메서드 없으면 try/catch로 unique constraint에 맡겨도 됨
boolean alreadyLinked = oauthAccountRepository
.existsByProviderAndUser(authProvider, user);

if (!alreadyLinked) {
UserOauthAccount oauth = UserOauthAccount.builder()
.user(saved)
.provider(AuthProvider.from(provider))
.user(user)
.provider(authProvider)
.providerUid(providerUid)
.build();
oauthAccountRepository.save(oauth);

return saved;
});

// log.info("[CustomOAuth2UserService] User resolved → returning OAuth2User");
}

} else {
// 4) email로도 못 찾으면 신규 생성 + 링크
User newUser = User.builder()
.email(email) // null 가능성: DB 제약(NOT NULL/UNIQUE) 있으면 정책 필요
.name(name)
.role(Role.TEAM_MEMBER)
.build();

User saved = userRepository.save(newUser);

UserOauthAccount oauth = UserOauthAccount.builder()
.user(saved)
.provider(authProvider)
.providerUid(providerUid)
.build();
oauthAccountRepository.save(oauth);

user = saved;
}
}

Map<String, Object> attributes = new java.util.HashMap<>();
attributes.put("provider", provider); // google / kakao / github
attributes.put("providerUid", providerUid); // 소셜 계정 UID
attributes.put("email", user.getEmail()); // DB email
attributes.put("provider", provider);
attributes.put("providerUid", providerUid);
attributes.put("email", user.getEmail());
attributes.put("name", user.getName());
attributes.put("userId", user.getUserId()); // DB user uuid
attributes.put("userId", user.getUserId());

return new DefaultOAuth2User(
// 기존처럼 고정 ROLE 주고 싶으면 그대로 두고,
// 권한을 user role 기반으로 주고 싶으면 아래 주석 라인으로 교체해도 됨
List.of(new SimpleGrantedAuthority("ROLE_TEAM_MEMBER")),
// List.of(new SimpleGrantedAuthority("ROLE_" + user.getRole().name())),
attributes,
"userId" // 또는 "email" -> email null 이면 id가 더 안전
"userId"
);

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public class TokenEncryptor {

private final SecretKeySpec secretKey;

public TokenEncryptor(@Value("${TOKEN_ENCRYPTION_KEY:mySecretKey1234}") String key) {
public TokenEncryptor(@Value("${TOKEN_ENCRYPTION_KEY:mySecretKey12345}") String key) {
if (key == null || key.length() != 16) {
throw new IllegalStateException(
"유효한 16바이트 토큰 암호화 키가 설정되지 않았습니다. 환경변수 TOKEN_ENCRYPTION_KEY를 확인하세요.");
Expand Down