From baa6769c0c24ee5f151e31136e49fbdaf18a6f71 Mon Sep 17 00:00:00 2001 From: 3un0ia Date: Tue, 30 Apr 2024 21:23:42 +0900 Subject: [PATCH 1/8] =?UTF-8?q?fix:=20=20=EC=98=A4=EB=A5=98=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/kr/tgwing/tech/security/config/SecurityConfig.java | 5 ----- src/main/java/kr/tgwing/tech/user/entity/UserEntity.java | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/main/java/kr/tgwing/tech/security/config/SecurityConfig.java b/src/main/java/kr/tgwing/tech/security/config/SecurityConfig.java index df74a11..1505224 100644 --- a/src/main/java/kr/tgwing/tech/security/config/SecurityConfig.java +++ b/src/main/java/kr/tgwing/tech/security/config/SecurityConfig.java @@ -82,17 +82,12 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti // .clearAuthentication(true)) .authorizeHttpRequests(request -> request -<<<<<<< HEAD - .requestMatchers("/register", "/login").permitAll() - .requestMatchers("/admin/**").hasRole("ADMIN") -======= .requestMatchers(PERMIT_URL_ARRAY) .permitAll() .requestMatchers("/register", "/login") .permitAll() .requestMatchers("/admin/**") .hasRole("ADMIN") ->>>>>>> feature/login .anyRequest().authenticated() ) .addFilterBefore(new JwtFilter(jwtUtil), LoginFilter.class) diff --git a/src/main/java/kr/tgwing/tech/user/entity/UserEntity.java b/src/main/java/kr/tgwing/tech/user/entity/UserEntity.java index 5e850d8..537470b 100644 --- a/src/main/java/kr/tgwing/tech/user/entity/UserEntity.java +++ b/src/main/java/kr/tgwing/tech/user/entity/UserEntity.java @@ -30,7 +30,7 @@ public class UserEntity extends BaseEntity implements Serializable { @Column(nullable = false) private String name; // 이름 - @Column(nullable = false) + @Column private Date birth; @Column(nullable = false, length = 11) From 99c2b29b1a082fd57c55a6148420e301a608edb9 Mon Sep 17 00:00:00 2001 From: 3un0ia Date: Tue, 30 Apr 2024 21:25:03 +0900 Subject: [PATCH 2/8] =?UTF-8?q?feat:=20=EB=8C=93=EA=B8=80=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../reply/controller/ReplyController.java | 72 ++++++++++ .../kr/tgwing/tech/reply/dto/ReplyDto.java | 27 ++++ .../tgwing/tech/reply/entity/ReplyEntity.java | 45 ++++++ .../reply/repository/ReplyRepository.java | 20 +++ .../tech/reply/service/ReplyService.java | 16 +++ .../tech/reply/service/ReplyServiceImpl.java | 128 ++++++++++++++++++ 6 files changed, 308 insertions(+) create mode 100644 src/main/java/kr/tgwing/tech/reply/controller/ReplyController.java create mode 100644 src/main/java/kr/tgwing/tech/reply/dto/ReplyDto.java create mode 100644 src/main/java/kr/tgwing/tech/reply/entity/ReplyEntity.java create mode 100644 src/main/java/kr/tgwing/tech/reply/repository/ReplyRepository.java create mode 100644 src/main/java/kr/tgwing/tech/reply/service/ReplyService.java create mode 100644 src/main/java/kr/tgwing/tech/reply/service/ReplyServiceImpl.java diff --git a/src/main/java/kr/tgwing/tech/reply/controller/ReplyController.java b/src/main/java/kr/tgwing/tech/reply/controller/ReplyController.java new file mode 100644 index 0000000..55b0e42 --- /dev/null +++ b/src/main/java/kr/tgwing/tech/reply/controller/ReplyController.java @@ -0,0 +1,72 @@ +package kr.tgwing.tech.reply.controller; + +import jakarta.transaction.Transactional; +import kr.tgwing.tech.reply.dto.ReplyDto; +import kr.tgwing.tech.reply.service.ReplyService; +import kr.tgwing.tech.reply.service.ReplyServiceImpl; +import kr.tgwing.tech.security.util.JwtUtil; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.web.PageableDefault; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequiredArgsConstructor +public class ReplyController { + + /* + * 특정 게시물 댓글 전부 가져오기 - GET, /tgwing.kr/info/notice/comment/{id} + * 게시물 댓글 달기 - POST, /tgwing.kr/notice/comment/post/{id} + * 자신이 작성한 댓글 삭제 - DELETE, /tgwing.kr/notice/comment/delete/{id} + * */ + + private final ReplyServiceImpl replyService; + private final JwtUtil jwtUtil; + + @GetMapping("/info/notice/comment/{id}") // 특정 게시물 전체 댓글 가져오기 + public ResponseEntity> getAllReplies(@PathVariable("id") Long postId) { + System.out.println("--- Get All Replies ---"); + + // post id 받고, 해당 post의 id를 reference로 가진 replies를 가져옴 + List results = replyService.getAll(postId); + return ResponseEntity.ok(results); + } + + @PostMapping("/notice/comment/post/{id}") + public ResponseEntity postReply(@PathVariable("id") Long postId, + @RequestBody ReplyDto reqDto) { + System.out.println("--- Post Reply ---"); + + ReplyDto result = replyService.post(reqDto, postId); + return ResponseEntity.ok(result); + } + + @DeleteMapping("/notice/comment/delete/{id}") + public ResponseEntity deleteReply(@PathVariable("id") Long postId, + @RequestBody ReplyDto reqDto, + @RequestHeader("authorization") String token ) { + System.out.println("--- Delete Own Reply ---"); + + String jwt = token.split(" ")[1]; + String tokenStudentId = jwtUtil.getStudentId(jwt); + + return replyService.delete(postId, reqDto, tokenStudentId); + } + + @GetMapping("/notice/comment/{id}") + @Transactional + public ResponseEntity getReplyInPage(@PathVariable("id") Long postId, + @RequestParam int page, + @RequestParam(required = false) int size, + @PageableDefault(size = 15) Pageable pageable) { + System.out.println("-- Get Replies in Page --"); + // 첫 번째 페이지 page = 0이므로, page-1로 전달 -> 1부터 요청할 수 있도록 + Page repliesInPage = replyService.findRepliesInPage(page-1, size, pageable, postId); + + return ResponseEntity.ok(repliesInPage); + } +} diff --git a/src/main/java/kr/tgwing/tech/reply/dto/ReplyDto.java b/src/main/java/kr/tgwing/tech/reply/dto/ReplyDto.java new file mode 100644 index 0000000..238e59b --- /dev/null +++ b/src/main/java/kr/tgwing/tech/reply/dto/ReplyDto.java @@ -0,0 +1,27 @@ +package kr.tgwing.tech.reply.dto; + +import kr.tgwing.tech.reply.entity.ReplyEntity; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; + +import java.time.LocalDateTime; + +@Data +@AllArgsConstructor +@Builder +public class ReplyDto { + + private Long id; + private Long writer; + private String description; + private LocalDateTime modDate; + + public static ReplyEntity toEntity(ReplyDto replyDto, Long postId) { + return ReplyEntity.builder() + .writer(replyDto.writer) + .description(replyDto.description) + .post(postId) + .build(); + } +} diff --git a/src/main/java/kr/tgwing/tech/reply/entity/ReplyEntity.java b/src/main/java/kr/tgwing/tech/reply/entity/ReplyEntity.java new file mode 100644 index 0000000..5b7ba4c --- /dev/null +++ b/src/main/java/kr/tgwing/tech/reply/entity/ReplyEntity.java @@ -0,0 +1,45 @@ +package kr.tgwing.tech.reply.entity; + +import jakarta.persistence.*; +import kr.tgwing.tech.common.BaseEntity; +import kr.tgwing.tech.reply.dto.ReplyDto; +import lombok.*; + +import java.time.LocalDateTime; + +@Entity +@Getter +@Builder +@ToString +@NoArgsConstructor +@AllArgsConstructor +@Table(name = "reply") +public class ReplyEntity extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + @JoinColumn(referencedColumnName = "id", table = "user", nullable = false) + private Long writer; + + @Column(nullable = false) + private String description; + + @JoinColumn(referencedColumnName = "id", table = "post", nullable = false) + private Long post; + +// @ManyToOne +// @JoinColumn(name = "post") +// private PostEntity post; + + public static ReplyDto toDto(ReplyEntity replyEntity) { + return ReplyDto.builder() + .id(replyEntity.id) + .writer(replyEntity.writer) + .description(replyEntity.description) + .modDate(replyEntity.getModDate()) + .build(); + } + +} diff --git a/src/main/java/kr/tgwing/tech/reply/repository/ReplyRepository.java b/src/main/java/kr/tgwing/tech/reply/repository/ReplyRepository.java new file mode 100644 index 0000000..9e37c7a --- /dev/null +++ b/src/main/java/kr/tgwing/tech/reply/repository/ReplyRepository.java @@ -0,0 +1,20 @@ +package kr.tgwing.tech.reply.repository; + +import kr.tgwing.tech.reply.entity.ReplyEntity; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface ReplyRepository extends JpaRepository { + @Query(value = "select * from reply r where r.post = :postId", nativeQuery = true) + List findAllByPost(Long postId); + + void deleteById(Long id); + + Page findAllByPostOrderByModDateDesc(Pageable pageable, Long postId); +} diff --git a/src/main/java/kr/tgwing/tech/reply/service/ReplyService.java b/src/main/java/kr/tgwing/tech/reply/service/ReplyService.java new file mode 100644 index 0000000..0175a4a --- /dev/null +++ b/src/main/java/kr/tgwing/tech/reply/service/ReplyService.java @@ -0,0 +1,16 @@ +package kr.tgwing.tech.reply.service; + +import kr.tgwing.tech.reply.dto.ReplyDto; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.http.ResponseEntity; + +import java.util.List; + +public interface ReplyService { + + List getAll(Long postId); + ReplyDto post(ReplyDto reqDto, Long postId); + ResponseEntity delete(Long postId, ReplyDto reqDto, String token); + Page findRepliesInPage(int page, int size, Pageable pageable, Long postId); +} diff --git a/src/main/java/kr/tgwing/tech/reply/service/ReplyServiceImpl.java b/src/main/java/kr/tgwing/tech/reply/service/ReplyServiceImpl.java new file mode 100644 index 0000000..9e94fad --- /dev/null +++ b/src/main/java/kr/tgwing/tech/reply/service/ReplyServiceImpl.java @@ -0,0 +1,128 @@ +package kr.tgwing.tech.reply.service; + +import kr.tgwing.tech.blog.entity.PostEntity; +import kr.tgwing.tech.blog.repository.PostRepository; +import kr.tgwing.tech.reply.dto.ReplyDto; +import kr.tgwing.tech.reply.entity.ReplyEntity; +import kr.tgwing.tech.reply.repository.ReplyRepository; +import kr.tgwing.tech.security.util.JwtUtil; +import kr.tgwing.tech.user.entity.UserEntity; +import kr.tgwing.tech.user.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.server.ResponseStatusException; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; + +import static kr.tgwing.tech.reply.entity.ReplyEntity.toDto; + +@Service +@Transactional +@RequiredArgsConstructor +public class ReplyServiceImpl implements ReplyService{ + private final PostRepository postRepository; + private final ReplyRepository replyRepository; + private final UserRepository userRepository; + + public List getAll(Long postId) { +// Optional postById = postRepository.findById(postId); +// PostEntity post = postById.orElseThrow(); + + List replies = replyRepository.findAllByPost(postId); + + if (replies.isEmpty()) { +// System.out.println("댓글 없음"); +// throw new ReplyNotFoundException(); + } + else { +// // 확인차 남기는 로그 +// for (ReplyEntity reply : replies) { +// System.out.println("reply = " + reply); +// } + List dtos = replies.stream().map(replyEntity -> toDto(replyEntity)).collect(Collectors.toList()); + return dtos; + } + return null; + } + + public ReplyDto post(ReplyDto reqDto, Long postId) { + + PostEntity post = postRepository.findById(postId).orElseThrow(); +// PostEntity post = postRepository.findById(postId).orElseThrow(PostNotFoundException::new); + + ReplyEntity replyEntity = ReplyDto.toEntity(reqDto, postId); + + ReplyEntity savedEntity = replyRepository.save(replyEntity); + ReplyDto dto = toDto(savedEntity); + + return dto; + } + + public ResponseEntity delete(Long postId, ReplyDto reqDto, String tokenStudentId) { + + // 요청으로 들어온 post의 id = postId + + // requestDto - postId, writer, description, time + + // tokenStudentId - 토큰 발급자 (현 로그인 사용자) + + + /** + * reqDto.getId() -> 레포지토리에서 엔티티 찾기 == ReplyEntity + * replyEntity -> getPost() - 댓글이 의존하고 있는 게시글의 아이디 가졍기 + * replyEntity.getPost() != postId이면 잘못된 주소. + * + * token에서 꺼낸 studentId (학번) + * reqDto.getwriter() -> 레포지토리에서 엔티티 찾기 == UserEntity + * userEntity.studentId != token.studentId 이면 삭제 권한 없음 + */ + + Optional replyById = replyRepository.findById(reqDto.getId()); + ReplyEntity replyEntity = replyById.orElseThrow(); +// ReplyEntity replyEntity = replyById.orElseThrow(ReplyNotFoundException::new); + + Optional userById = userRepository.findById(reqDto.getWriter()); + UserEntity userEntity = userById.orElseThrow(); +// UserEntity userEntity = userById.orElseThrow(UserNotFoundException::new); + + + if(Objects.equals(userEntity.getStudentId(), tokenStudentId)) { // 작성자, 수정자 확인 + + if(Objects.equals(replyEntity.getPost(), postId)) { + System.out.println("----- Delete Reply ------"); + replyRepository.delete(replyEntity); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + else { + System.out.println("요청된 게시글 ID와 삭제를 원하는 댓글이 속한 게시글 ID이 서로 다름"); + throw new ResponseStatusException(HttpStatus.BAD_REQUEST); + } + } + else { + System.out.println("URL 요청자와 공지 작성자가 다름"); + throw new ResponseStatusException(HttpStatus.FORBIDDEN); + } + } + + public Page findRepliesInPage(int page, int size, Pageable pageable, Long postId) { + PageRequest pageRequest = PageRequest.of(page, size); + + Page replyPage = replyRepository.findAllByPostOrderByModDateDesc(pageRequest, postId); + + List replies = replyPage.getContent(); + + List dtos = replies.stream().map(replyEntity -> toDto(replyEntity)).collect(Collectors.toList()); + + return new PageImpl<>(dtos, pageable, replyPage.getTotalElements()); + } +} From 9855ad9e13c7b4d103bfeab69224d063301d87f6 Mon Sep 17 00:00:00 2001 From: singsangssong Date: Mon, 6 May 2024 23:30:46 +0900 Subject: [PATCH 3/8] [REFECTOR] testing & check bug... --- build.gradle | 3 +- .../tech/blog/controller/PostController.java | 2 +- .../common/exception/ExceptionSituation.java | 2 +- .../tech/security/config/SecurityConfig.java | 6 +-- .../kr/tgwing/tech/security/util/JwtUtil.java | 1 - .../tech/user/controller/UserController.java | 6 ++- .../tgwing/tech/user/entity/UserEntity.java | 5 ++- .../tech/user/service/UserServiceImpl.java | 5 +-- src/main/resources/application.properties | 9 ++-- .../java/kr/tgwing/tech/user/LoginTest.java | 43 +++++++++++++++++++ 10 files changed, 63 insertions(+), 19 deletions(-) create mode 100644 src/test/java/kr/tgwing/tech/user/LoginTest.java diff --git a/build.gradle b/build.gradle index c5e8e16..7920484 100644 --- a/build.gradle +++ b/build.gradle @@ -35,10 +35,11 @@ dependencies { implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2' annotationProcessor 'org.projectlombok:lombok' + testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.security:spring-security-test' -// jwt + // jwt implementation 'io.jsonwebtoken:jjwt-api:0.11.2' implementation 'io.jsonwebtoken:jjwt-impl:0.11.2' implementation 'io.jsonwebtoken:jjwt-jackson:0.11.2' diff --git a/src/main/java/kr/tgwing/tech/blog/controller/PostController.java b/src/main/java/kr/tgwing/tech/blog/controller/PostController.java index 551655b..522358a 100644 --- a/src/main/java/kr/tgwing/tech/blog/controller/PostController.java +++ b/src/main/java/kr/tgwing/tech/blog/controller/PostController.java @@ -10,7 +10,7 @@ import org.springframework.web.bind.annotation.*; @RestController -//@RequestMapping("/api") +@RequestMapping("/admin") @RequiredArgsConstructor public class PostController { diff --git a/src/main/java/kr/tgwing/tech/common/exception/ExceptionSituation.java b/src/main/java/kr/tgwing/tech/common/exception/ExceptionSituation.java index 8adc72b..e823210 100644 --- a/src/main/java/kr/tgwing/tech/common/exception/ExceptionSituation.java +++ b/src/main/java/kr/tgwing/tech/common/exception/ExceptionSituation.java @@ -9,7 +9,7 @@ public class ExceptionSituation { private final String message; private final HttpStatus statusCode; - private final int errorCode; + private final int errorCode; // 어느기능에서 에러가 났는지에 대한 public static ExceptionSituation of(String message, HttpStatus statusCode, int errorCode) { return new ExceptionSituation(message, statusCode, errorCode); diff --git a/src/main/java/kr/tgwing/tech/security/config/SecurityConfig.java b/src/main/java/kr/tgwing/tech/security/config/SecurityConfig.java index df74a11..3c4b996 100644 --- a/src/main/java/kr/tgwing/tech/security/config/SecurityConfig.java +++ b/src/main/java/kr/tgwing/tech/security/config/SecurityConfig.java @@ -82,17 +82,13 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti // .clearAuthentication(true)) .authorizeHttpRequests(request -> request -<<<<<<< HEAD - .requestMatchers("/register", "/login").permitAll() - .requestMatchers("/admin/**").hasRole("ADMIN") -======= + .requestMatchers(PERMIT_URL_ARRAY) .permitAll() .requestMatchers("/register", "/login") .permitAll() .requestMatchers("/admin/**") .hasRole("ADMIN") ->>>>>>> feature/login .anyRequest().authenticated() ) .addFilterBefore(new JwtFilter(jwtUtil), LoginFilter.class) diff --git a/src/main/java/kr/tgwing/tech/security/util/JwtUtil.java b/src/main/java/kr/tgwing/tech/security/util/JwtUtil.java index ea0f89a..4096346 100644 --- a/src/main/java/kr/tgwing/tech/security/util/JwtUtil.java +++ b/src/main/java/kr/tgwing/tech/security/util/JwtUtil.java @@ -23,7 +23,6 @@ public class JwtUtil { public JwtUtil(@Value("${spring.jwt.secretKey}")String secret) { - secretKey = Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8)); } diff --git a/src/main/java/kr/tgwing/tech/user/controller/UserController.java b/src/main/java/kr/tgwing/tech/user/controller/UserController.java index 47ec714..38b5ac8 100644 --- a/src/main/java/kr/tgwing/tech/user/controller/UserController.java +++ b/src/main/java/kr/tgwing/tech/user/controller/UserController.java @@ -8,6 +8,7 @@ import kr.tgwing.tech.user.dto.UserDTO; import kr.tgwing.tech.user.service.UserService; import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; import org.apache.tomcat.util.http.parser.Authorization; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -17,6 +18,7 @@ @RestController @RequiredArgsConstructor +@Log4j2 public class UserController { private final UserService userService; @@ -35,7 +37,8 @@ public ResponseEntity test(@RequestHeader("authorization") String token) @PostMapping("/register") public ResponseEntity register(@RequestBody UserDTO userDTO) { - System.out.println(userDTO); + log.info("UserController Register..............."); + log.info(userDTO); try{ userService.register(userDTO); @@ -46,6 +49,7 @@ public ResponseEntity register(@RequestBody UserDTO userDTO) { return ResponseEntity.ok("okokok"); } + } diff --git a/src/main/java/kr/tgwing/tech/user/entity/UserEntity.java b/src/main/java/kr/tgwing/tech/user/entity/UserEntity.java index 5e850d8..5d4adbe 100644 --- a/src/main/java/kr/tgwing/tech/user/entity/UserEntity.java +++ b/src/main/java/kr/tgwing/tech/user/entity/UserEntity.java @@ -3,6 +3,7 @@ import jakarta.persistence.*; import kr.tgwing.tech.common.BaseEntity; import lombok.*; +import org.springframework.format.annotation.DateTimeFormat; import java.io.Serializable; import java.sql.Date; @@ -31,13 +32,15 @@ public class UserEntity extends BaseEntity implements Serializable { private String name; // 이름 @Column(nullable = false) + @DateTimeFormat(pattern = "yyyy-MM-dd") private Date birth; - @Column(nullable = false, length = 11) + @Column(nullable = false, length = 13) private String phoneNumber; @Column private String role; + @Column private String profilePicture; } diff --git a/src/main/java/kr/tgwing/tech/user/service/UserServiceImpl.java b/src/main/java/kr/tgwing/tech/user/service/UserServiceImpl.java index 6674768..6652d8d 100644 --- a/src/main/java/kr/tgwing/tech/user/service/UserServiceImpl.java +++ b/src/main/java/kr/tgwing/tech/user/service/UserServiceImpl.java @@ -29,14 +29,13 @@ public void register(UserDTO userDTO) throws Exception{ Boolean isExist = userRepository.existsByName(username); if (isExist) { - return; } UserEntity data = UserDTO.toUserEntity(userDTO); - data.setPassword(bCryptPasswordEncoder.encode(password)); - data.setRole("ROLE_USER"); + data.setPassword(bCryptPasswordEncoder.encode(password)); // 비밀번호 암호화 + data.setRole("ROLE_USER"); // register를 통해서 회원가입하는 유저들은 모두 USER역할 userRepository.save(data); } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index e9ee718..ca8931e 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -5,21 +5,20 @@ server.port= 8080 # Database connection spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver -spring.datasource.url=jdbc:mysql://localhost:3306/tgweb -spring.datasource.username=root -spring.datasource.password=00000000 +spring.datasource.url=jdbc:mysql://localhost:3306/tgwing +spring.datasource.username=tgwing +spring.datasource.password=tgwing spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl spring.jpa.show-sql=true -spring.jpa.hibernate.ddl-auto=update +spring.jpa.hibernate.ddl-auto=create spring.jpa.open-in-view=false spring.jpa.generate-ddl=true spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect spring.jwt.secretKey=2020105622thdtjdgnsdlaksemfdjTdmatlzbflxldjfudnj - # Web configurations spring.web.locale=ko_KR diff --git a/src/test/java/kr/tgwing/tech/user/LoginTest.java b/src/test/java/kr/tgwing/tech/user/LoginTest.java new file mode 100644 index 0000000..9a4cfd5 --- /dev/null +++ b/src/test/java/kr/tgwing/tech/user/LoginTest.java @@ -0,0 +1,43 @@ +package kr.tgwing.tech.user; + +import kr.tgwing.tech.user.entity.UserEntity; +import kr.tgwing.tech.user.repository.UserRepository; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.boot.test.context.SpringBootTest; + +import java.sql.Date; + +@SpringBootTest +public class LoginTest { + + @Autowired + UserRepository userRepository; + + @Test + @DisplayName("회원객체의 Date형식이 정확히 저장되는지를 확인하는 테스트") + public void registerTest() { + + //given + UserEntity userEntity = new UserEntity(); + userEntity.setId(5L); + userEntity.setStudentId("2020105622"); + userEntity.setPassword("1234"); + userEntity.setName("송성훈"); + userEntity.setEmail("sonng@gmail.com"); + userEntity.setBirth(Date.valueOf("2001-09-14")); + userEntity.setPhoneNumber("010-6348-5309"); + userEntity.setRole("ROLE_USER"); + + //when + UserEntity result = userRepository.save(userEntity); + + //then + Assertions.assertThat(userEntity.getBirth().getClass()).isEqualTo(result.getBirth().getClass()); + } + + +} From 73627a0253f8a4611a6d82e7d2896e39e6f6743e Mon Sep 17 00:00:00 2001 From: singsangssong Date: Mon, 6 May 2024 23:59:24 +0900 Subject: [PATCH 4/8] [REFACTOR] token time change 1month --- build.gradle | 6 +++++- .../tgwing/tech/security/config/SecurityConfig.java | 8 ++------ .../kr/tgwing/tech/security/filter/LoginFilter.java | 2 +- .../tgwing/tech/user/controller/UserController.java | 9 +++++++++ .../kr/tgwing/tech/user/dto/PasswordCheckDTO.java | 12 ++++++++++++ 5 files changed, 29 insertions(+), 8 deletions(-) create mode 100644 src/main/java/kr/tgwing/tech/user/dto/PasswordCheckDTO.java diff --git a/build.gradle b/build.gradle index c5e8e16..602bc59 100644 --- a/build.gradle +++ b/build.gradle @@ -38,10 +38,14 @@ dependencies { testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.security:spring-security-test' -// jwt + // jwt implementation 'io.jsonwebtoken:jjwt-api:0.11.2' implementation 'io.jsonwebtoken:jjwt-impl:0.11.2' implementation 'io.jsonwebtoken:jjwt-jackson:0.11.2' + + // 메일 서버와 연결해서 메일 발송함. + implementation 'org.springframework.boot:spring-boot-starter-mail' + } tasks.named('test') { diff --git a/src/main/java/kr/tgwing/tech/security/config/SecurityConfig.java b/src/main/java/kr/tgwing/tech/security/config/SecurityConfig.java index df74a11..915f14d 100644 --- a/src/main/java/kr/tgwing/tech/security/config/SecurityConfig.java +++ b/src/main/java/kr/tgwing/tech/security/config/SecurityConfig.java @@ -82,17 +82,13 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti // .clearAuthentication(true)) .authorizeHttpRequests(request -> request -<<<<<<< HEAD - .requestMatchers("/register", "/login").permitAll() - .requestMatchers("/admin/**").hasRole("ADMIN") -======= + .requestMatchers(PERMIT_URL_ARRAY) .permitAll() - .requestMatchers("/register", "/login") + .requestMatchers("/user/register", "/login") .permitAll() .requestMatchers("/admin/**") .hasRole("ADMIN") ->>>>>>> feature/login .anyRequest().authenticated() ) .addFilterBefore(new JwtFilter(jwtUtil), LoginFilter.class) diff --git a/src/main/java/kr/tgwing/tech/security/filter/LoginFilter.java b/src/main/java/kr/tgwing/tech/security/filter/LoginFilter.java index 8dd381e..de9ac4e 100644 --- a/src/main/java/kr/tgwing/tech/security/filter/LoginFilter.java +++ b/src/main/java/kr/tgwing/tech/security/filter/LoginFilter.java @@ -54,7 +54,7 @@ protected void successfulAuthentication(HttpServletRequest request, HttpServletR String role = auth.getAuthority(); - String token = jwtUtil.createJwt(username, role, 60*60*1000L); + String token = jwtUtil.createJwt(username, role, 24*60*60*1000*30L); response.addHeader("Authorization", "Bearer " + token); } diff --git a/src/main/java/kr/tgwing/tech/user/controller/UserController.java b/src/main/java/kr/tgwing/tech/user/controller/UserController.java index 47ec714..0461be2 100644 --- a/src/main/java/kr/tgwing/tech/user/controller/UserController.java +++ b/src/main/java/kr/tgwing/tech/user/controller/UserController.java @@ -16,6 +16,7 @@ import java.security.Principal; @RestController +@RequestMapping("/user") @RequiredArgsConstructor public class UserController { @@ -46,6 +47,14 @@ public ResponseEntity register(@RequestBody UserDTO userDTO) { return ResponseEntity.ok("okokok"); } + +// @PostMapping("/password/check") +// public ResponseEntity passwordCheck(@RequestBody ) { +// +// +// +// return ResponseEntity.ok().build(); +// } } diff --git a/src/main/java/kr/tgwing/tech/user/dto/PasswordCheckDTO.java b/src/main/java/kr/tgwing/tech/user/dto/PasswordCheckDTO.java new file mode 100644 index 0000000..af2ade7 --- /dev/null +++ b/src/main/java/kr/tgwing/tech/user/dto/PasswordCheckDTO.java @@ -0,0 +1,12 @@ +package kr.tgwing.tech.user.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class PasswordCheckDTO { + +} From 020fa5ef4f815e9fc9ced244c5b509612ca5397b Mon Sep 17 00:00:00 2001 From: singsangssong Date: Sun, 12 May 2024 17:43:53 +0900 Subject: [PATCH 5/8] =?UTF-8?q?feat:=EB=B9=84=EB=B0=80=EB=B2=88=ED=98=B8?= =?UTF-8?q?=20=EC=9E=AC=EC=84=A4=EC=A0=95=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 10 ++ .../kr/tgwing/tech/common/RedisConfig.java | 45 ++++++++ .../common/exception/ExceptionMapper.java | 1 + .../tech/security/config/SecurityConfig.java | 2 +- .../security/service/CustomUserDetails.java | 1 - .../kr/tgwing/tech/security/util/JwtUtil.java | 8 +- .../tech/user/controller/UserController.java | 78 +++++++++++++- .../tgwing/tech/user/dto/CheckNumberDTO.java | 10 ++ .../kr/tgwing/tech/user/dto/CheckUserDTO.java | 15 +++ .../tgwing/tech/user/dto/EmailMessageDTO.java | 14 +++ .../tech/user/dto/PasswordCheckDTO.java | 11 ++ .../java/kr/tgwing/tech/user/dto/UserDTO.java | 5 - .../java/kr/tgwing/tech/user/entity/Role.java | 5 - .../tech/user/repository/UserRepository.java | 2 +- .../tgwing/tech/user/service/UserService.java | 13 +-- .../tech/user/service/UserServiceImpl.java | 100 ++++++++++++++++-- src/main/resources/application.properties | 15 ++- src/main/resources/templates/email.html | 20 ++++ 18 files changed, 314 insertions(+), 41 deletions(-) create mode 100644 src/main/java/kr/tgwing/tech/common/RedisConfig.java create mode 100644 src/main/java/kr/tgwing/tech/user/dto/CheckNumberDTO.java create mode 100644 src/main/java/kr/tgwing/tech/user/dto/CheckUserDTO.java create mode 100644 src/main/java/kr/tgwing/tech/user/dto/EmailMessageDTO.java create mode 100644 src/main/java/kr/tgwing/tech/user/dto/PasswordCheckDTO.java delete mode 100644 src/main/java/kr/tgwing/tech/user/entity/Role.java create mode 100644 src/main/resources/templates/email.html diff --git a/build.gradle b/build.gradle index 7920484..b411cc6 100644 --- a/build.gradle +++ b/build.gradle @@ -43,6 +43,16 @@ dependencies { implementation 'io.jsonwebtoken:jjwt-api:0.11.2' implementation 'io.jsonwebtoken:jjwt-impl:0.11.2' implementation 'io.jsonwebtoken:jjwt-jackson:0.11.2' + + // gmail + implementation 'org.springframework.boot:spring-boot-starter-mail' + + // thymeleaf + implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' + implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect' + + // redis + implementation 'org.springframework.boot:spring-boot-starter-data-redis' } tasks.named('test') { diff --git a/src/main/java/kr/tgwing/tech/common/RedisConfig.java b/src/main/java/kr/tgwing/tech/common/RedisConfig.java new file mode 100644 index 0000000..55301e6 --- /dev/null +++ b/src/main/java/kr/tgwing/tech/common/RedisConfig.java @@ -0,0 +1,45 @@ +package kr.tgwing.tech.common; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.RedisStandaloneConfiguration; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +@EnableCaching +@Configuration +public class RedisConfig { + + @Value("${spring.data.redis.host}") + private String host; + + @Value("${spring.data.redis.port}") + private String port; + + @Value("${spring.data.redis.password}") + private String password; + + @Bean + public RedisConnectionFactory redisConnectionFactory() { + RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(); + redisStandaloneConfiguration.setHostName(host); + redisStandaloneConfiguration.setPort(Integer.parseInt(port)); + + LettuceConnectionFactory lettuceConnectionFactory = new LettuceConnectionFactory(redisStandaloneConfiguration); + return lettuceConnectionFactory; + } + + @Bean + public RedisTemplate redisTemplate() { + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setConnectionFactory(redisConnectionFactory()); + redisTemplate.setKeySerializer(new StringRedisSerializer()); + redisTemplate.setValueSerializer(new StringRedisSerializer()); + return redisTemplate; + } + +} diff --git a/src/main/java/kr/tgwing/tech/common/exception/ExceptionMapper.java b/src/main/java/kr/tgwing/tech/common/exception/ExceptionMapper.java index 83ace99..e805373 100644 --- a/src/main/java/kr/tgwing/tech/common/exception/ExceptionMapper.java +++ b/src/main/java/kr/tgwing/tech/common/exception/ExceptionMapper.java @@ -16,6 +16,7 @@ public class ExceptionMapper { // 예외 객체 -> 예외 상태로 바꿔주는 } private static void setUpUserException() { + } private static void setUpPostException() { diff --git a/src/main/java/kr/tgwing/tech/security/config/SecurityConfig.java b/src/main/java/kr/tgwing/tech/security/config/SecurityConfig.java index 3c4b996..ad831bb 100644 --- a/src/main/java/kr/tgwing/tech/security/config/SecurityConfig.java +++ b/src/main/java/kr/tgwing/tech/security/config/SecurityConfig.java @@ -85,7 +85,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti .requestMatchers(PERMIT_URL_ARRAY) .permitAll() - .requestMatchers("/register", "/login") + .requestMatchers("/user/**", "/login") .permitAll() .requestMatchers("/admin/**") .hasRole("ADMIN") diff --git a/src/main/java/kr/tgwing/tech/security/service/CustomUserDetails.java b/src/main/java/kr/tgwing/tech/security/service/CustomUserDetails.java index b8ed2e2..2779022 100644 --- a/src/main/java/kr/tgwing/tech/security/service/CustomUserDetails.java +++ b/src/main/java/kr/tgwing/tech/security/service/CustomUserDetails.java @@ -1,6 +1,5 @@ package kr.tgwing.tech.security.service; -import kr.tgwing.tech.user.entity.Role; import kr.tgwing.tech.user.entity.UserEntity; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; diff --git a/src/main/java/kr/tgwing/tech/security/util/JwtUtil.java b/src/main/java/kr/tgwing/tech/security/util/JwtUtil.java index 4096346..82fa3ed 100644 --- a/src/main/java/kr/tgwing/tech/security/util/JwtUtil.java +++ b/src/main/java/kr/tgwing/tech/security/util/JwtUtil.java @@ -2,18 +2,12 @@ import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.security.Keys; -import jakarta.annotation.PostConstruct; -import kr.tgwing.tech.user.entity.Role; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import javax.crypto.SecretKey; -import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; -import java.security.Key; -import java.util.Base64; import java.util.Date; @Component @@ -21,7 +15,7 @@ public class JwtUtil { private SecretKey secretKey; - public JwtUtil(@Value("${spring.jwt.secretKey}")String secret) { + public JwtUtil(@Value(".${spring.jwt.secretKey}")String secret) { secretKey = Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8)); } diff --git a/src/main/java/kr/tgwing/tech/user/controller/UserController.java b/src/main/java/kr/tgwing/tech/user/controller/UserController.java index 38b5ac8..ebcbf4a 100644 --- a/src/main/java/kr/tgwing/tech/user/controller/UserController.java +++ b/src/main/java/kr/tgwing/tech/user/controller/UserController.java @@ -2,27 +2,31 @@ import jakarta.servlet.http.HttpServletRequest; import kr.tgwing.tech.security.util.JwtUtil; -import kr.tgwing.tech.user.dto.LoginDTO; -import kr.tgwing.tech.user.dto.ProfileDTO; -import kr.tgwing.tech.user.dto.ProfileReqDTO; -import kr.tgwing.tech.user.dto.UserDTO; +import kr.tgwing.tech.user.dto.*; import kr.tgwing.tech.user.service.UserService; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; +import org.antlr.v4.runtime.misc.Pair; import org.apache.tomcat.util.http.parser.Authorization; +import org.springframework.boot.autoconfigure.data.redis.RedisProperties; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ValueOperations; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.security.Principal; +import java.util.UUID; @RestController @RequiredArgsConstructor +@RequestMapping("/user") @Log4j2 public class UserController { private final UserService userService; private final JwtUtil jwtUtil; + private final RedisTemplate redisTemplate; @GetMapping("/test") public ResponseEntity test(@RequestHeader("authorization") String token) { @@ -50,6 +54,72 @@ public ResponseEntity register(@RequestBody UserDTO userDTO) { return ResponseEntity.ok("okokok"); } + // 본인 확인 페이지 + @PostMapping("/password/checkUser") + public ResponseEntity> checkUser(@RequestBody CheckUserDTO checkUserDTO) { + log.info("UserController checkUser................"); + log.info(checkUserDTO); + + Boolean isExist = userService.checkUser(checkUserDTO); // user가 자신의 개인정보가 맞는지 확인 + + if(!isExist) + throw new IllegalStateException(); // 해당 회원정보는 존재하지 않습니다. + + // 맞으면 해당 메일로 인증번호 보내기 + EmailMessageDTO emailMessageDTO = EmailMessageDTO.builder() + .receiver(checkUserDTO.getEmail()) + .subject("[TGWING] Email 인증코드 발급") + .build(); + + String code = userService.sendEmail(emailMessageDTO); // 인증코드 만듬 + 이메이 전송 + + ValueOperations valueOperations = redisTemplate.opsForValue(); + + String studentKey = UUID.randomUUID().toString(); // UUID 또는 다른 고유한 값을 사용할 수 있습니다. + String emailKey = UUID.randomUUID().toString(); + + log.info(studentKey + " / " + emailKey); + + valueOperations.set(studentKey, checkUserDTO.getStudentId()); + valueOperations.set(emailKey, code); + + return ResponseEntity.ok(new Pair<>(studentKey, emailKey)); // ok + } + + // 인증번호 확인 페이지 + @PostMapping("/password/checkNumber") + public ResponseEntity checkNumber(@RequestParam("emailKey") String emailKey, + @RequestParam("studentKey") String studentKey, + @RequestBody CheckNumberDTO checkNumberDTO) { + + // redis로 가져와서 code의 숫자값과 입력으로 들어온 숫자가 같은지 확인하기 + ValueOperations valueOperations = redisTemplate.opsForValue(); + Object code = valueOperations.get(emailKey); + + + if(!code.equals(checkNumberDTO.getCode())) + throw new IllegalStateException(); // 인증코드가 일치하지 않습니다. + + return ResponseEntity.ok(new Pair<>(emailKey, studentKey)); + } + + // 비밀번호 재설정 페이지 + @PostMapping("/password/setPassword") // 쿼리 파라미터로 넘겨받기 + public ResponseEntity setNewPassword(@RequestParam("studentKey") String studentKey, + @RequestBody PasswordCheckDTO passwordCheckDTO) { + + log.info("UserController setNewPassword................"); + log.info(passwordCheckDTO); + + //redis로 studentId를 가져와서 학번으로 사람 구분하기 + ValueOperations valueOperations = redisTemplate.opsForValue(); + Object studentId = valueOperations.get(studentKey); + + userService.setNewPassword(studentId, passwordCheckDTO); + + return ResponseEntity.ok().build(); + } + } diff --git a/src/main/java/kr/tgwing/tech/user/dto/CheckNumberDTO.java b/src/main/java/kr/tgwing/tech/user/dto/CheckNumberDTO.java new file mode 100644 index 0000000..b3071cf --- /dev/null +++ b/src/main/java/kr/tgwing/tech/user/dto/CheckNumberDTO.java @@ -0,0 +1,10 @@ +package kr.tgwing.tech.user.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class CheckNumberDTO { + private String code; +} diff --git a/src/main/java/kr/tgwing/tech/user/dto/CheckUserDTO.java b/src/main/java/kr/tgwing/tech/user/dto/CheckUserDTO.java new file mode 100644 index 0000000..009442a --- /dev/null +++ b/src/main/java/kr/tgwing/tech/user/dto/CheckUserDTO.java @@ -0,0 +1,15 @@ +package kr.tgwing.tech.user.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Builder +@NoArgsConstructor @AllArgsConstructor +public class CheckUserDTO { + private String studentId; + private String name; + private String email; +} diff --git a/src/main/java/kr/tgwing/tech/user/dto/EmailMessageDTO.java b/src/main/java/kr/tgwing/tech/user/dto/EmailMessageDTO.java new file mode 100644 index 0000000..52c75e1 --- /dev/null +++ b/src/main/java/kr/tgwing/tech/user/dto/EmailMessageDTO.java @@ -0,0 +1,14 @@ +package kr.tgwing.tech.user.dto; + +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Builder +public class EmailMessageDTO { + private String receiver; + private String subject; + private String message; +} diff --git a/src/main/java/kr/tgwing/tech/user/dto/PasswordCheckDTO.java b/src/main/java/kr/tgwing/tech/user/dto/PasswordCheckDTO.java new file mode 100644 index 0000000..f5a2ed7 --- /dev/null +++ b/src/main/java/kr/tgwing/tech/user/dto/PasswordCheckDTO.java @@ -0,0 +1,11 @@ +package kr.tgwing.tech.user.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class PasswordCheckDTO { + private String newPassword; + private String checkPassword; +} diff --git a/src/main/java/kr/tgwing/tech/user/dto/UserDTO.java b/src/main/java/kr/tgwing/tech/user/dto/UserDTO.java index e783acf..47be833 100644 --- a/src/main/java/kr/tgwing/tech/user/dto/UserDTO.java +++ b/src/main/java/kr/tgwing/tech/user/dto/UserDTO.java @@ -1,10 +1,5 @@ package kr.tgwing.tech.user.dto; -import jakarta.persistence.Column; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import kr.tgwing.tech.user.entity.Role; import kr.tgwing.tech.user.entity.UserEntity; import lombok.*; diff --git a/src/main/java/kr/tgwing/tech/user/entity/Role.java b/src/main/java/kr/tgwing/tech/user/entity/Role.java deleted file mode 100644 index 15149db..0000000 --- a/src/main/java/kr/tgwing/tech/user/entity/Role.java +++ /dev/null @@ -1,5 +0,0 @@ -package kr.tgwing.tech.user.entity; - -public enum Role { - USER, ADMIN -} diff --git a/src/main/java/kr/tgwing/tech/user/repository/UserRepository.java b/src/main/java/kr/tgwing/tech/user/repository/UserRepository.java index 3163503..502c766 100644 --- a/src/main/java/kr/tgwing/tech/user/repository/UserRepository.java +++ b/src/main/java/kr/tgwing/tech/user/repository/UserRepository.java @@ -19,5 +19,5 @@ public interface UserRepository extends JpaRepository { @Query("UPDATE UserEntity U SET U.name = :name, U.phoneNumber = :phoneNumber, U.profilePicture = :profilePicture WHERE U.studentId = :id") void changeUser(String id, String name, String phoneNumber, String profilePicture); - Boolean existsByName(String name); + Boolean existsByStudentId(String studentId); } diff --git a/src/main/java/kr/tgwing/tech/user/service/UserService.java b/src/main/java/kr/tgwing/tech/user/service/UserService.java index 484a112..8b564cd 100644 --- a/src/main/java/kr/tgwing/tech/user/service/UserService.java +++ b/src/main/java/kr/tgwing/tech/user/service/UserService.java @@ -1,12 +1,7 @@ package kr.tgwing.tech.user.service; -import kr.tgwing.tech.user.dto.LoginDTO; -import kr.tgwing.tech.user.dto.ProfileDTO; -import kr.tgwing.tech.user.dto.ProfileReqDTO; -import kr.tgwing.tech.user.dto.UserDTO; - -import java.security.Principal; +import kr.tgwing.tech.user.dto.*; public interface UserService{ void register(UserDTO userDTO) throws Exception; @@ -17,4 +12,10 @@ public interface UserService{ ProfileDTO showUser(String name); + Boolean checkUser(CheckUserDTO checkUserDTO); // 본인 확인하기 + + String sendEmail(EmailMessageDTO emailMessage); // 메일로 인증번호 전송하기 + + void setNewPassword(Object studentId, PasswordCheckDTO password); + } diff --git a/src/main/java/kr/tgwing/tech/user/service/UserServiceImpl.java b/src/main/java/kr/tgwing/tech/user/service/UserServiceImpl.java index 6652d8d..19e8fd5 100644 --- a/src/main/java/kr/tgwing/tech/user/service/UserServiceImpl.java +++ b/src/main/java/kr/tgwing/tech/user/service/UserServiceImpl.java @@ -1,32 +1,36 @@ package kr.tgwing.tech.user.service; -import kr.tgwing.tech.user.dto.LoginDTO; -import kr.tgwing.tech.user.dto.ProfileDTO; -import kr.tgwing.tech.user.dto.ProfileReqDTO; -import kr.tgwing.tech.user.dto.UserDTO; +import jakarta.mail.MessagingException; +import jakarta.mail.internet.MimeMessage; +import kr.tgwing.tech.user.dto.*; import kr.tgwing.tech.user.entity.UserEntity; import kr.tgwing.tech.user.repository.UserRepository; import lombok.RequiredArgsConstructor; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.mail.javamail.MimeMessageHelper; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; +import org.thymeleaf.context.Context; +import org.thymeleaf.spring6.SpringTemplateEngine; import java.util.Optional; - -import static org.springframework.data.jpa.domain.AbstractPersistable_.id; +import java.util.Random; @Service @RequiredArgsConstructor public class UserServiceImpl implements UserService { private final UserRepository userRepository; + private final JavaMailSender javaMailSender; + private final SpringTemplateEngine templateEngine; private final BCryptPasswordEncoder bCryptPasswordEncoder; @Override public void register(UserDTO userDTO) throws Exception{ - String username = userDTO.getName(); + String studentId = userDTO.getStudentId(); String password = userDTO.getPassword(); - Boolean isExist = userRepository.existsByName(username); + Boolean isExist = userRepository.existsByStudentId(studentId); if (isExist) { return; @@ -41,8 +45,6 @@ public void register(UserDTO userDTO) throws Exception{ } - - @Override public void logout(String token, String studentId) { @@ -67,6 +69,7 @@ public Long changeUser(String studentId, ProfileReqDTO request){ return null; } }; + @Override public ProfileDTO showUser(String studentId){ Optional byStudentId = userRepository.findByStudentId(studentId); @@ -91,4 +94,81 @@ public ProfileDTO showUser(String studentId){ return null; } } + + @Override + public Boolean checkUser(CheckUserDTO checkUserDTO) { + Optional user = userRepository.findByStudentId(checkUserDTO.getStudentId()); + if(user.isPresent()) { + if(user.get().getEmail().equals(checkUserDTO.getEmail()) && user.get().getName().equals(checkUserDTO.getName())) + return true; + } + + return false; + } + + @Override + public String sendEmail(EmailMessageDTO emailMessageDTO) { + String authNum = createCode(); + + MimeMessage mimeMessage = javaMailSender.createMimeMessage(); + try { + MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, false, "UTF-8"); + mimeMessageHelper.setTo(emailMessageDTO.getReceiver()); + mimeMessageHelper.setSubject(emailMessageDTO.getSubject()); + mimeMessageHelper.setText(setContext(authNum, "email"), true); + javaMailSender.send(mimeMessage); + + } catch (MessagingException e) { + throw new RuntimeException(e); + } + + return authNum; + } + + public String createCode() { + Random random = new Random(); + StringBuffer key = new StringBuffer(); + for (int i = 0; i < 8; i++) { + int index = random.nextInt(4); + + switch (index) { + case 0: key.append((char) ((int) random.nextInt(26) + 97)); break; + case 1: key.append((char) ((int) random.nextInt(26) + 65)); break; + default: key.append(random.nextInt(9)); + } + } + return key.toString(); + } + + public String setContext(String code, String type) { + org.thymeleaf.context.Context context = new Context(); + context.setVariable("code", code); + + return templateEngine.process("email", context); + } + + @Override + public void setNewPassword(Object studentId, PasswordCheckDTO password) { + String newPassword = password.getNewPassword(); + System.out.println("newPassword = " + newPassword); + System.out.println("studentId.toString() = " + studentId.toString()); + + if(newPassword.equals(password.getCheckPassword())) { + Optional user = userRepository.findByStudentId(studentId.toString()); + System.out.println("user = " + user); + + if(user.isPresent()) { + user.get().setPassword(bCryptPasswordEncoder.encode(newPassword)); + userRepository.save(user.get()); + } + else { + // 비밀번호 불일치함. + } + + } + else { + throw new IllegalStateException(); // 비밀번호가 서로 일치하지 않습니다. + } + } + } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index ca8931e..400851a 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -13,12 +13,25 @@ spring.datasource.password=tgwing spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl spring.jpa.show-sql=true -spring.jpa.hibernate.ddl-auto=create +spring.jpa.hibernate.ddl-auto=update spring.jpa.open-in-view=false spring.jpa.generate-ddl=true spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect spring.jwt.secretKey=2020105622thdtjdgnsdlaksemfdjTdmatlzbflxldjfudnj +spring.mail.host=smtp.gmail.com +spring.mail.port=587 +spring.mail.username=plmko0914@gmail.com +spring.mail.password=jdud mdff zuqd qkhd +spring.mail.properties.mail.smtp.auth=true +spring.mail.properties.mail.smtp.timeout=5000 +spring.mail.properties.mail.smtp.starttls.enable=true + + # Web configurations spring.web.locale=ko_KR + +spring.data.redis.port=6379 +spring.data.redis.host=localhost +spring.data.redis.password=1234 diff --git a/src/main/resources/templates/email.html b/src/main/resources/templates/email.html new file mode 100644 index 0000000..196b5f1 --- /dev/null +++ b/src/main/resources/templates/email.html @@ -0,0 +1,20 @@ + + + + +
+

