지금 뭐 중복 이메일 처리 자체는 시큐리티 때문에 잘 되는데 로그인과 회원가입만 막는거지 알람 같은건 뜨지 않아서 유저가 영문도 모른채 다른 아이디를 사용해야된다는 불편함이 있다.
문제 발견 이유.
현재 내 스포티 파이 계정의 이메일과 세개의 구글 계정중 하나가 이메일이 같아서 알게 되었다.
그래서 기존 Ut 클래스를 사용해서 알람을 띄워보려고 했다.
if (localUser.isPresent()) {
return new CustomOAuth2User(localUser.get(), oAuth2User.getAttributes());
} else {
// 이메일 중복 확인은 새로운 회원을 등록할 때만 실행
if (memberRepository.findByEmail(email).isPresent()) {
throw new OAuth2AuthenticationException(Ut.jsHistoryBack("ERROR", "이미 사용 중인 이메일입니다."));
}
회원 검증 단계에서 이런 로직을 추가해봤는데 알림이 뜨지 않았다.
그래서 알아보니까
OAuth2AuthenticationException
은 주로 서버 측에서 발생하므로, 클라이언트로 직접 메시지를 전달하는 데 적합하지 않다고 한다.
아 간단할 줄 알았는데 이것도 시큐리티 단계에서 해결해야된다.
CustomOAuth2FailureHandler
클래스 선언.. 일이 커질 것 같은디.
오케이 일단 코드 다 짜서 실행을 해봤는데 여전히 안된다.
생각의 흐름
지금 내 상황 말해줄게 내 구글 아이디 il-------@gmail.com이 있고,
스포티 파이로 로그인할 시에도 il-----@gmail.com 이 이메일을 써,
그래서 db 초기화 이후에 둘 중 하나가 먼저 로그인을 시도하면 db에 회원으로 등록되어서 하나는 로그인이 실패가 자동으로 돼, 왜냐면 이메일이 중복이라서, 이게 시큐리티에서는 실패인 로직으로 보지 않는 것 같아. 로그인은 실질적으로 실패가 되어서 로그인 페이지로 리다이렉트 되지만 login으로 리다이렉트 되지 login?error로 리다이렉트 되지 않아서 이유를 정확히 유저가 알 수가 없어 이런 경우면 어떻게 해결해야되냐 가 생각의 흐름.
원인이 Google과 Spotify 둘 다 같은 이메일 주소로 로그인 시도할 경우, DB에서 해당 이메일로 이미 사용자가 등록되어 있을 때 발생하는 문제. 이메일 중복 때문에 로그인은 실패하지만, Spring Security에서는 OAuth 인증 자체가 성공적으로 처리되었기 때문에 이 상황을 인증 실패로 간주하지 않고 단순히 로그인 페이지로 리다이렉트만 시킨다.
그래서 이메일 중복을 OAuth 인증 실패로 처리해줘야 함.
우선 결과적으로 성공했다.
public class EmailAlreadyExistsException extends OAuth2AuthenticationException {
public EmailAlreadyExistsException(String msg) {
super(new OAuth2Error("email_exists", msg, null));
}
}
해당 클래스로 이메일 중복 상황은 OAuth2Error
라고 명시해주고,
CustomOAuth2UserService
클래스에서 기존 회원 가입 로직에 조건을 추가,
if (memberRepository.findByEmail(email).isPresent()) {
// 중복된 이메일이 존재할 경우 커스텀 예외를 발생시켜 Spring Security에서 실패로 처리하도록 함
throw new EmailAlreadyExistsException("이미 사용 중인 이메일입니다.");
}
이렇게, 이후에 CustomOAuth2FailureHandler
를 선언해서 실패 핸들러를 커스텀으로 제작.
@Component
public class CustomOAuth2FailureHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
// 예외 메시지를 추출
String errorMessage;
if (exception instanceof EmailAlreadyExistsException) {
errorMessage = exception.getMessage(); // 이메일 중복 메시지 사용
} else {
errorMessage = "알 수 없는 이유로 로그인에 실패했습니다.";
}
// 오류 페이지로 리다이렉트 또는 알림
response.sendRedirect("/usr/member/login?error=true&message=" + URLEncoder.encode(errorMessage, "UTF-8"));
}
}
그리고 시큐리티 단계에서 OAuth2 인증 로직에 해당 핸들러를 추가해주고,
.oauth2Login(oauth2 -> oauth2
.loginPage("/usr/member/login")
.successHandler(oAuth2AuthenticationSuccessHandler) // 로그인 성공 후 핸들러
.defaultSuccessUrl("/usr/home/main", true)
.failureHandler(customOAuth2FailureHandler)
.userInfoEndpoint(userInfoEndpoint -> userInfoEndpoint.userService(customOAuth2UserService))
)
<!-- 오류 메시지 표시 -->
<c:if test="${param.error eq 'true'}">
<p style="color: red; font-weight: bold;">
<c:choose>
<c:when test="${not empty param.message}">
${param.message}
</c:when>
<c:otherwise>
로그인에 실패했습니다.
</c:otherwise>
</c:choose>
</p>
</c:if>
login.jsp
에서 제일 상단에 오류메세지 경우를 c:if
로 만들어주면!
아까의 이미지와 같은 결과를 얻을 수 있다.
근데 다른 문제가 생김.
회원가입을 하면서 로그인은 되는데 회원 가입 이후에는 다 차단해버린다 ㅋㅋㅋㅋ
이건 조건문의 수정이 조금 필요해보인다.
// 이미 기존 이메일로 로그인한 사용자가 있을 경우, 기존 사용자로 로그인 처리
if (localUser.isPresent()) {
return new CustomOAuth2User(localUser.get(), oAuth2User.getAttributes());
}
조건문의 더 상위에 해당 로직을 추가해서 문제 없이 로그인 된다!!
// Google 사용자 처리
private OAuth2User processGoogleUser(OAuth2User oAuth2User, String email, String googleLoginId) {
Optional<Member> localUser = memberRepository.findByGoogleLoginId(googleLoginId);
// 이미 기존 이메일로 로그인한 사용자가 있을 경우, 기존 사용자로 로그인 처리
if (localUser.isPresent()) {
return new CustomOAuth2User(localUser.get(), oAuth2User.getAttributes());
}
if (memberRepository.findByEmail(email).isPresent()) {
// 중복된 이메일이 존재할 경우 커스텀 예외를 발생시켜 Spring Security에서 실패로 처리하도록 함
throw new EmailAlreadyExistsException("이미 사용 중인 이메일입니다.");
}
System.out.println("로컬 DB에 Google 사용자가 존재하지 않음, 새로운 사용자 등록 시작");
String displayName = oAuth2User.getAttribute("name") != null ? oAuth2User.getAttribute("name") : "사용자";
String nickname = email.split("@")[0];
createGoogleUserInLocalDB(email, displayName, nickname, googleLoginId);
Member newUser = memberRepository.findByGoogleLoginId(googleLoginId).orElseThrow();
return new CustomOAuth2User(newUser, oAuth2User.getAttributes());
}
// Spotify 사용자 처리
private OAuth2User processSpotifyUser(OAuth2User oAuth2User, String email, String spotifyLoginId) {
Optional<Member> localUser = memberRepository.findBySpotifyLoginId(spotifyLoginId);
// 이미 기존 이메일로 로그인한 사용자가 있을 경우, 기존 사용자로 로그인 처리
if (localUser.isPresent()) {
return new CustomOAuth2User(localUser.get(), oAuth2User.getAttributes());
}
if (memberRepository.findByEmail(email).isPresent()) {
// 중복된 이메일이 존재할 경우 커스텀 예외를 발생시켜 Spring Security에서 실패로 처리하도록 함
throw new EmailAlreadyExistsException("이미 사용 중인 이메일입니다.");
}
System.out.println("로컬 DB에 Spotify 사용자가 존재하지 않음, 새로운 사용자 등록 시작");
String displayName = oAuth2User.getAttribute("display_name") != null ? oAuth2User.getAttribute("display_name") : "사용자";
String nickname = email.split("@")[0];
createSpotifyUserInLocalDB(email, displayName, nickname, spotifyLoginId);
Member newUser = memberRepository.findBySpotifyLoginId(spotifyLoginId).orElseThrow();
return new CustomOAuth2User(newUser, oAuth2User.getAttributes());
}
이게 해당 로직 전문이다.
암튼 이렇게 신경쓰이던 부분 해결 완료.
'Coding History > project' 카테고리의 다른 글
1차 개인프로젝트 발표. (중간 간담회) (8) | 2024.10.01 |
---|---|
지금까지의 기술스텍 및 기능정의서 (4) | 2024.09.26 |
[IDE-인텔리제이] java.lang.ClassCastException 오류 해결. (0) | 2024.09.26 |
sample DB 다시 설계 (1) | 2024.09.26 |
추천 곡 랜덤화와 앨범 이미지까지 삽입하기. (5) | 2024.09.23 |