From 25c0ab4871a047c213130d810e45e3501a658806 Mon Sep 17 00:00:00 2001 From: jiwon1217 Date: Mon, 14 Jul 2025 11:46:05 +0900 Subject: [PATCH 1/4] =?UTF-8?q?:wrench:=20chore:=20Kakao=20OAuth=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B4=80=EB=A0=A8=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/build.gradle | 3 +++ backend/src/main/resources/application.yml | 23 ++++++++++++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/backend/build.gradle b/backend/build.gradle index e5a4520f..1b77cc8b 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -29,6 +29,8 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-data-redis' implementation 'org.springframework.boot:spring-boot-starter-websocket' + implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' /* DATABASE */ runtimeOnly 'com.mysql:mysql-connector-j' @@ -39,6 +41,7 @@ dependencies { testImplementation 'org.projectlombok:lombok' testAnnotationProcessor 'org.projectlombok:lombok' testRuntimeOnly 'com.h2database:h2' + testImplementation 'org.springframework.security:spring-security-test' /* ETC */ annotationProcessor 'org.projectlombok:lombok' diff --git a/backend/src/main/resources/application.yml b/backend/src/main/resources/application.yml index 82beff8c..a7adfd8b 100644 --- a/backend/src/main/resources/application.yml +++ b/backend/src/main/resources/application.yml @@ -4,7 +4,7 @@ spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver - url : ${DB_URL} + url: ${DB_URL} username: ${DB_USERNAME} password: ${DB_PASSWORD} @@ -20,4 +20,23 @@ spring: properties: hibernate: show_sql: true - format_sql: true \ No newline at end of file + format_sql: true + dialect: org.hibernate.dialect.MySQLDialect + + security: + oauth2: + client: + registration: + kakao: + client-name: Kakao + client-id: ${KAKAO_CLIENT} + client-secret: ${KAKAO_SECRET} + redirect-uri: "{baseUrl}/{action}/oauth2/code/{registrationId}" + authorization-grant-type: authorization_code + client-authentication-method: client_secret_post + provider: + kakao: + authorization-uri: https://kauth.kakao.com/oauth/authorize + token-uri: https://kauth.kakao.com/oauth/token + user-info-uri: https://kapi.kakao.com/v2/user/me + user-name-attribute: id \ No newline at end of file From 22cb3701d234bff1c357bf6b218dc6395d5e776b Mon Sep 17 00:00:00 2001 From: jiwon1217 Date: Mon, 14 Jul 2025 11:47:13 +0900 Subject: [PATCH 2/4] =?UTF-8?q?:sparkles:=20feat:=20Kakao=20Oauth=20?= =?UTF-8?q?=ED=9A=8C=EC=9B=90=EA=B0=80=EC=9E=85,=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8,=20=EB=A1=9C=EA=B7=B8=EC=95=84=EC=9B=83=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../f1/backend/domain/stat/entity/Stat.java | 12 +++ .../domain/user/api/SignupController.java | 26 +++++++ .../user/app/CustomOAuthUserService.java | 64 ++++++++++++++++ .../backend/domain/user/app/UserService.java | 73 ++++++++++++++++++ .../CustomAuthenticationEntryPoint.java | 20 +++++ .../handler/OAuthLogoutSuccessHandler.java | 17 +++++ .../user/app/handler/OAuthSuccessHandler.java | 41 ++++++++++ .../domain/user/dao/UserRepository.java | 14 ++++ .../backend/domain/user/dto/SessionUser.java | 22 ++++++ .../domain/user/dto/SignupRequestDto.java | 7 ++ .../domain/user/dto/SignupResponseDto.java | 24 ++++++ .../domain/user/dto/UserPrincipal.java | 76 +++++++++++++++++++ .../f1/backend/domain/user/entity/User.java | 25 +++++- .../backend/global/config/SecurityConfig.java | 54 +++++++++++++ .../f1/backend/global/util/SecurityUtils.java | 22 ++++++ 15 files changed, 496 insertions(+), 1 deletion(-) create mode 100644 backend/src/main/java/io/f1/backend/domain/user/api/SignupController.java create mode 100644 backend/src/main/java/io/f1/backend/domain/user/app/CustomOAuthUserService.java create mode 100644 backend/src/main/java/io/f1/backend/domain/user/app/UserService.java create mode 100644 backend/src/main/java/io/f1/backend/domain/user/app/handler/CustomAuthenticationEntryPoint.java create mode 100644 backend/src/main/java/io/f1/backend/domain/user/app/handler/OAuthLogoutSuccessHandler.java create mode 100644 backend/src/main/java/io/f1/backend/domain/user/app/handler/OAuthSuccessHandler.java create mode 100644 backend/src/main/java/io/f1/backend/domain/user/dao/UserRepository.java create mode 100644 backend/src/main/java/io/f1/backend/domain/user/dto/SessionUser.java create mode 100644 backend/src/main/java/io/f1/backend/domain/user/dto/SignupRequestDto.java create mode 100644 backend/src/main/java/io/f1/backend/domain/user/dto/SignupResponseDto.java create mode 100644 backend/src/main/java/io/f1/backend/domain/user/dto/UserPrincipal.java create mode 100644 backend/src/main/java/io/f1/backend/global/config/SecurityConfig.java create mode 100644 backend/src/main/java/io/f1/backend/global/util/SecurityUtils.java diff --git a/backend/src/main/java/io/f1/backend/domain/stat/entity/Stat.java b/backend/src/main/java/io/f1/backend/domain/stat/entity/Stat.java index 5bcb8b3a..811d0a9f 100644 --- a/backend/src/main/java/io/f1/backend/domain/stat/entity/Stat.java +++ b/backend/src/main/java/io/f1/backend/domain/stat/entity/Stat.java @@ -11,8 +11,12 @@ import jakarta.persistence.Id; import jakarta.persistence.JoinColumn; import jakarta.persistence.OneToOne; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.NoArgsConstructor; @Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) public class Stat extends BaseEntity { @Id @@ -31,4 +35,12 @@ public class Stat extends BaseEntity { @Column(nullable = false) private Long score; + + @Builder + public Stat(User user, Long totalGames, Long winningGames, Long score) { + this.user = user; + this.totalGames = totalGames; + this.winningGames = winningGames; + this.score = score; + } } diff --git a/backend/src/main/java/io/f1/backend/domain/user/api/SignupController.java b/backend/src/main/java/io/f1/backend/domain/user/api/SignupController.java new file mode 100644 index 00000000..eb121159 --- /dev/null +++ b/backend/src/main/java/io/f1/backend/domain/user/api/SignupController.java @@ -0,0 +1,26 @@ +package io.f1.backend.domain.user.api; + +import io.f1.backend.domain.user.app.UserService; +import io.f1.backend.domain.user.dto.SignupRequestDto; +import io.f1.backend.domain.user.dto.SignupResponseDto; +import jakarta.servlet.http.HttpSession; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +public class SignupController { + + private final UserService userService; + + @PostMapping("/signup") + public ResponseEntity completeSignup( + @RequestBody SignupRequestDto signupRequest, HttpSession httpSession) { + SignupResponseDto response = userService.signup(httpSession, signupRequest); + return ResponseEntity.status(HttpStatus.CREATED).body(response); + } +} diff --git a/backend/src/main/java/io/f1/backend/domain/user/app/CustomOAuthUserService.java b/backend/src/main/java/io/f1/backend/domain/user/app/CustomOAuthUserService.java new file mode 100644 index 00000000..2ef98c65 --- /dev/null +++ b/backend/src/main/java/io/f1/backend/domain/user/app/CustomOAuthUserService.java @@ -0,0 +1,64 @@ +package io.f1.backend.domain.user.app; + +import io.f1.backend.domain.stat.entity.Stat; +import io.f1.backend.domain.user.dao.UserRepository; +import io.f1.backend.domain.user.dto.SessionUser; +import io.f1.backend.domain.user.dto.UserPrincipal; +import io.f1.backend.domain.user.entity.User; +import jakarta.servlet.http.HttpSession; +import java.time.LocalDateTime; +import java.util.Objects; +import lombok.RequiredArgsConstructor; +import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService; +import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest; +import org.springframework.security.oauth2.core.OAuth2AuthenticationException; +import org.springframework.security.oauth2.core.user.OAuth2User; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class CustomOAuthUserService extends DefaultOAuth2UserService { + + private final UserRepository userRepository; + private final HttpSession httpSession; + + @Override + @Transactional + public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException { + OAuth2User oAuth2User = super.loadUser(userRequest); + + String provider = userRequest.getClientRegistration().getRegistrationId(); + String providerId = Objects.requireNonNull(oAuth2User.getAttribute("id")).toString(); + + User user = userRepository.findByProviderAndProviderId(provider, providerId) + .map(this::updateLastLogin) + .orElseGet(() -> createNewUser(provider, providerId)); + + httpSession.setAttribute("OAuthUser", new SessionUser(user)); + return new UserPrincipal(user, oAuth2User.getAttributes()); + } + + private User updateLastLogin(User user) { + user.updateLastLogin(LocalDateTime.now()); + return userRepository.save(user); + } + + private User createNewUser(String provider, String providerId) { + User user = User.builder() + .provider(provider) + .providerId(providerId) + .lastLogin(LocalDateTime.now()) + .build(); + + Stat stat = Stat.builder() + .totalGames(0L) + .winningGames(0L) + .score(0L) + .user(user) + .build(); + + user.initStat(stat); + return userRepository.save(user); + } +} diff --git a/backend/src/main/java/io/f1/backend/domain/user/app/UserService.java b/backend/src/main/java/io/f1/backend/domain/user/app/UserService.java new file mode 100644 index 00000000..7eb24d1e --- /dev/null +++ b/backend/src/main/java/io/f1/backend/domain/user/app/UserService.java @@ -0,0 +1,73 @@ +package io.f1.backend.domain.user.app; + +import io.f1.backend.domain.user.dao.UserRepository; +import io.f1.backend.domain.user.dto.SessionUser; +import io.f1.backend.domain.user.dto.SignupRequestDto; +import io.f1.backend.domain.user.dto.SignupResponseDto; +import io.f1.backend.domain.user.entity.User; +import io.f1.backend.global.util.SecurityUtils; +import jakarta.servlet.http.HttpSession; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class UserService { + + private final UserRepository userRepository; + + @Transactional + public SignupResponseDto signup(HttpSession session, SignupRequestDto signupRequest) { + SessionUser sessionUser = extractSessionUser(session); + + String nickname = signupRequest.nickname(); + validateNickname(nickname); + validateDuplicateNickname(nickname); + + User user = updateUserNickname(sessionUser.getUserId(), nickname); + updateSessionAfterSignup(session, user); + SecurityUtils.setAuthentication(user); + + return SignupResponseDto.toDto(user); + } + + private SessionUser extractSessionUser(HttpSession session) { + SessionUser sessionUser = (SessionUser) session.getAttribute("OAuthUser"); + if (sessionUser == null) { + throw new RuntimeException("세션에 OAuth 정보 없음"); + } + return sessionUser; + } + + private void validateNickname(String nickname) { + if (nickname == null || nickname.trim().isEmpty()) { + throw new RuntimeException("E400002: 닉네임은 필수 입력입니다."); + } + if (nickname.length() > 6) { + throw new RuntimeException("E400003: 닉네임은 6글자 이하로 입력해야 합니다."); + } + if (!nickname.matches("^[가-힣a-zA-Z0-9]+$")) { + throw new RuntimeException("E400004: 한글, 영문, 숫자만 입력해주세요."); + } + } + + private void validateDuplicateNickname(String nickname) { + if (userRepository.existsUserByNickname(nickname)) { + throw new RuntimeException("닉네임 중복"); + } + } + + private User updateUserNickname(Long userId, String nickname) { + User user = userRepository.findById(userId) + .orElseThrow(() -> new RuntimeException("사용자 없음")); + user.updateNickname(nickname); + + return userRepository.save(user); + } + + private void updateSessionAfterSignup(HttpSession session, User user) { + session.removeAttribute("OAuthUser"); + session.setAttribute("user", new SessionUser(user)); + } +} diff --git a/backend/src/main/java/io/f1/backend/domain/user/app/handler/CustomAuthenticationEntryPoint.java b/backend/src/main/java/io/f1/backend/domain/user/app/handler/CustomAuthenticationEntryPoint.java new file mode 100644 index 00000000..a659852f --- /dev/null +++ b/backend/src/main/java/io/f1/backend/domain/user/app/handler/CustomAuthenticationEntryPoint.java @@ -0,0 +1,20 @@ +package io.f1.backend.domain.user.app.handler; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.stereotype.Component; + +@Component +public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint { + + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, + AuthenticationException authException) throws IOException { + response.setContentType("application/json;charset=UTF-8"); + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); // 401 + response.getWriter().write("{\"error\": \"Unauthorized\"}"); + } +} \ No newline at end of file diff --git a/backend/src/main/java/io/f1/backend/domain/user/app/handler/OAuthLogoutSuccessHandler.java b/backend/src/main/java/io/f1/backend/domain/user/app/handler/OAuthLogoutSuccessHandler.java new file mode 100644 index 00000000..4bfe57f6 --- /dev/null +++ b/backend/src/main/java/io/f1/backend/domain/user/app/handler/OAuthLogoutSuccessHandler.java @@ -0,0 +1,17 @@ +package io.f1.backend.domain.user.app.handler; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; +import org.springframework.stereotype.Component; + +@Component +public class OAuthLogoutSuccessHandler implements LogoutSuccessHandler { + + @Override + public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, + Authentication authentication) { + response.setStatus(HttpServletResponse.SC_NO_CONTENT); // 204 + } +} diff --git a/backend/src/main/java/io/f1/backend/domain/user/app/handler/OAuthSuccessHandler.java b/backend/src/main/java/io/f1/backend/domain/user/app/handler/OAuthSuccessHandler.java new file mode 100644 index 00000000..f6387914 --- /dev/null +++ b/backend/src/main/java/io/f1/backend/domain/user/app/handler/OAuthSuccessHandler.java @@ -0,0 +1,41 @@ +package io.f1.backend.domain.user.app.handler; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.f1.backend.domain.user.dto.UserPrincipal; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; +import java.io.IOException; +import java.util.Map; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class OAuthSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { + + private final ObjectMapper objectMapper; + + @Override + public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, + Authentication authentication) throws IOException { + UserPrincipal principal = (UserPrincipal) authentication.getPrincipal(); + response.setContentType("application/json;charset=UTF-8"); + + if (principal.getUserNickname() == null) { + // 닉네임 설정 필요 → 202 Accepted + response.setStatus(HttpServletResponse.SC_ACCEPTED); + objectMapper.writeValue(response.getWriter(), Map.of( + "message", "닉네임을 설정하세요." + )); + } else { + // 정상 로그인 → 200 OK + response.setStatus(HttpServletResponse.SC_OK); + objectMapper.writeValue(response.getWriter(), Map.of( + "message", "로그인 성공" + )); + } + } +} diff --git a/backend/src/main/java/io/f1/backend/domain/user/dao/UserRepository.java b/backend/src/main/java/io/f1/backend/domain/user/dao/UserRepository.java new file mode 100644 index 00000000..2a235544 --- /dev/null +++ b/backend/src/main/java/io/f1/backend/domain/user/dao/UserRepository.java @@ -0,0 +1,14 @@ +package io.f1.backend.domain.user.dao; + +import io.f1.backend.domain.user.entity.User; +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface UserRepository extends JpaRepository { + + Optional findByProviderAndProviderId(String provider, String providerId); + + Boolean existsUserByNickname(String nickname); +} diff --git a/backend/src/main/java/io/f1/backend/domain/user/dto/SessionUser.java b/backend/src/main/java/io/f1/backend/domain/user/dto/SessionUser.java new file mode 100644 index 00000000..cc6d59a7 --- /dev/null +++ b/backend/src/main/java/io/f1/backend/domain/user/dto/SessionUser.java @@ -0,0 +1,22 @@ +package io.f1.backend.domain.user.dto; + +import io.f1.backend.domain.user.entity.User; +import java.io.Serializable; +import java.time.LocalDateTime; +import lombok.Getter; + +@Getter +public class SessionUser implements Serializable { + + private final Long userId; + private final String nickname; + private final String providerId; + private final LocalDateTime lastLogin; + + public SessionUser(User user) { + this.userId = user.getId(); + this.nickname = user.getNickname(); + this.providerId = user.getProviderId(); + this.lastLogin = user.getLastLogin(); + } +} \ No newline at end of file diff --git a/backend/src/main/java/io/f1/backend/domain/user/dto/SignupRequestDto.java b/backend/src/main/java/io/f1/backend/domain/user/dto/SignupRequestDto.java new file mode 100644 index 00000000..5ef1e5a5 --- /dev/null +++ b/backend/src/main/java/io/f1/backend/domain/user/dto/SignupRequestDto.java @@ -0,0 +1,7 @@ +package io.f1.backend.domain.user.dto; + +import jakarta.validation.constraints.NotBlank; + +public record SignupRequestDto(@NotBlank(message = "닉네임을 입력하세요") String nickname) { + +} diff --git a/backend/src/main/java/io/f1/backend/domain/user/dto/SignupResponseDto.java b/backend/src/main/java/io/f1/backend/domain/user/dto/SignupResponseDto.java new file mode 100644 index 00000000..9c591470 --- /dev/null +++ b/backend/src/main/java/io/f1/backend/domain/user/dto/SignupResponseDto.java @@ -0,0 +1,24 @@ +package io.f1.backend.domain.user.dto; + +import io.f1.backend.domain.user.entity.User; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class SignupResponseDto { + + private Long id; + private String nickname; + + public static SignupResponseDto toDto(User user) { + return SignupResponseDto.builder() + .id(user.getId()) + .nickname(user.getNickname()) + .build(); + } +} diff --git a/backend/src/main/java/io/f1/backend/domain/user/dto/UserPrincipal.java b/backend/src/main/java/io/f1/backend/domain/user/dto/UserPrincipal.java new file mode 100644 index 00000000..10719c81 --- /dev/null +++ b/backend/src/main/java/io/f1/backend/domain/user/dto/UserPrincipal.java @@ -0,0 +1,76 @@ +package io.f1.backend.domain.user.dto; + +import io.f1.backend.domain.user.entity.User; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import lombok.Getter; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.oauth2.core.user.OAuth2User; + +@Getter +public class UserPrincipal implements UserDetails, OAuth2User { + + public static final String ROLE_USER = "ROLE_USER"; + private final User user; + private final Map attributes; + + public UserPrincipal(User user, Map attributes) { + this.user = user; + this.attributes = attributes; + } + + @Override + public Map getAttributes() { + return attributes; + } + + public Long getUserId() { + return user.getId(); + } + + public String getUserNickname() { + return user.getNickname(); + } + + @Override + public String getName() { + return user.getProviderId(); + } + + @Override + public Collection getAuthorities() { + return Collections.singleton(() -> ROLE_USER); + } + + @Override + public String getPassword() { + return null; // 소셜 로그인이라 비밀번호 없음 + } + + @Override + public String getUsername() { + return user.getProviderId(); + } + + @Override + public boolean isAccountNonExpired() { + return true; + } + + @Override + public boolean isAccountNonLocked() { + return true; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return true; + } +} diff --git a/backend/src/main/java/io/f1/backend/domain/user/entity/User.java b/backend/src/main/java/io/f1/backend/domain/user/entity/User.java index e989134c..6f9f7df6 100644 --- a/backend/src/main/java/io/f1/backend/domain/user/entity/User.java +++ b/backend/src/main/java/io/f1/backend/domain/user/entity/User.java @@ -12,7 +12,10 @@ import jakarta.persistence.OneToOne; import jakarta.persistence.Table; +import lombok.AccessLevel; +import lombok.Builder; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.Setter; import java.time.LocalDateTime; @@ -21,6 +24,7 @@ @Setter // quizService의 퀴즈 조회 메서드 구현 시까지 임시 사용 @Entity @Table(name = "`user`") +@NoArgsConstructor public class User extends BaseEntity { @Id @@ -30,7 +34,7 @@ public class User extends BaseEntity { @OneToOne(mappedBy = "user", cascade = CascadeType.ALL) private Stat stat; - @Column(unique = true, nullable = false) + @Column(unique = true) private String nickname; @Column(nullable = false) @@ -41,4 +45,23 @@ public class User extends BaseEntity { @Column(nullable = false) private LocalDateTime lastLogin; + + @Builder + public User(String provider, String providerId, LocalDateTime lastLogin) { + this.provider = provider; + this.providerId = providerId; + this.lastLogin = lastLogin; + } + + public void updateNickname(String nickname) { + this.nickname = nickname; + } + + public void updateLastLogin(LocalDateTime lastLogin) { + this.lastLogin = lastLogin; + } + + public void initStat(Stat stat) { + this.stat = stat; + } } diff --git a/backend/src/main/java/io/f1/backend/global/config/SecurityConfig.java b/backend/src/main/java/io/f1/backend/global/config/SecurityConfig.java new file mode 100644 index 00000000..49255ae1 --- /dev/null +++ b/backend/src/main/java/io/f1/backend/global/config/SecurityConfig.java @@ -0,0 +1,54 @@ +package io.f1.backend.global.config; + +import io.f1.backend.domain.user.app.handler.CustomAuthenticationEntryPoint; +import io.f1.backend.domain.user.app.CustomOAuthUserService; +import io.f1.backend.domain.user.app.handler.OAuthLogoutSuccessHandler; +import io.f1.backend.domain.user.app.handler.OAuthSuccessHandler; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.web.SecurityFilterChain; + +@Configuration +@EnableWebSecurity +@RequiredArgsConstructor +public class SecurityConfig { + + private final CustomAuthenticationEntryPoint customAuthenticationEntryPoint; + private final CustomOAuthUserService customOAuthUserService; + private final OAuthSuccessHandler oAuthSuccessHandler; + private final OAuthLogoutSuccessHandler oAuthLogoutSuccessHandler; + + @Bean + public SecurityFilterChain userFilterChain(HttpSecurity http) throws Exception { + http + .csrf(AbstractHttpConfigurer::disable) + .exceptionHandling(exception -> exception + .authenticationEntryPoint(customAuthenticationEntryPoint) + ) + .authorizeHttpRequests(auth -> auth + .requestMatchers("/", "/login", "/oauth2/**", "/signup", "/css/**", "/js/**") + .permitAll() + .requestMatchers("/ws/**").authenticated() + .anyRequest().authenticated() + ) + .formLogin(AbstractHttpConfigurer::disable) + .oauth2Login(oauth2 -> oauth2 + .userInfoEndpoint(userInfo -> userInfo + .userService(customOAuthUserService) + ) + .successHandler(oAuthSuccessHandler) + ) + .logout(logout -> logout + .logoutUrl("/logout") + .logoutSuccessHandler(oAuthLogoutSuccessHandler) + .clearAuthentication(true) + .invalidateHttpSession(true) + .permitAll() + ); + return http.build(); + } +} \ No newline at end of file diff --git a/backend/src/main/java/io/f1/backend/global/util/SecurityUtils.java b/backend/src/main/java/io/f1/backend/global/util/SecurityUtils.java new file mode 100644 index 00000000..c284158a --- /dev/null +++ b/backend/src/main/java/io/f1/backend/global/util/SecurityUtils.java @@ -0,0 +1,22 @@ +package io.f1.backend.global.util; + +import io.f1.backend.domain.user.dto.UserPrincipal; +import io.f1.backend.domain.user.entity.User; +import java.util.Collections; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; + +public class SecurityUtils { + + private SecurityUtils() { + + } + + public static void setAuthentication(User user) { + UserPrincipal userPrincipal = new UserPrincipal(user, Collections.emptyMap()); + UsernamePasswordAuthenticationToken authentication = + new UsernamePasswordAuthenticationToken(userPrincipal, null, + userPrincipal.getAuthorities()); + SecurityContextHolder.getContext().setAuthentication(authentication); + } +} From 53fa387d16c353a303450da4cf70edc50a21256e Mon Sep 17 00:00:00 2001 From: github-actions <> Date: Mon, 14 Jul 2025 02:49:07 +0000 Subject: [PATCH 3/4] =?UTF-8?q?chore:=20Java=20=EC=8A=A4=ED=83=80=EC=9D=BC?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../f1/backend/domain/stat/entity/Stat.java | 1 + .../domain/user/api/SignupController.java | 5 +- .../user/app/CustomOAuthUserService.java | 34 +++++----- .../backend/domain/user/app/UserService.java | 7 +- .../CustomAuthenticationEntryPoint.java | 13 ++-- .../handler/OAuthLogoutSuccessHandler.java | 7 +- .../user/app/handler/OAuthSuccessHandler.java | 23 +++---- .../domain/user/dao/UserRepository.java | 4 +- .../backend/domain/user/dto/SessionUser.java | 6 +- .../domain/user/dto/SignupRequestDto.java | 4 +- .../domain/user/dto/SignupResponseDto.java | 6 +- .../domain/user/dto/UserPrincipal.java | 9 ++- .../f1/backend/domain/user/entity/User.java | 1 - .../backend/global/config/SecurityConfig.java | 64 +++++++++++-------- .../f1/backend/global/util/SecurityUtils.java | 12 ++-- 15 files changed, 113 insertions(+), 83 deletions(-) diff --git a/backend/src/main/java/io/f1/backend/domain/stat/entity/Stat.java b/backend/src/main/java/io/f1/backend/domain/stat/entity/Stat.java index 811d0a9f..c022f16c 100644 --- a/backend/src/main/java/io/f1/backend/domain/stat/entity/Stat.java +++ b/backend/src/main/java/io/f1/backend/domain/stat/entity/Stat.java @@ -11,6 +11,7 @@ import jakarta.persistence.Id; import jakarta.persistence.JoinColumn; import jakarta.persistence.OneToOne; + import lombok.AccessLevel; import lombok.Builder; import lombok.NoArgsConstructor; diff --git a/backend/src/main/java/io/f1/backend/domain/user/api/SignupController.java b/backend/src/main/java/io/f1/backend/domain/user/api/SignupController.java index eb121159..d0cb45ac 100644 --- a/backend/src/main/java/io/f1/backend/domain/user/api/SignupController.java +++ b/backend/src/main/java/io/f1/backend/domain/user/api/SignupController.java @@ -3,8 +3,11 @@ import io.f1.backend.domain.user.app.UserService; import io.f1.backend.domain.user.dto.SignupRequestDto; import io.f1.backend.domain.user.dto.SignupResponseDto; + import jakarta.servlet.http.HttpSession; + import lombok.RequiredArgsConstructor; + import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; @@ -19,7 +22,7 @@ public class SignupController { @PostMapping("/signup") public ResponseEntity completeSignup( - @RequestBody SignupRequestDto signupRequest, HttpSession httpSession) { + @RequestBody SignupRequestDto signupRequest, HttpSession httpSession) { SignupResponseDto response = userService.signup(httpSession, signupRequest); return ResponseEntity.status(HttpStatus.CREATED).body(response); } diff --git a/backend/src/main/java/io/f1/backend/domain/user/app/CustomOAuthUserService.java b/backend/src/main/java/io/f1/backend/domain/user/app/CustomOAuthUserService.java index 2ef98c65..61fd8bd6 100644 --- a/backend/src/main/java/io/f1/backend/domain/user/app/CustomOAuthUserService.java +++ b/backend/src/main/java/io/f1/backend/domain/user/app/CustomOAuthUserService.java @@ -5,10 +5,11 @@ import io.f1.backend.domain.user.dto.SessionUser; import io.f1.backend.domain.user.dto.UserPrincipal; import io.f1.backend.domain.user.entity.User; + import jakarta.servlet.http.HttpSession; -import java.time.LocalDateTime; -import java.util.Objects; + import lombok.RequiredArgsConstructor; + import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService; import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest; import org.springframework.security.oauth2.core.OAuth2AuthenticationException; @@ -16,6 +17,9 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.time.LocalDateTime; +import java.util.Objects; + @Service @RequiredArgsConstructor public class CustomOAuthUserService extends DefaultOAuth2UserService { @@ -31,9 +35,11 @@ public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2Authentic String provider = userRequest.getClientRegistration().getRegistrationId(); String providerId = Objects.requireNonNull(oAuth2User.getAttribute("id")).toString(); - User user = userRepository.findByProviderAndProviderId(provider, providerId) - .map(this::updateLastLogin) - .orElseGet(() -> createNewUser(provider, providerId)); + User user = + userRepository + .findByProviderAndProviderId(provider, providerId) + .map(this::updateLastLogin) + .orElseGet(() -> createNewUser(provider, providerId)); httpSession.setAttribute("OAuthUser", new SessionUser(user)); return new UserPrincipal(user, oAuth2User.getAttributes()); @@ -45,18 +51,14 @@ private User updateLastLogin(User user) { } private User createNewUser(String provider, String providerId) { - User user = User.builder() - .provider(provider) - .providerId(providerId) - .lastLogin(LocalDateTime.now()) - .build(); + User user = + User.builder() + .provider(provider) + .providerId(providerId) + .lastLogin(LocalDateTime.now()) + .build(); - Stat stat = Stat.builder() - .totalGames(0L) - .winningGames(0L) - .score(0L) - .user(user) - .build(); + Stat stat = Stat.builder().totalGames(0L).winningGames(0L).score(0L).user(user).build(); user.initStat(stat); return userRepository.save(user); diff --git a/backend/src/main/java/io/f1/backend/domain/user/app/UserService.java b/backend/src/main/java/io/f1/backend/domain/user/app/UserService.java index 7eb24d1e..57e27746 100644 --- a/backend/src/main/java/io/f1/backend/domain/user/app/UserService.java +++ b/backend/src/main/java/io/f1/backend/domain/user/app/UserService.java @@ -6,8 +6,11 @@ import io.f1.backend.domain.user.dto.SignupResponseDto; import io.f1.backend.domain.user.entity.User; import io.f1.backend.global.util.SecurityUtils; + import jakarta.servlet.http.HttpSession; + import lombok.RequiredArgsConstructor; + import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -59,8 +62,8 @@ private void validateDuplicateNickname(String nickname) { } private User updateUserNickname(Long userId, String nickname) { - User user = userRepository.findById(userId) - .orElseThrow(() -> new RuntimeException("사용자 없음")); + User user = + userRepository.findById(userId).orElseThrow(() -> new RuntimeException("사용자 없음")); user.updateNickname(nickname); return userRepository.save(user); diff --git a/backend/src/main/java/io/f1/backend/domain/user/app/handler/CustomAuthenticationEntryPoint.java b/backend/src/main/java/io/f1/backend/domain/user/app/handler/CustomAuthenticationEntryPoint.java index a659852f..ebc9ffb1 100644 --- a/backend/src/main/java/io/f1/backend/domain/user/app/handler/CustomAuthenticationEntryPoint.java +++ b/backend/src/main/java/io/f1/backend/domain/user/app/handler/CustomAuthenticationEntryPoint.java @@ -2,19 +2,24 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import java.io.IOException; + import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.stereotype.Component; +import java.io.IOException; + @Component public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint { @Override - public void commence(HttpServletRequest request, HttpServletResponse response, - AuthenticationException authException) throws IOException { + public void commence( + HttpServletRequest request, + HttpServletResponse response, + AuthenticationException authException) + throws IOException { response.setContentType("application/json;charset=UTF-8"); response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); // 401 response.getWriter().write("{\"error\": \"Unauthorized\"}"); } -} \ No newline at end of file +} diff --git a/backend/src/main/java/io/f1/backend/domain/user/app/handler/OAuthLogoutSuccessHandler.java b/backend/src/main/java/io/f1/backend/domain/user/app/handler/OAuthLogoutSuccessHandler.java index 4bfe57f6..8f0528b6 100644 --- a/backend/src/main/java/io/f1/backend/domain/user/app/handler/OAuthLogoutSuccessHandler.java +++ b/backend/src/main/java/io/f1/backend/domain/user/app/handler/OAuthLogoutSuccessHandler.java @@ -2,6 +2,7 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; + import org.springframework.security.core.Authentication; import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; import org.springframework.stereotype.Component; @@ -10,8 +11,10 @@ public class OAuthLogoutSuccessHandler implements LogoutSuccessHandler { @Override - public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, - Authentication authentication) { + public void onLogoutSuccess( + HttpServletRequest request, + HttpServletResponse response, + Authentication authentication) { response.setStatus(HttpServletResponse.SC_NO_CONTENT); // 204 } } diff --git a/backend/src/main/java/io/f1/backend/domain/user/app/handler/OAuthSuccessHandler.java b/backend/src/main/java/io/f1/backend/domain/user/app/handler/OAuthSuccessHandler.java index f6387914..a465c885 100644 --- a/backend/src/main/java/io/f1/backend/domain/user/app/handler/OAuthSuccessHandler.java +++ b/backend/src/main/java/io/f1/backend/domain/user/app/handler/OAuthSuccessHandler.java @@ -1,17 +1,21 @@ package io.f1.backend.domain.user.app.handler; import com.fasterxml.jackson.databind.ObjectMapper; + import io.f1.backend.domain.user.dto.UserPrincipal; + import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import jakarta.servlet.http.HttpSession; -import java.io.IOException; -import java.util.Map; + import lombok.RequiredArgsConstructor; + import org.springframework.security.core.Authentication; import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; import org.springframework.stereotype.Component; +import java.io.IOException; +import java.util.Map; + @Component @RequiredArgsConstructor public class OAuthSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { @@ -19,23 +23,20 @@ public class OAuthSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { private final ObjectMapper objectMapper; @Override - public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, - Authentication authentication) throws IOException { + public void onAuthenticationSuccess( + HttpServletRequest request, HttpServletResponse response, Authentication authentication) + throws IOException { UserPrincipal principal = (UserPrincipal) authentication.getPrincipal(); response.setContentType("application/json;charset=UTF-8"); if (principal.getUserNickname() == null) { // 닉네임 설정 필요 → 202 Accepted response.setStatus(HttpServletResponse.SC_ACCEPTED); - objectMapper.writeValue(response.getWriter(), Map.of( - "message", "닉네임을 설정하세요." - )); + objectMapper.writeValue(response.getWriter(), Map.of("message", "닉네임을 설정하세요.")); } else { // 정상 로그인 → 200 OK response.setStatus(HttpServletResponse.SC_OK); - objectMapper.writeValue(response.getWriter(), Map.of( - "message", "로그인 성공" - )); + objectMapper.writeValue(response.getWriter(), Map.of("message", "로그인 성공")); } } } diff --git a/backend/src/main/java/io/f1/backend/domain/user/dao/UserRepository.java b/backend/src/main/java/io/f1/backend/domain/user/dao/UserRepository.java index 2a235544..d7420e59 100644 --- a/backend/src/main/java/io/f1/backend/domain/user/dao/UserRepository.java +++ b/backend/src/main/java/io/f1/backend/domain/user/dao/UserRepository.java @@ -1,10 +1,12 @@ package io.f1.backend.domain.user.dao; import io.f1.backend.domain.user.entity.User; -import java.util.Optional; + import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; +import java.util.Optional; + @Repository public interface UserRepository extends JpaRepository { diff --git a/backend/src/main/java/io/f1/backend/domain/user/dto/SessionUser.java b/backend/src/main/java/io/f1/backend/domain/user/dto/SessionUser.java index cc6d59a7..80aeb4ed 100644 --- a/backend/src/main/java/io/f1/backend/domain/user/dto/SessionUser.java +++ b/backend/src/main/java/io/f1/backend/domain/user/dto/SessionUser.java @@ -1,9 +1,11 @@ package io.f1.backend.domain.user.dto; import io.f1.backend.domain.user.entity.User; + +import lombok.Getter; + import java.io.Serializable; import java.time.LocalDateTime; -import lombok.Getter; @Getter public class SessionUser implements Serializable { @@ -19,4 +21,4 @@ public SessionUser(User user) { this.providerId = user.getProviderId(); this.lastLogin = user.getLastLogin(); } -} \ No newline at end of file +} diff --git a/backend/src/main/java/io/f1/backend/domain/user/dto/SignupRequestDto.java b/backend/src/main/java/io/f1/backend/domain/user/dto/SignupRequestDto.java index 5ef1e5a5..efb34bc4 100644 --- a/backend/src/main/java/io/f1/backend/domain/user/dto/SignupRequestDto.java +++ b/backend/src/main/java/io/f1/backend/domain/user/dto/SignupRequestDto.java @@ -2,6 +2,4 @@ import jakarta.validation.constraints.NotBlank; -public record SignupRequestDto(@NotBlank(message = "닉네임을 입력하세요") String nickname) { - -} +public record SignupRequestDto(@NotBlank(message = "닉네임을 입력하세요") String nickname) {} diff --git a/backend/src/main/java/io/f1/backend/domain/user/dto/SignupResponseDto.java b/backend/src/main/java/io/f1/backend/domain/user/dto/SignupResponseDto.java index 9c591470..f0593a3a 100644 --- a/backend/src/main/java/io/f1/backend/domain/user/dto/SignupResponseDto.java +++ b/backend/src/main/java/io/f1/backend/domain/user/dto/SignupResponseDto.java @@ -1,6 +1,7 @@ package io.f1.backend.domain.user.dto; import io.f1.backend.domain.user.entity.User; + import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; @@ -16,9 +17,6 @@ public class SignupResponseDto { private String nickname; public static SignupResponseDto toDto(User user) { - return SignupResponseDto.builder() - .id(user.getId()) - .nickname(user.getNickname()) - .build(); + return SignupResponseDto.builder().id(user.getId()).nickname(user.getNickname()).build(); } } diff --git a/backend/src/main/java/io/f1/backend/domain/user/dto/UserPrincipal.java b/backend/src/main/java/io/f1/backend/domain/user/dto/UserPrincipal.java index 10719c81..ef82fcf8 100644 --- a/backend/src/main/java/io/f1/backend/domain/user/dto/UserPrincipal.java +++ b/backend/src/main/java/io/f1/backend/domain/user/dto/UserPrincipal.java @@ -1,14 +1,17 @@ package io.f1.backend.domain.user.dto; import io.f1.backend.domain.user.entity.User; -import java.util.Collection; -import java.util.Collections; -import java.util.Map; + import lombok.Getter; + import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.oauth2.core.user.OAuth2User; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; + @Getter public class UserPrincipal implements UserDetails, OAuth2User { diff --git a/backend/src/main/java/io/f1/backend/domain/user/entity/User.java b/backend/src/main/java/io/f1/backend/domain/user/entity/User.java index 6f9f7df6..4402e4e0 100644 --- a/backend/src/main/java/io/f1/backend/domain/user/entity/User.java +++ b/backend/src/main/java/io/f1/backend/domain/user/entity/User.java @@ -12,7 +12,6 @@ import jakarta.persistence.OneToOne; import jakarta.persistence.Table; -import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; diff --git a/backend/src/main/java/io/f1/backend/global/config/SecurityConfig.java b/backend/src/main/java/io/f1/backend/global/config/SecurityConfig.java index 49255ae1..57c98ac4 100644 --- a/backend/src/main/java/io/f1/backend/global/config/SecurityConfig.java +++ b/backend/src/main/java/io/f1/backend/global/config/SecurityConfig.java @@ -1,10 +1,12 @@ package io.f1.backend.global.config; -import io.f1.backend.domain.user.app.handler.CustomAuthenticationEntryPoint; import io.f1.backend.domain.user.app.CustomOAuthUserService; +import io.f1.backend.domain.user.app.handler.CustomAuthenticationEntryPoint; import io.f1.backend.domain.user.app.handler.OAuthLogoutSuccessHandler; import io.f1.backend.domain.user.app.handler.OAuthSuccessHandler; + import lombok.RequiredArgsConstructor; + import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; @@ -24,31 +26,39 @@ public class SecurityConfig { @Bean public SecurityFilterChain userFilterChain(HttpSecurity http) throws Exception { - http - .csrf(AbstractHttpConfigurer::disable) - .exceptionHandling(exception -> exception - .authenticationEntryPoint(customAuthenticationEntryPoint) - ) - .authorizeHttpRequests(auth -> auth - .requestMatchers("/", "/login", "/oauth2/**", "/signup", "/css/**", "/js/**") - .permitAll() - .requestMatchers("/ws/**").authenticated() - .anyRequest().authenticated() - ) - .formLogin(AbstractHttpConfigurer::disable) - .oauth2Login(oauth2 -> oauth2 - .userInfoEndpoint(userInfo -> userInfo - .userService(customOAuthUserService) - ) - .successHandler(oAuthSuccessHandler) - ) - .logout(logout -> logout - .logoutUrl("/logout") - .logoutSuccessHandler(oAuthLogoutSuccessHandler) - .clearAuthentication(true) - .invalidateHttpSession(true) - .permitAll() - ); + http.csrf(AbstractHttpConfigurer::disable) + .exceptionHandling( + exception -> + exception.authenticationEntryPoint(customAuthenticationEntryPoint)) + .authorizeHttpRequests( + auth -> + auth.requestMatchers( + "/", + "/login", + "/oauth2/**", + "/signup", + "/css/**", + "/js/**") + .permitAll() + .requestMatchers("/ws/**") + .authenticated() + .anyRequest() + .authenticated()) + .formLogin(AbstractHttpConfigurer::disable) + .oauth2Login( + oauth2 -> + oauth2.userInfoEndpoint( + userInfo -> + userInfo.userService( + customOAuthUserService)) + .successHandler(oAuthSuccessHandler)) + .logout( + logout -> + logout.logoutUrl("/logout") + .logoutSuccessHandler(oAuthLogoutSuccessHandler) + .clearAuthentication(true) + .invalidateHttpSession(true) + .permitAll()); return http.build(); } -} \ No newline at end of file +} diff --git a/backend/src/main/java/io/f1/backend/global/util/SecurityUtils.java b/backend/src/main/java/io/f1/backend/global/util/SecurityUtils.java index c284158a..cd00c053 100644 --- a/backend/src/main/java/io/f1/backend/global/util/SecurityUtils.java +++ b/backend/src/main/java/io/f1/backend/global/util/SecurityUtils.java @@ -2,21 +2,21 @@ import io.f1.backend.domain.user.dto.UserPrincipal; import io.f1.backend.domain.user.entity.User; -import java.util.Collections; + import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; -public class SecurityUtils { +import java.util.Collections; - private SecurityUtils() { +public class SecurityUtils { - } + private SecurityUtils() {} public static void setAuthentication(User user) { UserPrincipal userPrincipal = new UserPrincipal(user, Collections.emptyMap()); UsernamePasswordAuthenticationToken authentication = - new UsernamePasswordAuthenticationToken(userPrincipal, null, - userPrincipal.getAuthorities()); + new UsernamePasswordAuthenticationToken( + userPrincipal, null, userPrincipal.getAuthorities()); SecurityContextHolder.getContext().setAuthentication(authentication); } } From d30b34027d16c806117a0f76e992386a069b8af4 Mon Sep 17 00:00:00 2001 From: github-actions <> Date: Mon, 14 Jul 2025 03:07:45 +0000 Subject: [PATCH 4/4] =?UTF-8?q?chore:=20Java=20=EC=8A=A4=ED=83=80=EC=9D=BC?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/f1/backend/domain/user/dao/UserRepository.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/backend/src/main/java/io/f1/backend/domain/user/dao/UserRepository.java b/backend/src/main/java/io/f1/backend/domain/user/dao/UserRepository.java index f4e65dd3..d7420e59 100644 --- a/backend/src/main/java/io/f1/backend/domain/user/dao/UserRepository.java +++ b/backend/src/main/java/io/f1/backend/domain/user/dao/UserRepository.java @@ -3,7 +3,6 @@ import io.f1.backend.domain.user.entity.User; import org.springframework.data.jpa.repository.JpaRepository; - import org.springframework.stereotype.Repository; import java.util.Optional; @@ -14,4 +13,4 @@ public interface UserRepository extends JpaRepository { Optional findByProviderAndProviderId(String provider, String providerId); Boolean existsUserByNickname(String nickname); -} \ No newline at end of file +}