안녕하세요.

+

티지윙 동아리 가입 인증코드 창입니다.

+
+

아래 코드를 인증을 위해 입력해주세요.

+
+ +
+

인증 코드 입니다.

+
+
+
+
+ + + \ No newline at end of file From 52fac696e7d89713ac4e3c337f97e200f6d39310 Mon Sep 17 00:00:00 2001 From: singsangssong Date: Tue, 14 May 2024 01:03:52 +0900 Subject: [PATCH 6/8] =?UTF-8?q?refactor:=20exception=20user,=20profile=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=ED=95=98rl?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kr/tgwing/tech/common/ApiResponse.java | 20 ++++ .../common/exception/ExceptionMapper.java | 12 +- .../tech/security/filter/JwtFilter.java | 11 +- .../tech/security/filter/LoginFilter.java | 2 +- .../user/controller/ProfileController.java | 20 ++-- .../tech/user/controller/UserController.java | 33 ++++-- .../user/exception/EmailCodeException.java | 7 ++ .../tech/user/exception/MessageException.java | 7 ++ .../user/exception/PasswordException.java | 8 ++ .../exception/UserDuplicatedException.java | 8 ++ .../user/exception/UserNotFoundException.java | 8 ++ .../tgwing/tech/user/service/UserService.java | 5 +- .../tech/user/service/UserServiceImpl.java | 111 ++++++++---------- 13 files changed, 160 insertions(+), 92 deletions(-) create mode 100644 src/main/java/kr/tgwing/tech/common/ApiResponse.java create mode 100644 src/main/java/kr/tgwing/tech/user/exception/EmailCodeException.java create mode 100644 src/main/java/kr/tgwing/tech/user/exception/MessageException.java create mode 100644 src/main/java/kr/tgwing/tech/user/exception/PasswordException.java create mode 100644 src/main/java/kr/tgwing/tech/user/exception/UserDuplicatedException.java create mode 100644 src/main/java/kr/tgwing/tech/user/exception/UserNotFoundException.java diff --git a/src/main/java/kr/tgwing/tech/common/ApiResponse.java b/src/main/java/kr/tgwing/tech/common/ApiResponse.java new file mode 100644 index 0000000..05c07d6 --- /dev/null +++ b/src/main/java/kr/tgwing/tech/common/ApiResponse.java @@ -0,0 +1,20 @@ +package kr.tgwing.tech.common; + +public record ApiResponse (String message, T data){ + + public static ApiResponse ok() { + return new ApiResponse<>("ok", null); + } + + public static ApiResponse ok(T result) { + return new ApiResponse<>("ok", result); + } + + public static ApiResponse created(T result) { + return new ApiResponse<>("created", result); + } + + public static ApiResponse delete(T result) { + return new ApiResponse<>("delete", result); + } +} diff --git a/src/main/java/kr/tgwing/tech/common/exception/ExceptionMapper.java b/src/main/java/kr/tgwing/tech/common/exception/ExceptionMapper.java index e805373..d5c409c 100644 --- a/src/main/java/kr/tgwing/tech/common/exception/ExceptionMapper.java +++ b/src/main/java/kr/tgwing/tech/common/exception/ExceptionMapper.java @@ -1,5 +1,6 @@ package kr.tgwing.tech.common.exception; +import kr.tgwing.tech.user.exception.*; import org.springframework.http.HttpStatus; import java.util.LinkedHashMap; @@ -16,7 +17,16 @@ public class ExceptionMapper { // 예외 객체 -> 예외 상태로 바꿔주는 } private static void setUpUserException() { - + mapper.put(UserNotFoundException.class, + ExceptionSituation.of("해당 사용자가 존재하지 않습니다.", HttpStatus.NOT_FOUND, 1000)); + mapper.put(UserDuplicatedException.class, + ExceptionSituation.of("해당 사용자의 정보가 중복됩니다.", HttpStatus.CONFLICT, 1001)); + mapper.put(PasswordException.class, + ExceptionSituation.of("비밀번호가 서로 일치하지 않습니다.", HttpStatus.BAD_REQUEST,1002)); + mapper.put(MessageException.class, + ExceptionSituation.of("메일에서 에러가 발생했습니다.", HttpStatus.INTERNAL_SERVER_ERROR, 1003)); + mapper.put(EmailCodeException.class, + ExceptionSituation.of("인증번호가 서로 인치하지 않습니다.", HttpStatus.CONFLICT, 1004)); } private static void setUpPostException() { diff --git a/src/main/java/kr/tgwing/tech/security/filter/JwtFilter.java b/src/main/java/kr/tgwing/tech/security/filter/JwtFilter.java index 62a4d24..ec54147 100644 --- a/src/main/java/kr/tgwing/tech/security/filter/JwtFilter.java +++ b/src/main/java/kr/tgwing/tech/security/filter/JwtFilter.java @@ -7,17 +7,19 @@ import kr.tgwing.tech.security.service.CustomUserDetails; import kr.tgwing.tech.security.util.JwtUtil; import kr.tgwing.tech.user.entity.UserEntity; +import lombok.extern.slf4j.Slf4j; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Service; import org.springframework.web.filter.OncePerRequestFilter; import java.io.IOException; +@Slf4j public class JwtFilter extends OncePerRequestFilter { private final JwtUtil jwtUtil; public JwtFilter(JwtUtil jwtUtil) { - this.jwtUtil = jwtUtil; } @@ -32,21 +34,22 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse //Authorization 헤더 검증 if (authorization == null || !authorization.startsWith("Bearer ")) { - System.out.println("token null"); + log.info("token is null"); filterChain.doFilter(request, response); //조건이 해당되면 메소드 종료 (필수) return; } - System.out.println("authorization now"); + log.info("authorization now..."); + //Bearer 부분 제거 후 순수 토큰만 획득 String token = authorization.split(" ")[1]; //토큰 소멸 시간 검증 if (jwtUtil.isExpired(token)) { - System.out.println("token expired"); + log.info("token is expired."); filterChain.doFilter(request, response); //조건이 해당되면 메소드 종료 (필수) diff --git a/src/main/java/kr/tgwing/tech/security/filter/LoginFilter.java b/src/main/java/kr/tgwing/tech/security/filter/LoginFilter.java index 8dd381e..b495b96 100644 --- a/src/main/java/kr/tgwing/tech/security/filter/LoginFilter.java +++ b/src/main/java/kr/tgwing/tech/security/filter/LoginFilter.java @@ -43,7 +43,7 @@ public Authentication attemptAuthentication(HttpServletRequest request, HttpServ //로그인 성공시 실행하는 메소드 (여기서 JWT를 발급하면 됨) @Override protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) { -//UserDetailsS + //UserDetailsS CustomUserDetails customUserDetails = (CustomUserDetails) authentication.getPrincipal(); String username = customUserDetails.getUsername(); diff --git a/src/main/java/kr/tgwing/tech/user/controller/ProfileController.java b/src/main/java/kr/tgwing/tech/user/controller/ProfileController.java index 4458b9e..19e7192 100644 --- a/src/main/java/kr/tgwing/tech/user/controller/ProfileController.java +++ b/src/main/java/kr/tgwing/tech/user/controller/ProfileController.java @@ -18,27 +18,21 @@ public class ProfileController { private final UserService userService; @GetMapping(value = {"", "/change"}) public ResponseEntity showProfile(Principal principal){ - String name = principal.getName(); - ProfileDTO show = userService.showUser(name); + String studentId = principal.getName(); + ProfileDTO profileDTO = userService.showUser(studentId); - if(show != null){ - return ResponseEntity.ok(show); - } - return ResponseEntity.status(HttpStatus.NOT_FOUND).build(); + return ResponseEntity.ok(profileDTO); } @PutMapping("/change") public ResponseEntity changeProfile(@RequestBody ProfileReqDTO request, Principal principal){ - String name = principal.getName(); - Long change = userService.changeUser(name, request); + String studentId = principal.getName(); + Long change = userService.changeUser(studentId, request); - - if(change != null){ - return ResponseEntity.ok(change); //요거가 response다시 - } - return ResponseEntity.status(HttpStatus.NOT_FOUND).build(); + return ResponseEntity.ok(change); //요거가 response다시 } + // @GetMapping("/myPosting") diff --git a/src/main/java/kr/tgwing/tech/user/controller/UserController.java b/src/main/java/kr/tgwing/tech/user/controller/UserController.java index ebcbf4a..26d571d 100644 --- a/src/main/java/kr/tgwing/tech/user/controller/UserController.java +++ b/src/main/java/kr/tgwing/tech/user/controller/UserController.java @@ -1,8 +1,11 @@ package kr.tgwing.tech.user.controller; import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import kr.tgwing.tech.common.ApiResponse; import kr.tgwing.tech.security.util.JwtUtil; import kr.tgwing.tech.user.dto.*; +import kr.tgwing.tech.user.exception.MessageException; import kr.tgwing.tech.user.service.UserService; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; @@ -39,19 +42,27 @@ public ResponseEntity test(@RequestHeader("authorization") String token) @PostMapping("/register") - public ResponseEntity register(@RequestBody UserDTO userDTO) { + public ResponseEntity> register(@RequestBody UserDTO userDTO) { log.info("UserController Register..............."); log.info(userDTO); - try{ - userService.register(userDTO); + Long userId = userService.register(userDTO); - } catch (Exception e) { + return ResponseEntity.ok(ApiResponse.ok(userId)); + } + + @PostMapping("/logout") + public ResponseEntity> logout(HttpServletRequest request, HttpServletResponse response) { - } + String authorization = request.getHeader("authorization"); + String token = authorization.split(" ")[1]; + String studentId = jwtUtil.getStudentId(token); - return ResponseEntity.ok("okokok"); + Long userId = userService.logout(studentId); + response.setHeader("authorization", null); + + return ResponseEntity.ok(ApiResponse.ok(userId)); } // 본인 확인 페이지 @@ -88,19 +99,17 @@ public ResponseEntity> checkUser(@RequestBody CheckUserDTO // 인증번호 확인 페이지 @PostMapping("/password/checkNumber") - public ResponseEntity checkNumber(@RequestParam("emailKey") String emailKey, - @RequestParam("studentKey") String studentKey, + public ResponseEntity checkNumber(@RequestParam("studentKey") String studentKey, + @RequestParam("emailKey") String emailKey, @RequestBody CheckNumberDTO checkNumberDTO) { // redis로 가져와서 code의 숫자값과 입력으로 들어온 숫자가 같은지 확인하기 ValueOperations valueOperations = redisTemplate.opsForValue(); Object code = valueOperations.get(emailKey); + if(!code.equals(checkNumberDTO.getCode())) throw new MessageException(); // 인증코드가 일치하지 않습니다. - if(!code.equals(checkNumberDTO.getCode())) - throw new IllegalStateException(); // 인증코드가 일치하지 않습니다. - - return ResponseEntity.ok(new Pair<>(emailKey, studentKey)); + return ResponseEntity.ok(new Pair<>(studentKey, emailKey)); } // 비밀번호 재설정 페이지 diff --git a/src/main/java/kr/tgwing/tech/user/exception/EmailCodeException.java b/src/main/java/kr/tgwing/tech/user/exception/EmailCodeException.java new file mode 100644 index 0000000..0a311c9 --- /dev/null +++ b/src/main/java/kr/tgwing/tech/user/exception/EmailCodeException.java @@ -0,0 +1,7 @@ +package kr.tgwing.tech.user.exception; + +import kr.tgwing.tech.common.exception.CommonException; + +public class EmailCodeException extends CommonException { + +} diff --git a/src/main/java/kr/tgwing/tech/user/exception/MessageException.java b/src/main/java/kr/tgwing/tech/user/exception/MessageException.java new file mode 100644 index 0000000..f56b6a9 --- /dev/null +++ b/src/main/java/kr/tgwing/tech/user/exception/MessageException.java @@ -0,0 +1,7 @@ +package kr.tgwing.tech.user.exception; + +import kr.tgwing.tech.common.exception.CommonException; + +public class MessageException extends CommonException { + +} diff --git a/src/main/java/kr/tgwing/tech/user/exception/PasswordException.java b/src/main/java/kr/tgwing/tech/user/exception/PasswordException.java new file mode 100644 index 0000000..7c3145c --- /dev/null +++ b/src/main/java/kr/tgwing/tech/user/exception/PasswordException.java @@ -0,0 +1,8 @@ +package kr.tgwing.tech.user.exception; + +import kr.tgwing.tech.common.exception.CommonException; +import kr.tgwing.tech.user.entity.UserEntity; + +public class PasswordException extends CommonException { + +} diff --git a/src/main/java/kr/tgwing/tech/user/exception/UserDuplicatedException.java b/src/main/java/kr/tgwing/tech/user/exception/UserDuplicatedException.java new file mode 100644 index 0000000..45f1b33 --- /dev/null +++ b/src/main/java/kr/tgwing/tech/user/exception/UserDuplicatedException.java @@ -0,0 +1,8 @@ +package kr.tgwing.tech.user.exception; + +import kr.tgwing.tech.common.exception.CommonException; +import kr.tgwing.tech.user.entity.UserEntity; + +public class UserDuplicatedException extends CommonException { + +} diff --git a/src/main/java/kr/tgwing/tech/user/exception/UserNotFoundException.java b/src/main/java/kr/tgwing/tech/user/exception/UserNotFoundException.java new file mode 100644 index 0000000..69cccb7 --- /dev/null +++ b/src/main/java/kr/tgwing/tech/user/exception/UserNotFoundException.java @@ -0,0 +1,8 @@ +package kr.tgwing.tech.user.exception; + +import kr.tgwing.tech.common.exception.CommonException; +import kr.tgwing.tech.user.entity.UserEntity; + +public class UserNotFoundException extends CommonException { + +} diff --git a/src/main/java/kr/tgwing/tech/user/service/UserService.java b/src/main/java/kr/tgwing/tech/user/service/UserService.java index 8b564cd..614b51b 100644 --- a/src/main/java/kr/tgwing/tech/user/service/UserService.java +++ b/src/main/java/kr/tgwing/tech/user/service/UserService.java @@ -4,9 +4,9 @@ import kr.tgwing.tech.user.dto.*; public interface UserService{ - void register(UserDTO userDTO) throws Exception; + Long register(UserDTO userDTO); - void logout(String token, String studentId); + Long logout(String studentId); Long changeUser(String name, ProfileReqDTO request); @@ -18,4 +18,5 @@ public interface UserService{ void setNewPassword(Object studentId, PasswordCheckDTO password); + } diff --git a/src/main/java/kr/tgwing/tech/user/service/UserServiceImpl.java b/src/main/java/kr/tgwing/tech/user/service/UserServiceImpl.java index 19e8fd5..821f737 100644 --- a/src/main/java/kr/tgwing/tech/user/service/UserServiceImpl.java +++ b/src/main/java/kr/tgwing/tech/user/service/UserServiceImpl.java @@ -2,14 +2,20 @@ import jakarta.mail.MessagingException; import jakarta.mail.internet.MimeMessage; +import kr.tgwing.tech.common.exception.CommonException; import kr.tgwing.tech.user.dto.*; import kr.tgwing.tech.user.entity.UserEntity; +import kr.tgwing.tech.user.exception.MessageException; +import kr.tgwing.tech.user.exception.PasswordException; +import kr.tgwing.tech.user.exception.UserDuplicatedException; +import kr.tgwing.tech.user.exception.UserNotFoundException; import kr.tgwing.tech.user.repository.UserRepository; import lombok.RequiredArgsConstructor; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.MimeMessageHelper; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import org.thymeleaf.context.Context; import org.thymeleaf.spring6.SpringTemplateEngine; @@ -26,82 +32,73 @@ public class UserServiceImpl implements UserService { private final BCryptPasswordEncoder bCryptPasswordEncoder; @Override - public void register(UserDTO userDTO) throws Exception{ + public Long register(UserDTO userDTO){ String studentId = userDTO.getStudentId(); String password = userDTO.getPassword(); - Boolean isExist = userRepository.existsByStudentId(studentId); + Optional user = userRepository.findByStudentId(studentId); - if (isExist) { - return; - } + if(user.isPresent()) + throw new UserDuplicatedException(); UserEntity data = UserDTO.toUserEntity(userDTO); data.setPassword(bCryptPasswordEncoder.encode(password)); // 비밀번호 암호화 data.setRole("ROLE_USER"); // register를 통해서 회원가입하는 유저들은 모두 USER역할 - userRepository.save(data); + UserEntity save = userRepository.save(data); + + return save.getId(); } @Override - public void logout(String token, String studentId) { + public Long logout(String studentId) { + UserEntity user = userRepository.findByStudentId(studentId).orElseThrow(UserNotFoundException::new); + return user.getId(); } - + // user 정보 수정하기 @Override public Long changeUser(String studentId, ProfileReqDTO request){ - Optional userEntity = userRepository.findByStudentId(studentId); - // 만약 사용자 정보가 존재한다면 업데이트를 수행 - if (userEntity.isPresent()) { - userRepository.changeUser(studentId, request.getName(), request.getPhoneNumber(), request.getProfilePicture()); - } - Optional byStudentId = userRepository.findByStudentId(studentId); - Long id = byStudentId.get().getId(); - // 업데이트된 사용자 정보가 존재한다면 UserDTO로 변환하여 반환 - if (byStudentId.isPresent()) { - return id; - } - else{ - return null; - } + UserEntity userEntity = userRepository.findByStudentId(studentId) + .orElseThrow(UserNotFoundException::new); + + userRepository.changeUser(studentId, request.getName(), request.getPhoneNumber(), request.getProfilePicture()); + + Long id = userEntity.getId(); + + return id; }; + @Override public ProfileDTO showUser(String studentId){ - Optional byStudentId = userRepository.findByStudentId(studentId); + + UserEntity user = userRepository.findByStudentId(studentId) + .orElseThrow(UserNotFoundException::new); // 만약 사용자 정보가 존재한다면 업데이트를 수행 - if (byStudentId.isPresent()) { - UserEntity user = byStudentId.get(); - - // UserEntity를 UserDTO로 변환 - ProfileDTO userDTO = ProfileDTO.builder() - .studentId(user.getStudentId()) - .email(user.getEmail()) - .name(user.getName()) - .birth(user.getBirth()) - .phoneNumber(user.getPhoneNumber()) - .profilePicture(user.getProfilePicture()) - .build(); - - return userDTO; - } - else{ - return null; - } + ProfileDTO profileDTO = ProfileDTO.builder() + .studentId(user.getStudentId()) + .email(user.getEmail()) + .name(user.getName()) + .birth(user.getBirth()) + .phoneNumber(user.getPhoneNumber()) + .profilePicture(user.getProfilePicture()) + .build(); + + return profileDTO; } @Override public Boolean checkUser(CheckUserDTO checkUserDTO) { - Optional user = userRepository.findByStudentId(checkUserDTO.getStudentId()); - if(user.isPresent()) { - if(user.get().getEmail().equals(checkUserDTO.getEmail()) && user.get().getName().equals(checkUserDTO.getName())) - return true; - } + UserEntity user = userRepository.findByStudentId(checkUserDTO.getStudentId()) + .orElseThrow(UserNotFoundException::new); + + if(user.getEmail().equals(checkUserDTO.getEmail()) && user.getName().equals(checkUserDTO.getName())) return true; return false; } @@ -111,15 +108,19 @@ public String sendEmail(EmailMessageDTO emailMessageDTO) { String authNum = createCode(); MimeMessage mimeMessage = javaMailSender.createMimeMessage(); + try { MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, false, "UTF-8"); mimeMessageHelper.setTo(emailMessageDTO.getReceiver()); mimeMessageHelper.setSubject(emailMessageDTO.getSubject()); mimeMessageHelper.setText(setContext(authNum, "email"), true); + javaMailSender.send(mimeMessage); } catch (MessagingException e) { + throw new RuntimeException(e); + } return authNum; @@ -137,6 +138,7 @@ public String createCode() { default: key.append(random.nextInt(9)); } } + return key.toString(); } @@ -150,24 +152,15 @@ public String setContext(String code, String type) { @Override public void setNewPassword(Object studentId, PasswordCheckDTO password) { String newPassword = password.getNewPassword(); - System.out.println("newPassword = " + newPassword); - System.out.println("studentId.toString() = " + studentId.toString()); if(newPassword.equals(password.getCheckPassword())) { - Optional user = userRepository.findByStudentId(studentId.toString()); - System.out.println("user = " + user); - - if(user.isPresent()) { - user.get().setPassword(bCryptPasswordEncoder.encode(newPassword)); - userRepository.save(user.get()); - } - else { - // 비밀번호 불일치함. - } + UserEntity user = userRepository.findByStudentId(studentId.toString()).orElseThrow(UserNotFoundException::new); + user.setPassword(bCryptPasswordEncoder.encode(newPassword)); + userRepository.save(user); } else { - throw new IllegalStateException(); // 비밀번호가 서로 일치하지 않습니다. + throw new PasswordException();// 비밀번호가 서로 일치하지 않습니다. } } From 1d0935f2fece887bc85916ad55749dc94c4ccace Mon Sep 17 00:00:00 2001 From: singsangssong Date: Tue, 14 May 2024 02:37:04 +0900 Subject: [PATCH 7/8] =?UTF-8?q?refactor:apirespones=EC=A0=9C=EC=9E=91=20?= =?UTF-8?q?=EB=B0=8F=20=EC=9D=91=EB=8B=B5=ED=98=95=EC=8B=9D=20=ED=86=B5?= =?UTF-8?q?=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kr/tgwing/tech/common/ApiResponse.java | 4 ++ .../user/controller/ProfileController.java | 19 +++++++--- .../tech/user/controller/UserController.java | 38 +++++++++---------- .../tgwing/tech/user/service/UserService.java | 2 +- .../tech/user/service/UserServiceImpl.java | 6 ++- 5 files changed, 39 insertions(+), 30 deletions(-) diff --git a/src/main/java/kr/tgwing/tech/common/ApiResponse.java b/src/main/java/kr/tgwing/tech/common/ApiResponse.java index 05c07d6..78a0271 100644 --- a/src/main/java/kr/tgwing/tech/common/ApiResponse.java +++ b/src/main/java/kr/tgwing/tech/common/ApiResponse.java @@ -14,6 +14,10 @@ public static ApiResponse created(T result) { return new ApiResponse<>("created", result); } + public static ApiResponse updated(T result) { + return new ApiResponse<>("update", result); + } + public static ApiResponse delete(T result) { return new ApiResponse<>("delete", result); } diff --git a/src/main/java/kr/tgwing/tech/user/controller/ProfileController.java b/src/main/java/kr/tgwing/tech/user/controller/ProfileController.java index 19e7192..4a1cd36 100644 --- a/src/main/java/kr/tgwing/tech/user/controller/ProfileController.java +++ b/src/main/java/kr/tgwing/tech/user/controller/ProfileController.java @@ -1,5 +1,8 @@ package kr.tgwing.tech.user.controller; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import kr.tgwing.tech.common.ApiResponse; import kr.tgwing.tech.user.dto.ProfileDTO; import kr.tgwing.tech.user.dto.ProfileReqDTO; import kr.tgwing.tech.user.service.UserService; @@ -13,23 +16,27 @@ @RestController @RequiredArgsConstructor @RequestMapping("/profile") +@Tag(name = "회원정보 수정하기") public class ProfileController { private final UserService userService; - @GetMapping(value = {"", "/change"}) - public ResponseEntity showProfile(Principal principal){ + + @Operation(summary = "회원정보 조회하기 + 수정페이지 입력부분") + @GetMapping(value = {"", "/fix"}) + public ResponseEntity> showProfile(Principal principal){ String studentId = principal.getName(); ProfileDTO profileDTO = userService.showUser(studentId); - return ResponseEntity.ok(profileDTO); + return ResponseEntity.ok(ApiResponse.ok(profileDTO)); } - @PutMapping("/change") - public ResponseEntity changeProfile(@RequestBody ProfileReqDTO request, Principal principal){ + @Operation(summary = "유저 정보 수정 완료" ) + @PutMapping("/fix") + public ResponseEntity> changeProfile(@RequestBody ProfileReqDTO request, Principal principal){ String studentId = principal.getName(); Long change = userService.changeUser(studentId, request); - return ResponseEntity.ok(change); //요거가 response다시 + return ResponseEntity.ok(ApiResponse.updated(change)); } diff --git a/src/main/java/kr/tgwing/tech/user/controller/UserController.java b/src/main/java/kr/tgwing/tech/user/controller/UserController.java index 26d571d..ce443bf 100644 --- a/src/main/java/kr/tgwing/tech/user/controller/UserController.java +++ b/src/main/java/kr/tgwing/tech/user/controller/UserController.java @@ -1,5 +1,7 @@ package kr.tgwing.tech.user.controller; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import kr.tgwing.tech.common.ApiResponse; @@ -25,22 +27,15 @@ @RequiredArgsConstructor @RequestMapping("/user") @Log4j2 +@Tag(name = "로그인 하기 전 기능 + 로그아웃") public class UserController { private final UserService userService; private final JwtUtil jwtUtil; private final RedisTemplate redisTemplate; - @GetMapping("/test") - public ResponseEntity test(@RequestHeader("authorization") String token) { - - String s = token.split(" ")[1]; - String studentId = jwtUtil.getStudentId(s); - - return ResponseEntity.ok(studentId); - } - + @Operation(summary = "회원 등록하기") @PostMapping("/register") public ResponseEntity> register(@RequestBody UserDTO userDTO) { @@ -52,6 +47,7 @@ public ResponseEntity> register(@RequestBody UserDTO userDTO) return ResponseEntity.ok(ApiResponse.ok(userId)); } + @Operation(summary = "로그아웃하기") @PostMapping("/logout") public ResponseEntity> logout(HttpServletRequest request, HttpServletResponse response) { @@ -65,9 +61,9 @@ public ResponseEntity> logout(HttpServletRequest request, Http return ResponseEntity.ok(ApiResponse.ok(userId)); } - // 본인 확인 페이지 - @PostMapping("/password/checkUser") - public ResponseEntity> checkUser(@RequestBody CheckUserDTO checkUserDTO) { + @Operation(summary = "비밀전호 재설정하기 전, 본인확인하기") + @PostMapping("/password") + public ResponseEntity>> checkUser(@RequestBody CheckUserDTO checkUserDTO) { log.info("UserController checkUser................"); log.info(checkUserDTO); @@ -94,12 +90,12 @@ public ResponseEntity> checkUser(@RequestBody CheckUserDTO valueOperations.set(studentKey, checkUserDTO.getStudentId()); valueOperations.set(emailKey, code); - return ResponseEntity.ok(new Pair<>(studentKey, emailKey)); // ok + return ResponseEntity.ok(ApiResponse.created(new Pair<>(studentKey, emailKey))); } - // 인증번호 확인 페이지 + @Operation(summary = "본인확인 이메일 인증하기") @PostMapping("/password/checkNumber") - public ResponseEntity checkNumber(@RequestParam("studentKey") String studentKey, + public ResponseEntity> checkNumber(@RequestParam("studentKey") String studentKey, @RequestParam("emailKey") String emailKey, @RequestBody CheckNumberDTO checkNumberDTO) { @@ -109,12 +105,12 @@ public ResponseEntity checkNumber(@RequestParam("studentKey") String studentKey, if(!code.equals(checkNumberDTO.getCode())) throw new MessageException(); // 인증코드가 일치하지 않습니다. - return ResponseEntity.ok(new Pair<>(studentKey, emailKey)); + return ResponseEntity.ok(ApiResponse.created(studentKey)); } - // 비밀번호 재설정 페이지 - @PostMapping("/password/setPassword") // 쿼리 파라미터로 넘겨받기 - public ResponseEntity setNewPassword(@RequestParam("studentKey") String studentKey, + @Operation(summary = "비밀번호 재설정하기") + @PostMapping("/password/reset") // 쿼리 파라미터로 넘겨받기 + public ResponseEntity> setNewPassword(@RequestParam("studentKey") String studentKey, @RequestBody PasswordCheckDTO passwordCheckDTO) { log.info("UserController setNewPassword................"); @@ -124,9 +120,9 @@ public ResponseEntity setNewPassword(@RequestParam("studentKey") String studentK ValueOperations valueOperations = redisTemplate.opsForValue(); Object studentId = valueOperations.get(studentKey); - userService.setNewPassword(studentId, passwordCheckDTO); + Long userId = userService.setNewPassword(studentId, passwordCheckDTO); - return ResponseEntity.ok().build(); + return ResponseEntity.ok(ApiResponse.updated(userId)); } } diff --git a/src/main/java/kr/tgwing/tech/user/service/UserService.java b/src/main/java/kr/tgwing/tech/user/service/UserService.java index 614b51b..10c1be2 100644 --- a/src/main/java/kr/tgwing/tech/user/service/UserService.java +++ b/src/main/java/kr/tgwing/tech/user/service/UserService.java @@ -16,7 +16,7 @@ public interface UserService{ String sendEmail(EmailMessageDTO emailMessage); // 메일로 인증번호 전송하기 - void setNewPassword(Object studentId, PasswordCheckDTO password); + Long setNewPassword(Object studentId, PasswordCheckDTO password); } diff --git a/src/main/java/kr/tgwing/tech/user/service/UserServiceImpl.java b/src/main/java/kr/tgwing/tech/user/service/UserServiceImpl.java index 821f737..14a7e93 100644 --- a/src/main/java/kr/tgwing/tech/user/service/UserServiceImpl.java +++ b/src/main/java/kr/tgwing/tech/user/service/UserServiceImpl.java @@ -150,14 +150,16 @@ public String setContext(String code, String type) { } @Override - public void setNewPassword(Object studentId, PasswordCheckDTO password) { + public Long setNewPassword(Object studentId, PasswordCheckDTO password) { String newPassword = password.getNewPassword(); if(newPassword.equals(password.getCheckPassword())) { UserEntity user = userRepository.findByStudentId(studentId.toString()).orElseThrow(UserNotFoundException::new); user.setPassword(bCryptPasswordEncoder.encode(newPassword)); - userRepository.save(user); + UserEntity save = userRepository.save(user); + + return save.getId(); } else { throw new PasswordException();// 비밀번호가 서로 일치하지 않습니다. From 11ccfce776251902e5f758bcf0cea88ae333cbd8 Mon Sep 17 00:00:00 2001 From: singsangssong Date: Tue, 14 May 2024 19:43:22 +0900 Subject: [PATCH 8/8] =?UTF-8?q?fix:=20=EB=B2=84=EA=B7=B8=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/controller/ProfileController.java | 13 +++--- .../tech/user/controller/UserController.java | 44 +++++++++---------- .../tech/user/service/UserServiceImpl.java | 3 +- 3 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/main/java/kr/tgwing/tech/user/controller/ProfileController.java b/src/main/java/kr/tgwing/tech/user/controller/ProfileController.java index 4a1cd36..9f6aff0 100644 --- a/src/main/java/kr/tgwing/tech/user/controller/ProfileController.java +++ b/src/main/java/kr/tgwing/tech/user/controller/ProfileController.java @@ -21,9 +21,10 @@ public class ProfileController { private final UserService userService; - @Operation(summary = "회원정보 조회하기 + 수정페이지 입력부분") - @GetMapping(value = {"", "/fix"}) - public ResponseEntity> showProfile(Principal principal){ + @Operation(summary = "회원정보 조회하기") + @GetMapping("") + public ResponseEntity> showProfile( + Principal principal){ String studentId = principal.getName(); ProfileDTO profileDTO = userService.showUser(studentId); @@ -31,8 +32,10 @@ public ResponseEntity> showProfile(Principal principal){ } @Operation(summary = "유저 정보 수정 완료" ) - @PutMapping("/fix") - public ResponseEntity> changeProfile(@RequestBody ProfileReqDTO request, Principal principal){ + @PutMapping("") + public ResponseEntity> changeProfile( + @RequestBody ProfileReqDTO request, + Principal principal){ String studentId = principal.getName(); Long change = userService.changeUser(studentId, request); diff --git a/src/main/java/kr/tgwing/tech/user/controller/UserController.java b/src/main/java/kr/tgwing/tech/user/controller/UserController.java index ce443bf..b7c4578 100644 --- a/src/main/java/kr/tgwing/tech/user/controller/UserController.java +++ b/src/main/java/kr/tgwing/tech/user/controller/UserController.java @@ -38,19 +38,18 @@ public class UserController { @Operation(summary = "회원 등록하기") @PostMapping("/register") public ResponseEntity> register(@RequestBody UserDTO userDTO) { - log.info("UserController Register..............."); log.info(userDTO); - Long userId = userService.register(userDTO); - return ResponseEntity.ok(ApiResponse.ok(userId)); + return ResponseEntity.ok(ApiResponse.created(userId)); } @Operation(summary = "로그아웃하기") @PostMapping("/logout") - public ResponseEntity> logout(HttpServletRequest request, HttpServletResponse response) { - + public ResponseEntity> logout( + HttpServletRequest request, + HttpServletResponse response) { String authorization = request.getHeader("authorization"); String token = authorization.split(" ")[1]; String studentId = jwtUtil.getStudentId(token); @@ -63,30 +62,24 @@ public ResponseEntity> logout(HttpServletRequest request, Http @Operation(summary = "비밀전호 재설정하기 전, 본인확인하기") @PostMapping("/password") - public ResponseEntity>> checkUser(@RequestBody CheckUserDTO checkUserDTO) { + public ResponseEntity>> checkUser( + @RequestBody CheckUserDTO checkUserDTO) { log.info("UserController checkUser................"); log.info(checkUserDTO); - Boolean isExist = userService.checkUser(checkUserDTO); // user가 자신의 개인정보가 맞는지 확인 - - if(!isExist) - throw new IllegalStateException(); // 해당 회원정보는 존재하지 않습니다. + Boolean isExist = userService.checkUser(checkUserDTO); + if(!isExist) throw new IllegalStateException(); - // 맞으면 해당 메일로 인증번호 보내기 EmailMessageDTO emailMessageDTO = EmailMessageDTO.builder() .receiver(checkUserDTO.getEmail()) .subject("[TGWING] Email 인증코드 발급") .build(); - - String code = userService.sendEmail(emailMessageDTO); // 인증코드 만듬 + 이메이 전송 + String code = userService.sendEmail(emailMessageDTO); ValueOperations valueOperations = redisTemplate.opsForValue(); - - String studentKey = UUID.randomUUID().toString(); // UUID 또는 다른 고유한 값을 사용할 수 있습니다. + String studentKey = UUID.randomUUID().toString(); String emailKey = UUID.randomUUID().toString(); - log.info(studentKey + " / " + emailKey); - valueOperations.set(studentKey, checkUserDTO.getStudentId()); valueOperations.set(emailKey, code); @@ -94,15 +87,18 @@ public ResponseEntity>> checkUser(@RequestBody } @Operation(summary = "본인확인 이메일 인증하기") - @PostMapping("/password/checkNumber") - public ResponseEntity> checkNumber(@RequestParam("studentKey") String studentKey, - @RequestParam("emailKey") String emailKey, - @RequestBody CheckNumberDTO checkNumberDTO) { + @PostMapping("/password/check") + public ResponseEntity> checkNumber( + @RequestParam("studentKey") String studentKey, + @RequestParam("emailKey") String emailKey, + @RequestBody CheckNumberDTO checkNumberDTO) { // redis로 가져와서 code의 숫자값과 입력으로 들어온 숫자가 같은지 확인하기 ValueOperations valueOperations = redisTemplate.opsForValue(); Object code = valueOperations.get(emailKey); + log.info(code); + if(!code.equals(checkNumberDTO.getCode())) throw new MessageException(); // 인증코드가 일치하지 않습니다. return ResponseEntity.ok(ApiResponse.created(studentKey)); @@ -110,8 +106,9 @@ public ResponseEntity> checkNumber(@RequestParam("studentKey @Operation(summary = "비밀번호 재설정하기") @PostMapping("/password/reset") // 쿼리 파라미터로 넘겨받기 - public ResponseEntity> setNewPassword(@RequestParam("studentKey") String studentKey, - @RequestBody PasswordCheckDTO passwordCheckDTO) { + public ResponseEntity> setNewPassword( + @RequestParam("studentKey") String studentKey, + @RequestBody PasswordCheckDTO passwordCheckDTO) { log.info("UserController setNewPassword................"); log.info(passwordCheckDTO); @@ -119,7 +116,6 @@ public ResponseEntity> setNewPassword(@RequestParam("studentKe //redis로 studentId를 가져와서 학번으로 사람 구분하기 ValueOperations valueOperations = redisTemplate.opsForValue(); Object studentId = valueOperations.get(studentKey); - Long userId = userService.setNewPassword(studentId, passwordCheckDTO); return ResponseEntity.ok(ApiResponse.updated(userId)); diff --git a/src/main/java/kr/tgwing/tech/user/service/UserServiceImpl.java b/src/main/java/kr/tgwing/tech/user/service/UserServiceImpl.java index 14a7e93..0ec311e 100644 --- a/src/main/java/kr/tgwing/tech/user/service/UserServiceImpl.java +++ b/src/main/java/kr/tgwing/tech/user/service/UserServiceImpl.java @@ -100,11 +100,12 @@ public Boolean checkUser(CheckUserDTO checkUserDTO) { if(user.getEmail().equals(checkUserDTO.getEmail()) && user.getName().equals(checkUserDTO.getName())) return true; - return false; + else throw new UserNotFoundException(); } @Override public String sendEmail(EmailMessageDTO emailMessageDTO) { + String authNum = createCode(); MimeMessage mimeMessage = javaMailSender.createMimeMessage();