diff --git a/build.gradle b/build.gradle index 24fbb4a..469c692 100644 --- a/build.gradle +++ b/build.gradle @@ -31,6 +31,12 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE' + implementation 'com.google.api-client:google-api-client:2.4.0' + implementation 'com.auth0:java-jwt:4.4.0' + implementation group: 'com.google.auth', name: 'google-auth-library-oauth2-http', version: '1.23.0' + implementation group: 'com.google.http-client', name: 'google-http-client-jackson2', version: '1.44.2' + implementation group: 'org.springframework.security', name: 'spring-security-core', version: '6.3.1' + implementation 'org.springframework.boot:spring-boot-starter-security' // Hibernate Core implementation 'org.hibernate:hibernate-core:6.5.2.Final' compileOnly 'org.projectlombok:lombok' diff --git a/src/main/java/com/aisip/OnO/backend/Auth/GoogleTokenVerifier.java b/src/main/java/com/aisip/OnO/backend/Auth/GoogleTokenVerifier.java new file mode 100644 index 0000000..36b7fce --- /dev/null +++ b/src/main/java/com/aisip/OnO/backend/Auth/GoogleTokenVerifier.java @@ -0,0 +1,33 @@ +package com.aisip.OnO.backend.Auth; + +import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken; +import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier; +import com.google.api.client.http.javanet.NetHttpTransport; +import com.google.api.client.json.jackson2.JacksonFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.util.Collections; + +@Component +public class GoogleTokenVerifier { + + @Value("${spring.security.oauth2.client.registration.google.client-id}") + private String clientId; // static 제거 + + public GoogleIdToken.Payload verifyToken(String idTokenString) throws GeneralSecurityException, IOException { + + GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(new NetHttpTransport(), new JacksonFactory()) + .setAudience(Collections.singletonList(clientId)) + .build(); + + GoogleIdToken idToken = verifier.verify(idTokenString); + if (idToken != null) { + return idToken.getPayload(); + } else { + throw new GeneralSecurityException("Invalid ID token."); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/aisip/OnO/backend/Auth/JwtTokenFilter.java b/src/main/java/com/aisip/OnO/backend/Auth/JwtTokenFilter.java new file mode 100644 index 0000000..9af1fff --- /dev/null +++ b/src/main/java/com/aisip/OnO/backend/Auth/JwtTokenFilter.java @@ -0,0 +1,53 @@ +package com.aisip.OnO.backend.Auth; + +import com.auth0.jwt.JWT; +import com.auth0.jwt.JWTVerifier; +import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.exceptions.JWTVerificationException; +import com.auth0.jwt.interfaces.DecodedJWT; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; +import java.util.Collections; + +public class JwtTokenFilter extends OncePerRequestFilter { + + private final String secret; + + public JwtTokenFilter(String secret) { + this.secret = secret; + } + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + String authHeader = request.getHeader("Authorization"); + if (authHeader != null && authHeader.startsWith("Bearer ")) { + String token = authHeader.substring(7); + try { + Algorithm algorithm = Algorithm.HMAC512(secret); + JWTVerifier verifier = JWT.require(algorithm).build(); + DecodedJWT decodedJWT = verifier.verify(token); + + String username = decodedJWT.getSubject(); + Long userId = decodedJWT.getClaim("userId").asLong(); + + UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userId, null, Collections.emptyList()); + authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + + SecurityContextHolder.getContext().setAuthentication(authentication); + } catch (JWTVerificationException e) { + // JWT Verification failed + SecurityContextHolder.clearContext(); + } + } + + filterChain.doFilter(request, response); + } +} \ No newline at end of file diff --git a/src/main/java/com/aisip/OnO/backend/Auth/JwtTokenProvider.java b/src/main/java/com/aisip/OnO/backend/Auth/JwtTokenProvider.java new file mode 100644 index 0000000..362adde --- /dev/null +++ b/src/main/java/com/aisip/OnO/backend/Auth/JwtTokenProvider.java @@ -0,0 +1,35 @@ +package com.aisip.OnO.backend.Auth; + +import com.auth0.jwt.JWT; +import com.auth0.jwt.algorithms.Algorithm; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.util.Date; + +@Component +public class JwtTokenProvider { + + @Value("${spring.jwt.secret}") + private String secret; + + @Value("${spring.jwt.expiration}") + private long expirationTime; + + public String createToken(Long userId, String email) { + return JWT.create() + .withSubject(email) + .withClaim("userId", userId) + .withIssuedAt(new Date()) + .withExpiresAt(new Date(System.currentTimeMillis() + expirationTime)) + .sign(Algorithm.HMAC512(secret.getBytes())); + } + + public Long getUserIdFromToken(String token) { + return JWT.decode(token).getClaim("userId").asLong(); + } + + public String getEmailFromToken(String token) { + return JWT.decode(token).getSubject(); + } +} \ No newline at end of file diff --git a/src/main/java/com/aisip/OnO/backend/Auth/SecurityConfig.java b/src/main/java/com/aisip/OnO/backend/Auth/SecurityConfig.java new file mode 100644 index 0000000..1d99875 --- /dev/null +++ b/src/main/java/com/aisip/OnO/backend/Auth/SecurityConfig.java @@ -0,0 +1,39 @@ +package com.aisip.OnO.backend.Auth; + +import org.springframework.beans.factory.annotation.Value; +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.http.SessionCreationPolicy; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +@Configuration +@EnableWebSecurity +public class SecurityConfig { + + @Value("${spring.jwt.secret}") + private String secret; + + @Bean + public JwtTokenFilter jwtTokenFilter() { + return new JwtTokenFilter(secret); + } + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http.csrf().disable() + .authorizeHttpRequests(authorizeRequests -> + authorizeRequests + .requestMatchers("/api/auth/**").permitAll() + .anyRequest().authenticated() + ) + .sessionManagement(sessionManagement -> + sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS) + ) + .addFilterBefore(jwtTokenFilter(), UsernamePasswordAuthenticationFilter.class); + + return http.build(); + } +} diff --git a/src/main/java/com/aisip/OnO/backend/BackendApplication.java b/src/main/java/com/aisip/OnO/backend/BackendApplication.java index ff892f8..7cb7824 100644 --- a/src/main/java/com/aisip/OnO/backend/BackendApplication.java +++ b/src/main/java/com/aisip/OnO/backend/BackendApplication.java @@ -2,8 +2,10 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; @SpringBootApplication +@EnableJpaAuditing public class BackendApplication { public static void main(String[] args) { diff --git a/src/main/java/com/aisip/OnO/backend/Dto/User/UserRegisterDto.java b/src/main/java/com/aisip/OnO/backend/Dto/User/UserRegisterDto.java deleted file mode 100644 index 58bd183..0000000 --- a/src/main/java/com/aisip/OnO/backend/Dto/User/UserRegisterDto.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.aisip.OnO.backend.Dto.User; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Data -@NoArgsConstructor -@AllArgsConstructor -public class UserRegisterDto { - - private String googleId; - private String appleId; - private String email; - private String userName; - private String socialLoginType; - - // 소셜 ID를 반환하는 메서드 - public String getSocialId() { - if ("GOOGLE".equalsIgnoreCase(socialLoginType)) { - return googleId; - } else if ("APPLE".equalsIgnoreCase(socialLoginType)) { - return appleId; - } - return null; - } -} \ No newline at end of file diff --git a/src/main/java/com/aisip/OnO/backend/controller/AuthController.java b/src/main/java/com/aisip/OnO/backend/controller/AuthController.java new file mode 100644 index 0000000..4169770 --- /dev/null +++ b/src/main/java/com/aisip/OnO/backend/controller/AuthController.java @@ -0,0 +1,95 @@ +package com.aisip.OnO.backend.controller; + +import com.aisip.OnO.backend.service.AuthService; +import com.aisip.OnO.backend.Auth.GoogleTokenVerifier; +import com.aisip.OnO.backend.Auth.JwtTokenProvider; +import com.aisip.OnO.backend.entity.User; +import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken; +import com.google.common.io.BaseEncoding; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.security.GeneralSecurityException; + +@RestController +@RequestMapping("/api/auth") +public class AuthController { + + @Autowired + private GoogleTokenVerifier googleTokenVerifier; + + @Autowired + private AuthService authService; + + @Autowired + private JwtTokenProvider jwtTokenProvider; + + @PostMapping("/google") + public ResponseEntity googleLogin(@RequestBody TokenRequest tokenRequest) { + try { + GoogleIdToken.Payload payload = googleTokenVerifier.verifyToken(tokenRequest.getIdToken()); + User user = authService.registerOrLoginUser(payload.getEmail(), (String) payload.get("name")); + String token = jwtTokenProvider.createToken(user.getUserId(), user.getEmail()); + return ResponseEntity.ok(new AuthResponse(token)); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ErrorResponse("Invalid ID token format")); + } catch (BaseEncoding.DecodingException e) { + e.printStackTrace(); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ErrorResponse("ID token decoding error")); + } catch (GeneralSecurityException | IOException e) { + e.printStackTrace(); + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(new ErrorResponse("Invalid Google token")); + } catch (Exception e) { + e.printStackTrace(); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new ErrorResponse("Internal server error")); + } + } + + public static class TokenRequest { + private String idToken; + + public String getIdToken() { + return idToken; + } + + public void setIdToken(String idToken) { + this.idToken = idToken; + } + } + + public static class AuthResponse { + private String token; + + public AuthResponse(String token) { + this.token = token; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + } + + public static class ErrorResponse { + private String error; + + public ErrorResponse(String error) { + this.error = error; + } + + public String getError() { + return error; + } + + public void setError(String error) { + this.error = error; + } + } +} diff --git a/src/main/java/com/aisip/OnO/backend/controller/ProblemController.java b/src/main/java/com/aisip/OnO/backend/controller/ProblemController.java index d182069..074e8d3 100644 --- a/src/main/java/com/aisip/OnO/backend/controller/ProblemController.java +++ b/src/main/java/com/aisip/OnO/backend/controller/ProblemController.java @@ -6,9 +6,9 @@ import com.aisip.OnO.backend.service.ProblemService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.apache.coyote.Response; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.*; import java.util.List; @@ -23,10 +23,11 @@ public class ProblemController { @GetMapping("/problem/{problemId}") public ResponseEntity getProblemByUserId( - @RequestHeader("userId") Long userId, + Authentication authentication, @PathVariable("problemId") Long problemId ) { try { + Long userId = (Long) authentication.getPrincipal(); ProblemResponseDto problemResponseDto = problemService.findProblemByUserId(userId, problemId); return ResponseEntity.ok(problemResponseDto); } catch (ProblemNotFoundException e) { @@ -35,18 +36,19 @@ public ResponseEntity getProblemByUserId( } @GetMapping("/problems") - public ResponseEntity getProblemsByUserId(@RequestHeader("userId") Long userId){ + public ResponseEntity getProblemsByUserId(Authentication authentication) { + Long userId = (Long) authentication.getPrincipal(); List problems = problemService.findAllProblemsByUserId(userId); return ResponseEntity.ok(problems); } @PostMapping("/problem") public ResponseEntity registerProblem( - @RequestHeader("userId") Long userId, + Authentication authentication, @ModelAttribute ProblemRegisterDto problemRegisterDto ) { try { - System.out.println(problemRegisterDto.toString()); + Long userId = (Long) authentication.getPrincipal(); boolean isSaved = problemService.saveProblem(userId, problemRegisterDto); if(isSaved){ @@ -62,28 +64,13 @@ public ResponseEntity registerProblem( } } - /* - @PutMapping("/problem/{problemId}") - public ResponseEntity updateProblem( - @RequestHeader("userId") Long userId, - @PathVariable("problemId") Long problemId, - @RequestBody ProblemRegisterDto problemRegisterDto - ) { - try { - ProblemResponseDto updatedProblem = problemService.updateProblem(userId, problemId, problemRegisterDto); - return ResponseEntity.ok(updatedProblem); - } catch (ProblemNotFoundException e) { - return ResponseEntity.status(HttpStatus.NOT_FOUND).body(e.getMessage()); - } - } - */ - @DeleteMapping("/problem") public ResponseEntity deleteProblem( - @RequestHeader("userId") Long userId, + Authentication authentication, @RequestHeader("problemId") Long problemId ) { try { + Long userId = (Long) authentication.getPrincipal(); problemService.deleteProblem(userId, problemId); return ResponseEntity.ok("삭제를 완료했습니다."); } catch (ProblemNotFoundException e) { diff --git a/src/main/java/com/aisip/OnO/backend/controller/UserController.java b/src/main/java/com/aisip/OnO/backend/controller/UserController.java index 83dcbc7..dafcb5c 100644 --- a/src/main/java/com/aisip/OnO/backend/controller/UserController.java +++ b/src/main/java/com/aisip/OnO/backend/controller/UserController.java @@ -1,42 +1,45 @@ package com.aisip.OnO.backend.controller; -import com.aisip.OnO.backend.Dto.User.UserRegisterDto; -import com.aisip.OnO.backend.Dto.User.UserResponseDto; -import com.aisip.OnO.backend.exception.UserNotFoundException; -import com.aisip.OnO.backend.service.UserService; -import lombok.RequiredArgsConstructor; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; +import com.aisip.OnO.backend.service.AuthService; +import com.aisip.OnO.backend.entity.User; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; +import org.springframework.security.core.Authentication; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; -@RequiredArgsConstructor @RestController @RequestMapping("/api/user") public class UserController { - private final UserService userService; + @Autowired + private AuthService authService; - @GetMapping("/{userId}") - public ResponseEntity getUserById(@PathVariable Long userId) { - UserResponseDto userResponseDto = userService.getUserByUserId(userId); - return ResponseEntity.ok().body(userResponseDto); + @GetMapping("/info") + public ResponseEntity getUserInfo(Authentication authentication) { + Long userId = (Long) authentication.getPrincipal(); + User user = authService.getUserById(userId); + if (user != null) { + return ResponseEntity.ok(user); + } else { + return ResponseEntity.status(404).body(new ErrorResponse("User not found")); + } } - @PostMapping("") - public ResponseEntity saveUser(@RequestBody UserRegisterDto userRegisterDto) { - UserResponseDto userResponseDto = userService.saveUser(userRegisterDto); - return ResponseEntity.ok().body(userResponseDto); - } + public static class ErrorResponse { + private String error; + + public ErrorResponse(String error) { + this.error = error; + } -// @GetMapping("/autoLogin/{googleId}") -// public ResponseEntity autoLogin(@PathVariable String googleId) { -// try { -// UserResponseDto userResponseDto = userService.getUserByGoogleId(googleId); -// return ResponseEntity.ok().body(userResponseDto); -// } catch (UserNotFoundException e) { -// return ResponseEntity.status(HttpStatus.NOT_FOUND).body(e.getMessage()); -// } -// } -} \ No newline at end of file + public String getError() { + return error; + } + + public void setError(String error) { + this.error = error; + } + } +} diff --git a/src/main/java/com/aisip/OnO/backend/converter/UserConverter.java b/src/main/java/com/aisip/OnO/backend/converter/UserConverter.java deleted file mode 100644 index d5251aa..0000000 --- a/src/main/java/com/aisip/OnO/backend/converter/UserConverter.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.aisip.OnO.backend.converter; - -import com.aisip.OnO.backend.Dto.User.UserResponseDto; -import com.aisip.OnO.backend.entity.SocialLogin.SocialLogin; -import com.aisip.OnO.backend.entity.SocialLogin.SocialLoginType; -import com.aisip.OnO.backend.entity.User; - -public class UserConverter { - - public static UserResponseDto convertToResponseDto(User user, SocialLogin socialLogin) { - if(user == null){ - return null; - } - - UserResponseDto dto = new UserResponseDto(); - dto.setUserId(user.getId()); - dto.setUserName(user.getUserName()); - dto.setUserEmail(user.getEmail()); - dto.setSocialId(socialLogin.getSocialId()); - dto.setSocialLoginType(socialLogin.getSocialLoginType().name()); - - return dto; - } -} diff --git a/src/main/java/com/aisip/OnO/backend/entity/BaseEntity.java b/src/main/java/com/aisip/OnO/backend/entity/BaseEntity.java new file mode 100644 index 0000000..45f600e --- /dev/null +++ b/src/main/java/com/aisip/OnO/backend/entity/BaseEntity.java @@ -0,0 +1,24 @@ +package com.aisip.OnO.backend.entity; + +import jakarta.persistence.EntityListeners; +import jakarta.persistence.MappedSuperclass; +import lombok.Getter; +import lombok.Setter; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import java.time.LocalDate; +import java.time.LocalDateTime; + +@MappedSuperclass +@Getter @Setter +@EntityListeners(AuditingEntityListener.class) +public class BaseEntity { + + @CreatedDate + private LocalDateTime createdAt; + + @LastModifiedDate + private LocalDateTime updatedAt; +} diff --git a/src/main/java/com/aisip/OnO/backend/entity/Problem.java b/src/main/java/com/aisip/OnO/backend/entity/Problem.java index bc261f8..78732c4 100644 --- a/src/main/java/com/aisip/OnO/backend/entity/Problem.java +++ b/src/main/java/com/aisip/OnO/backend/entity/Problem.java @@ -14,7 +14,7 @@ @NoArgsConstructor @AllArgsConstructor @Builder -public class Problem { +public class Problem extends BaseEntity{ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -30,10 +30,6 @@ public class Problem { private LocalDateTime solvedAt; - private LocalDateTime createdAt; - - private LocalDateTime updateAt; - @OneToMany(mappedBy = "problem", cascade = CascadeType.ALL, fetch = FetchType.LAZY) private List images; } diff --git a/src/main/java/com/aisip/OnO/backend/entity/SocialLogin/SocialLogin.java b/src/main/java/com/aisip/OnO/backend/entity/SocialLogin/SocialLogin.java deleted file mode 100644 index a8e379f..0000000 --- a/src/main/java/com/aisip/OnO/backend/entity/SocialLogin/SocialLogin.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.aisip.OnO.backend.entity.SocialLogin; - -import com.aisip.OnO.backend.entity.User; -import jakarta.persistence.*; -import lombok.*; - -import java.time.LocalDate; - -@Entity -@Getter -@Setter -@NoArgsConstructor -@AllArgsConstructor -@Builder -@Table(name = "social_login") -public class SocialLogin { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - private String socialId; // 소셜 서비스에서 제공하는 고유 ID - - @Enumerated(EnumType.STRING) - private SocialLoginType socialLoginType; // Enum 타입의 소셜 서비스 제공자 - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "user_id") - private User user; // 연결된 사용자 엔티티 - - private LocalDate linkedDate; // 계정 연결 날짜 -} diff --git a/src/main/java/com/aisip/OnO/backend/entity/SocialLogin/SocialLoginType.java b/src/main/java/com/aisip/OnO/backend/entity/SocialLogin/SocialLoginType.java deleted file mode 100644 index 99d73dc..0000000 --- a/src/main/java/com/aisip/OnO/backend/entity/SocialLogin/SocialLoginType.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.aisip.OnO.backend.entity.SocialLogin; - -public enum SocialLoginType { - GOOGLE, - APPLE -} diff --git a/src/main/java/com/aisip/OnO/backend/entity/User.java b/src/main/java/com/aisip/OnO/backend/entity/User.java index 2c2b19a..e991aa9 100644 --- a/src/main/java/com/aisip/OnO/backend/entity/User.java +++ b/src/main/java/com/aisip/OnO/backend/entity/User.java @@ -1,36 +1,20 @@ package com.aisip.OnO.backend.entity; -import com.aisip.OnO.backend.entity.SocialLogin.SocialLogin; -import jakarta.persistence.*; -import lombok.*; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import lombok.Getter; +import lombok.Setter; -import java.time.LocalDate; -import java.util.List; - -@Entity @Getter @Setter -@Table(name = "user") -@NoArgsConstructor -@AllArgsConstructor -@Builder +@Entity public class User { - @Id @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - + private Long userId; private String email; - - private String userName; - - private LocalDate createdAt; - - private LocalDate updateAt; - - @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY) - private List socialLogins; - - @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY) - private List problems; + private String name; } + diff --git a/src/main/java/com/aisip/OnO/backend/repository/ProblemRepository.java b/src/main/java/com/aisip/OnO/backend/repository/ProblemRepository.java index 960ea25..ff60c75 100644 --- a/src/main/java/com/aisip/OnO/backend/repository/ProblemRepository.java +++ b/src/main/java/com/aisip/OnO/backend/repository/ProblemRepository.java @@ -1,7 +1,7 @@ package com.aisip.OnO.backend.repository; -import com.aisip.OnO.backend.entity.Problem; import com.aisip.OnO.backend.entity.User; +import com.aisip.OnO.backend.entity.Problem; import org.springframework.data.jpa.repository.JpaRepository; import java.util.List; diff --git a/src/main/java/com/aisip/OnO/backend/repository/SocialLoginRepository.java b/src/main/java/com/aisip/OnO/backend/repository/SocialLoginRepository.java deleted file mode 100644 index 88e835a..0000000 --- a/src/main/java/com/aisip/OnO/backend/repository/SocialLoginRepository.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.aisip.OnO.backend.repository; - -import com.aisip.OnO.backend.entity.SocialLogin.SocialLogin; -import com.aisip.OnO.backend.entity.User; -import org.springframework.data.jpa.repository.JpaRepository; - -import java.util.Optional; - -public interface SocialLoginRepository extends JpaRepository { - Optional findBySocialId(String socialId); - - Optional findByUser(User user); -} diff --git a/src/main/java/com/aisip/OnO/backend/repository/UserRepository.java b/src/main/java/com/aisip/OnO/backend/repository/UserRepository.java index 962c931..511f2e5 100644 --- a/src/main/java/com/aisip/OnO/backend/repository/UserRepository.java +++ b/src/main/java/com/aisip/OnO/backend/repository/UserRepository.java @@ -3,8 +3,6 @@ import com.aisip.OnO.backend.entity.User; import org.springframework.data.jpa.repository.JpaRepository; -import java.util.Optional; - public interface UserRepository extends JpaRepository { + User findByEmail(String email); } - diff --git a/src/main/java/com/aisip/OnO/backend/service/AuthService.java b/src/main/java/com/aisip/OnO/backend/service/AuthService.java new file mode 100644 index 0000000..46dbe83 --- /dev/null +++ b/src/main/java/com/aisip/OnO/backend/service/AuthService.java @@ -0,0 +1,28 @@ +package com.aisip.OnO.backend.service; + +import com.aisip.OnO.backend.repository.UserRepository; +import com.aisip.OnO.backend.entity.User; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class AuthService { + + @Autowired + private UserRepository userRepository; + + public User registerOrLoginUser(String email, String name) { + User user = userRepository.findByEmail(email); + if (user == null) { + user = new User(); + user.setEmail(email); + user.setName(name); + userRepository.save(user); + } + return user; + } + + public User getUserById(Long userId) { + return userRepository.findById(userId).orElse(null); + } +} diff --git a/src/main/java/com/aisip/OnO/backend/service/FileUploadService.java b/src/main/java/com/aisip/OnO/backend/service/FileUploadService.java index 62528e8..1e0f0db 100644 --- a/src/main/java/com/aisip/OnO/backend/service/FileUploadService.java +++ b/src/main/java/com/aisip/OnO/backend/service/FileUploadService.java @@ -14,6 +14,8 @@ public interface FileUploadService { String uploadFileToS3(MultipartFile file) throws IOException; ImageData saveImageData(String imageUrl, Problem problem, ImageType imageType); + String getProcessImageUrlFromProblemImageUrl(String problemImageUrl); + List getProblemImages(Long problemId); void deleteImage(String fileUrl); diff --git a/src/main/java/com/aisip/OnO/backend/service/FileUploadServiceImpl.java b/src/main/java/com/aisip/OnO/backend/service/FileUploadServiceImpl.java index 2514507..9563d1e 100644 --- a/src/main/java/com/aisip/OnO/backend/service/FileUploadServiceImpl.java +++ b/src/main/java/com/aisip/OnO/backend/service/FileUploadServiceImpl.java @@ -11,7 +11,9 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.client.RestTemplate; import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.util.UriComponentsBuilder; import java.io.IOException; import java.util.List; @@ -26,6 +28,10 @@ public class FileUploadServiceImpl implements FileUploadService{ @Value("${cloud.aws.s3.bucket}") private String bucket; + @Value("${external.api.fastApiUrl}") + private String fastApiUrl; + + @Override public String uploadFileToS3(MultipartFile file) throws IOException { String fileName = System.currentTimeMillis() + "_" + file.getOriginalFilename(); String fileUrl = "https://" + bucket + ".s3.ap-northeast-2.amazonaws.com/" + fileName; @@ -39,6 +45,7 @@ public String uploadFileToS3(MultipartFile file) throws IOException { return fileUrl; } + @Override public ImageData saveImageData(String imageUrl, Problem problem, ImageType imageType){ ImageData imageData = ImageData.builder() @@ -50,6 +57,22 @@ public ImageData saveImageData(String imageUrl, Problem problem, ImageType image return imageDataRepository.save(imageData); } + @Override + public String getProcessImageUrlFromProblemImageUrl(String problemImageUrl) { + RestTemplate restTemplate = new RestTemplate(); + String url = fastApiUrl + "/show-url"; + + UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromHttpUrl(url) + .queryParam("full_url", problemImageUrl); + + // GET 요청 보내기 + String response = restTemplate.getForObject(uriBuilder.toUriString(), String.class); + + // 응답 로그 출력 + System.out.println("Response from server: " + response); + return response; + } + @Transactional(readOnly = true) public List getProblemImages(Long problemId) { return imageDataRepository.findByProblemId(problemId); diff --git a/src/main/java/com/aisip/OnO/backend/service/ProblemService.java b/src/main/java/com/aisip/OnO/backend/service/ProblemService.java index 89da793..4851077 100644 --- a/src/main/java/com/aisip/OnO/backend/service/ProblemService.java +++ b/src/main/java/com/aisip/OnO/backend/service/ProblemService.java @@ -12,7 +12,6 @@ public interface ProblemService { List findAllProblemsByUserId(Long userId); boolean saveProblem(Long userId, ProblemRegisterDto problemRegisterDto); - //ProblemResponseDto updateProblem(Long userId, Long problemId, ProblemRegisterDto problemRegisterDto); void deleteProblem(Long userId, Long problemId); diff --git a/src/main/java/com/aisip/OnO/backend/service/ProblemServiceImpl.java b/src/main/java/com/aisip/OnO/backend/service/ProblemServiceImpl.java index 7d90e6a..7b261d9 100644 --- a/src/main/java/com/aisip/OnO/backend/service/ProblemServiceImpl.java +++ b/src/main/java/com/aisip/OnO/backend/service/ProblemServiceImpl.java @@ -1,22 +1,21 @@ package com.aisip.OnO.backend.service; +import com.aisip.OnO.backend.entity.User; +import com.aisip.OnO.backend.repository.UserRepository; import com.aisip.OnO.backend.Dto.Problem.ProblemRegisterDto; import com.aisip.OnO.backend.Dto.Problem.ProblemResponseDto; import com.aisip.OnO.backend.converter.ProblemConverter; import com.aisip.OnO.backend.entity.Image.ImageData; import com.aisip.OnO.backend.entity.Image.ImageType; import com.aisip.OnO.backend.entity.Problem; -import com.aisip.OnO.backend.entity.User; import com.aisip.OnO.backend.exception.ProblemNotFoundException; import com.aisip.OnO.backend.exception.UserNotAuthorizedException; import com.aisip.OnO.backend.exception.UserNotFoundException; import com.aisip.OnO.backend.repository.ProblemRepository; -import com.aisip.OnO.backend.repository.UserRepository; import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; -import java.time.LocalDateTime; import java.util.Collections; import java.util.List; import java.util.Optional; @@ -29,7 +28,6 @@ public class ProblemServiceImpl implements ProblemService { private final UserRepository userRepository; private final ProblemRepository problemRepository; - private final FileUploadService fileUploadService; /**특정 문제 조회*/ @@ -39,7 +37,7 @@ public ProblemResponseDto findProblemByUserId(Long userId, Long problemId) { if (optionalProblem.isPresent()) { Problem problem = optionalProblem.get(); - if (problem.getUser().getId().equals(userId)) { + if (problem.getUser().getUserId().equals(userId)) { List images = fileUploadService.getProblemImages(problemId); return ProblemConverter.convertToResponseDto(problem, images); } else { @@ -67,19 +65,17 @@ public List findAllProblemsByUserId(Long userId) { /**문제 저장*/ @Override public boolean saveProblem(Long userId, ProblemRegisterDto problemRegisterDto) { - Optional optionalUser = userRepository.findById(userId); + Optional optionalUserEntity = userRepository.findById(userId); - try{ - if (optionalUser.isPresent()) { - User user = optionalUser.get(); + try { + if (optionalUserEntity.isPresent()) { + User user = optionalUserEntity.get(); Problem problem = Problem.builder() .user(user) .reference(problemRegisterDto.getReference()) .memo(problemRegisterDto.getMemo()) .solvedAt(problemRegisterDto.getSolvedAt()) - .createdAt(LocalDateTime.now()) - .updateAt(LocalDateTime.now()) .build(); Problem savedProblem = problemRepository.save(problem); @@ -93,46 +89,20 @@ public boolean saveProblem(Long userId, ProblemRegisterDto problemRegisterDto) { String solveImageUrl = fileUploadService.uploadFileToS3(problemRegisterDto.getSolveImage()); fileUploadService.saveImageData(solveImageUrl, savedProblem, ImageType.SOLVE_IMAGE); - String processImageUrl = ""; + if (problemImageUrl != null && !problemImageUrl.isEmpty()) { + String processImageUrl = fileUploadService.getProcessImageUrlFromProblemImageUrl(problemImageUrl); + fileUploadService.saveImageData("processImageUrl", savedProblem, ImageType.PROCESS_IMAGE); + } return true; } else { throw new UserNotFoundException("User not found with ID: " + userId); } - } catch (Exception e){ + } catch (Exception e) { e.printStackTrace(); return false; } - - } - - /**문제 업데이트*/ - /* - @Override - public ProblemResponseDto updateProblem(Long userId, Long problemId, ProblemRegisterDto problemRegisterDto) { - Optional optionalUser = userRepository.findById(userId); - Optional optionalProblem = problemRepository.findById(problemId); - - if (optionalUser.isPresent() && optionalProblem.isPresent()) { - User user = optionalUser.get(); - Problem problem = optionalProblem.get(); - - // Update fields from ProblemRegisterDto - problem.setImageUrl(problemRegisterDto.getImageUrl()); - problem.setAnswerImageUrl(problemRegisterDto.getAnswerImageUrl()); - problem.setSolveImageUrl(problemRegisterDto.getSolveImageUrl()); - problem.setMemo(problemRegisterDto.getMemo()); - problem.setReference(problemRegisterDto.getReference()); - problem.setSolvedAt(problemRegisterDto.getSolvedAt()); - problem.setUpdateAt(LocalDate.now()); - - Problem savedProblem = problemRepository.save(problem); - return ProblemConverter.convertToResponseDto(savedProblem); - } else { - throw new ProblemNotFoundException("Problem not found with ID: " + problemId); - } } - */ /**문제 삭제*/ @Override @@ -140,7 +110,7 @@ public void deleteProblem(Long userId, Long problemId) { Optional optionalProblem = problemRepository.findById(problemId); if (optionalProblem.isPresent()) { Problem problem = optionalProblem.get(); - if (problem.getUser().getId().equals(userId)) { + if (problem.getUser().getUserId().equals(userId)) { // 문제와 관련된 이미지 데이터를 조회 List images = fileUploadService.getProblemImages(problemId); // 각 이미지에 대해 S3에서 삭제 diff --git a/src/main/java/com/aisip/OnO/backend/service/UserService.java b/src/main/java/com/aisip/OnO/backend/service/UserService.java deleted file mode 100644 index b7a6c0e..0000000 --- a/src/main/java/com/aisip/OnO/backend/service/UserService.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.aisip.OnO.backend.service; - -import com.aisip.OnO.backend.Dto.User.UserRegisterDto; -import com.aisip.OnO.backend.Dto.User.UserResponseDto; -import com.aisip.OnO.backend.entity.User; - -public interface UserService { - - public UserResponseDto getUserByUserId(Long userId); - public UserResponseDto saveUser(UserRegisterDto userRegisterDto); -} - diff --git a/src/main/java/com/aisip/OnO/backend/service/UserServiceImpl.java b/src/main/java/com/aisip/OnO/backend/service/UserServiceImpl.java deleted file mode 100644 index 9fc9110..0000000 --- a/src/main/java/com/aisip/OnO/backend/service/UserServiceImpl.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.aisip.OnO.backend.service; - -import com.aisip.OnO.backend.Dto.User.UserRegisterDto; -import com.aisip.OnO.backend.Dto.User.UserResponseDto; -import com.aisip.OnO.backend.converter.UserConverter; -import com.aisip.OnO.backend.entity.SocialLogin.SocialLogin; -import com.aisip.OnO.backend.entity.SocialLogin.SocialLoginType; -import com.aisip.OnO.backend.entity.User; -import com.aisip.OnO.backend.exception.UserNotFoundException; -import com.aisip.OnO.backend.repository.SocialLoginRepository; -import com.aisip.OnO.backend.repository.UserRepository; -import jakarta.transaction.Transactional; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; - -import java.time.LocalDate; -import java.util.Optional; - -@Service -@RequiredArgsConstructor -@Transactional -public class UserServiceImpl implements UserService{ - - private final UserRepository userRepository; - - private final SocialLoginRepository socialLoginRepository; - - @Override - public UserResponseDto getUserByUserId(Long userId) { - Optional optionalUser = userRepository.findById(userId); - if(optionalUser.isPresent()){ - User user = optionalUser.get(); - SocialLogin socialLogin = socialLoginRepository.findByUser(user).get(); - return UserConverter.convertToResponseDto(user, socialLogin); - } else { - throw new UserNotFoundException("User not found with ID: " + userId); - } - } - - @Override - public UserResponseDto saveUser(UserRegisterDto userRegisterDto) { - - Optional optionalSocialLogin; - - User user; - SocialLogin socialLogin; - optionalSocialLogin = socialLoginRepository.findBySocialId(userRegisterDto.getSocialId()); - if(optionalSocialLogin.isPresent()){ - socialLogin = optionalSocialLogin.get(); - user = socialLogin.getUser(); - } - - else { - // 새로운 사용자 등록 - user = User.builder() - .email(userRegisterDto.getEmail()) - .userName(userRegisterDto.getUserName()) - .createdAt(LocalDate.now()) - .updateAt(LocalDate.now()) - .build(); - - user = userRepository.save(user); // 새 사용자 정보 저장 - - // 새 소셜 로그인 정보 저장 - socialLogin = SocialLogin.builder() - .socialId(userRegisterDto.getSocialId()) - .socialLoginType(SocialLoginType.valueOf(userRegisterDto.getSocialLoginType())) - .linkedDate(LocalDate.now()) - .user(user) - .build(); - - socialLoginRepository.save(socialLogin); // 소셜 로그인 정보 저장 - } - - return UserConverter.convertToResponseDto(user, socialLogin); - } -} \ No newline at end of file