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
4 changes: 3 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ dependencies {
runtimeOnly 'com.mysql:mysql-connector-j'
runtimeOnly 'com.h2database:h2'


// Security
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-validation'
Expand Down Expand Up @@ -88,6 +87,9 @@ dependencies {

// Jasypt
implementation 'com.github.ulisesbocchio:jasypt-spring-boot-starter:3.0.5'

// Slack-Alarm
implementation 'com.github.maricn:logback-slack-appender:1.6.1'
}

tasks.named('test') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@RequiredArgsConstructor
public class CustomLogoutFilter extends GenericFilterBean {

Expand Down Expand Up @@ -47,6 +49,8 @@ private void doFilter(HttpServletRequest request, HttpServletResponse response,

tokenProvider.deleteRefreshToken(id);

log.info("로그아웃 완료: userId={}", id);

ResponseUtil.writeNoContent(
response,
objectMapper,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
import jakarta.servlet.FilterChain;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public abstract class CustomUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

private final AuthenticationManager authenticationManager;
Expand Down Expand Up @@ -64,13 +66,17 @@ protected void successfulAuthentication(HttpServletRequest request, HttpServletR
@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
AuthenticationException failed) throws IOException {
handleFailureAuthentication(response);
handleFailureAuthentication(request, response);
}

private void handleSuccessAuthentication(HttpServletResponse response, Authentication authentication)
throws IOException {

CustomUserDetails userDetails = (CustomUserDetails)authentication.getPrincipal();

log.info("일반 로그인 성공: userId={}, role={}",
userDetails.getUserId(), userDetails.getRole());

String userid = String.valueOf(userDetails.getUserId());

Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
Expand All @@ -96,8 +102,20 @@ private void handleSuccessAuthentication(HttpServletResponse response, Authentic
);
}

private void handleFailureAuthentication(HttpServletResponse response) throws IOException {
private void handleFailureAuthentication(HttpServletRequest request, HttpServletResponse response) throws IOException {
String attemptedId = extractAttemptedId(request);
log.warn("로그인 실패: 아이디='{}', 잘못된 아이디 또는 비밀번호", attemptedId);

ResponseUtil.writeErrorResponse(response, objectMapper, AuthErrorCode.WRONG_ID_PW);
}

private String extractAttemptedId(HttpServletRequest request) {
try {
String messageBody = StreamUtils.copyToString(request.getInputStream(), StandardCharsets.UTF_8);
LoginRequest loginRequest = objectMapper.readValue(messageBody, LoginRequest.class);
return loginRequest.id();
} catch (Exception e) {
return "unknown";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {

Expand All @@ -44,18 +46,22 @@ protected void doFilterInternal(
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (ExpiredJwtException e) {
log.warn("만료된 토큰으로 접근 시도: 토큰 만료");
SecurityContextHolder.clearContext();
ResponseUtil.writeErrorResponse(response, objectMapper, AuthErrorCode.TOKEN_EXPIRED);
return;
} catch (IncorrectClaimException e) {
log.warn("잘못된 클레임 토큰으로 접근 시도: {}", e.getMessage());
SecurityContextHolder.clearContext();
ResponseUtil.writeErrorResponse(response, objectMapper, AuthErrorCode.INCORRECT_CLAIM_TOKEN);
return;
} catch (UsernameNotFoundException e) {
log.warn("존재하지 않는 사용자 토큰으로 접근 시도: {}", e.getMessage());
SecurityContextHolder.clearContext();
ResponseUtil.writeErrorResponse(response, objectMapper, AuthErrorCode.USER_NOT_FOUND);
return;
} catch (Exception e) {
log.warn("유효하지 않은 토큰으로 접근 시도: {}", e.getMessage());
SecurityContextHolder.clearContext();
ResponseUtil.writeErrorResponse(response, objectMapper, AuthErrorCode.INVALID_TOKEN);
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
@RequiredArgsConstructor
public class CustomOAuth2FailureHandler extends SimpleUrlAuthenticationFailureHandler {
Expand All @@ -22,6 +24,8 @@ public class CustomOAuth2FailureHandler extends SimpleUrlAuthenticationFailureHa
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException {

log.warn("소셜 로그인 실패: 오류={}", exception.getMessage());

response.sendRedirect(frontendUrl);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
@RequiredArgsConstructor
public class CustomOAuth2SuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
Expand Down Expand Up @@ -60,6 +62,10 @@ private void handleNewSocialUser(HttpServletResponse response, String socialId,

private void handleExistingUser(HttpServletResponse response, CustomUserDetails userDetails,
String registrationId) throws IOException {

log.info("소셜 로그인 성공: userId={}, socialType={}, role={}",
userDetails.getUserId(), registrationId.toUpperCase(), userDetails.getRole());

String userid = String.valueOf(userDetails.getUserId());

UserTokenResponse loginToken = tokenProvider.createLoginToken(userid, userDetails.getRole());
Expand Down
33 changes: 30 additions & 3 deletions src/main/java/doldol_server/doldol/auth/service/AuthService.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@
import doldol_server.doldol.user.service.UserService;
import io.jsonwebtoken.Claims;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
Expand All @@ -44,6 +46,7 @@ public void checkIdDuplicate(String id) {
boolean isIdExists = userRepository.existsByLoginId(id);

if (isIdExists) {
log.warn("아이디 중복 확인: 이미 존재하는 아이디={}", id);
throw new CustomException(AuthErrorCode.ID_DUPLICATED);
}
}
Expand Down Expand Up @@ -78,15 +81,18 @@ public void validateVerificationCode(String email, String code) {
Object result = redisTemplate.opsForValue().get(email);

if (result == null) {
log.warn("인증 코드 만료: email={}", email);
throw new CustomException(AuthErrorCode.EMAIL_NOT_FOUND);
}

String verificationCode = result.toString();

if (!verificationCode.equals(code)) {
log.warn("인증 코드 불일치: email={}, 입력코드={}", email, code);
throw new CustomException(AuthErrorCode.VERIFICATION_CODE_WRONG);
}

log.info("이메일 인증 완료: email={}", email);
redisTemplate.opsForValue().set(email, EMAIL_VERIFIED_KEY, 30, TimeUnit.MINUTES);
}

Expand All @@ -103,6 +109,9 @@ public void register(RegisterRequest registerRequest) {
.build();

userRepository.save(user);

log.info("자체 회원가입 완료: userId={}, email={}, name='{}'",
user.getId(), user.getEmail(), user.getName());
}

@Transactional
Expand All @@ -118,6 +127,9 @@ public void oauthRegister(OAuthRegisterRequest oAuthRegisterRequest) {
.build();

userRepository.save(user);

log.info("소셜 회원가입 완료: userId={}, email={}, socialType={}",
user.getId(), user.getEmail(), user.getSocialType());
}

@Transactional
Expand All @@ -139,6 +151,8 @@ public ReissueTokenResponse reissue(String refreshToken) {

UserTokenResponse newTokens = tokenProvider.createLoginToken(userId, user.getRole().getRole());

log.info("토큰 재발급 완료: userId={}", userId);

return ReissueTokenResponse
.builder()
.accessToken(newTokens.accessToken())
Expand All @@ -155,14 +169,22 @@ public void withdraw(Long userId) {
}

if (user.getSocialId() != null) {
OAuth2ResponseStrategy strategy = oAuthSeperator.getStrategy(user.getSocialType().name());
strategy.unlink(user.getSocialId());
user.deleteOAuthInfo();
try {
OAuth2ResponseStrategy strategy = oAuthSeperator.getStrategy(user.getSocialType().name());
strategy.unlink(user.getSocialId());
user.deleteOAuthInfo();
} catch (Exception e) {
log.error("소셜 계정 연동 해제 실패: userId={}, socialType={}, 오류={}",
userId, user.getSocialType(), e.getMessage(), e);
}
}

user.updateDeleteStatus();

tokenProvider.deleteRefreshToken(String.valueOf(userId));

log.info("소셜 계정 연동 해제: userId={}, socialType={}",
userId, user.getSocialType());
}

public void validateUserInfo(String name, String email, String phone) {
Expand Down Expand Up @@ -205,17 +227,22 @@ public void resetPassword(String email) {
user.updateUserPassword(passwordEncoder.encode(tempPassword));

emailService.sendEmailTempPassword(email, tempPassword);

log.info("비밀번호 재설정 완료: email={}, userId={}", email, user.getId());
}

public void checkRegisterInfoDuplicate(String email, String phone) {
boolean existsByEmail = userRepository.existsByEmail(email);
boolean existsByPhone = userRepository.existsByPhone(phone);

if (existsByEmail && !existsByPhone) {
log.warn("이메일 중복: email={}", email);
throw new CustomException(AuthErrorCode.EMAIl_DUPLICATED);
} else if (!existsByEmail && existsByPhone) {
log.warn("전화번호 중복: phone={}", phone);
throw new CustomException(AuthErrorCode.PHONE_DUPLICATED);
} else if (existsByEmail && existsByPhone) {
log.warn("이메일/전화번호 모두 중복: email={}, phone={}", email, phone);
throw new CustomException(AuthErrorCode.EMAIL_PHONE_DUPLICATED);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
import doldol_server.doldol.user.repository.UserRepository;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
@RequiredArgsConstructor
public class CustomOAuth2UserService extends DefaultOAuth2UserService {
Expand Down Expand Up @@ -51,6 +53,8 @@ public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2Authentic
}

else if (socialLinkedUser.isPresent() && socialLinkedUser.get().isDeleted()) {
log.warn("탈퇴한 계정으로 소셜 로그인 시도: socialId={}, socialType={}",
oAuth2Response.getSocialId(), registrationId);
throw new CustomOAuth2Exception(AuthErrorCode.ALREADY_WITHDRAWN);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
@RequiredArgsConstructor
public class EmailService {
Expand All @@ -29,7 +31,9 @@ public void sendEmailVerificationCode(String email, String verificationCode) {
}
try {
sendToEmailCode(email, verificationCode);
log.info("인증 코드 이메일 발송 완료: email={}", email);
} catch (MessagingException e) {
log.error("인증 코드 이메일 발송 실패: email={}, 오류={}", email, e.getMessage(), e);
throw new CustomException(MailErrorCode.EMAIL_SENDING_ERROR);
}
}
Expand All @@ -41,7 +45,9 @@ public void sendEmailTempPassword(String email, String password) {
}
try {
sendToEmailTempPassword(email, password);
log.info("임시 비밀번호 이메일 발송 완료: email={}", email);
} catch (MessagingException e) {
log.error("임시 비밀번호 이메일 발송 실패: email={}, 오류={}", email, e.getMessage(), e);
throw new CustomException(MailErrorCode.EMAIL_SENDING_ERROR);
}
}
Expand Down
Loading
Loading