From f5563a7e473fd56ccf2bdce53e1b8fbd2b699f7e Mon Sep 17 00:00:00 2001
From: kkj6235 <kkj6235@ajou.ac.kr>
Date: Thu, 23 Nov 2023 03:14:40 +0900
Subject: [PATCH] refactor: enhance JWT token-based authentication for user
 login, post creation, and comment creation

---
 .../config/security/JwtTokenProvider.java     |  10 +-
 .../spring/post/config/security/MyUser.java   |  35 +++++
 .../post/config/security/SecurityUtil.java    |  12 +-
 .../post/controller/AuthController.java       |  14 +-
 .../post/controller/PostController.java       |  21 +--
 .../umc/spring/post/data/dto/CommentDto.java  |   1 -
 .../spring/post/data/dto/CommentResDto.java   |   1 +
 .../umc/spring/post/data/dto/PostDto.java     |   2 -
 .../umc/spring/post/data/dto/UserInfoDto.java |   3 +-
 .../umc/spring/post/data/entity/Post.java     |   2 +
 .../spring/post/service/AuthServiceImpl.java  |  21 ++-
 .../spring/post/service/PostServiceImpl.java  | 133 ++++++++++++------
 12 files changed, 178 insertions(+), 77 deletions(-)
 create mode 100644 src/main/java/umc/spring/post/config/security/MyUser.java

diff --git a/src/main/java/umc/spring/post/config/security/JwtTokenProvider.java b/src/main/java/umc/spring/post/config/security/JwtTokenProvider.java
index d96c1f6..80a8a69 100644
--- a/src/main/java/umc/spring/post/config/security/JwtTokenProvider.java
+++ b/src/main/java/umc/spring/post/config/security/JwtTokenProvider.java
@@ -6,7 +6,6 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticatio
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.authority.SimpleGrantedAuthority;
-import org.springframework.security.core.userdetails.User;
 import org.springframework.security.core.userdetails.UserDetails;
 import org.springframework.stereotype.Component;
 
@@ -24,7 +23,7 @@ public class JwtTokenProvider {
         this.secretKey = secretKey;
     }
 
-    public TokenInfo generateToken(Authentication authentication) {
+    public TokenInfo generateToken(Authentication authentication, Long id, String username) {
         System.out.println(authentication);
         String authorities = authentication.getAuthorities().stream()
                 .map(GrantedAuthority::getAuthority)
@@ -36,6 +35,8 @@ public class JwtTokenProvider {
         String accessToken = Jwts.builder()
                 .setSubject(authentication.getName())
                 .claim("auth", authorities)
+                .claim("userId",id)
+                .claim("userName",username)
                 .setExpiration(accessTokenExpiration)
                 .signWith(SignatureAlgorithm.HS256, secretKey)
                 .compact();
@@ -53,9 +54,10 @@ public class JwtTokenProvider {
         return tokenInfo;
     }
 
+
     public Authentication getAuthentication(String accessToken) {
         Claims claims = parseClaims(accessToken);
-        System.out.println("log" + claims);
+        System.out.println(claims);
         if (claims.get("auth") == null) {
             throw new RuntimeException("권한 정보가 없는 토큰입니다.");
         }
@@ -65,7 +67,7 @@ public class JwtTokenProvider {
                         .map(SimpleGrantedAuthority::new)
                         .collect(Collectors.toList());
 
-        UserDetails principal = new User(claims.getSubject(), "", authorities);
+        UserDetails principal = new MyUser(claims.getSubject(), "", authorities,claims);
         return new UsernamePasswordAuthenticationToken(principal, "", authorities);
     }
 
diff --git a/src/main/java/umc/spring/post/config/security/MyUser.java b/src/main/java/umc/spring/post/config/security/MyUser.java
new file mode 100644
index 0000000..1c1a878
--- /dev/null
+++ b/src/main/java/umc/spring/post/config/security/MyUser.java
@@ -0,0 +1,35 @@
+package umc.spring.post.config.security;
+
+import io.jsonwebtoken.Claims;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.userdetails.User;
+
+import java.util.Collection;
+
+public class MyUser extends User {
+
+    Long userId;
+    String userName;
+
+    public MyUser(String username, String password, Collection<? extends GrantedAuthority> authorities, Claims claims) {
+        super(username, password, authorities);
+        this.userId = (long) ((Integer) claims.get("userId")).intValue();
+        this.userName = claims.get("userName").toString();
+    }
+
+    public MyUser(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities, Claims claims) {
+        super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
+    }
+
+    public Long getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Long userId) {
+        this.userId = userId;
+    }
+
+    public String getUserName() {
+        return userName;
+    }
+}
diff --git a/src/main/java/umc/spring/post/config/security/SecurityUtil.java b/src/main/java/umc/spring/post/config/security/SecurityUtil.java
index 6574930..030ac5f 100644
--- a/src/main/java/umc/spring/post/config/security/SecurityUtil.java
+++ b/src/main/java/umc/spring/post/config/security/SecurityUtil.java
@@ -1,22 +1,30 @@
 package umc.spring.post.config.security;
 
 
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Controller;
 import umc.spring.post.data.dto.UserInfoDto;
+import umc.spring.post.data.entity.User;
+import umc.spring.post.repository.UserRepository;
+
+import java.util.Objects;
 
 public class SecurityUtil {
+
     public static UserInfoDto getCurrentMemberId() {
         final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
 
         if (authentication == null || authentication.getName() == null) {
             throw new RuntimeException("No authentication information.");
         }
-
+        MyUser myUser = (MyUser) authentication.getPrincipal();
         UserInfoDto userInfoDto = new UserInfoDto();
+        userInfoDto.setUserId(myUser.getUserId());
+        userInfoDto.setUserName(myUser.getUserName());
         userInfoDto.setLoginId(authentication.getName());
         userInfoDto.setMemberRole(authentication.getAuthorities().stream().toList().get(0).toString().replaceAll("ROLE_", ""));
-
         return userInfoDto;
     }
 }
\ No newline at end of file
diff --git a/src/main/java/umc/spring/post/controller/AuthController.java b/src/main/java/umc/spring/post/controller/AuthController.java
index 0882508..3389080 100644
--- a/src/main/java/umc/spring/post/controller/AuthController.java
+++ b/src/main/java/umc/spring/post/controller/AuthController.java
@@ -2,13 +2,19 @@ package umc.spring.post.controller;
 
 
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.*;
+import org.springframework.web.server.ResponseStatusException;
 import umc.spring.post.config.security.TokenInfo;
 import umc.spring.post.data.dto.UserInfoDto;
 import umc.spring.post.data.dto.UserJoinDto;
 import umc.spring.post.data.dto.UserLoginDto;
 import umc.spring.post.service.AuthService;
 
+import javax.management.AttributeNotFoundException;
+
 @RestController
 @RequestMapping("/user")
 public class AuthController {
@@ -30,7 +36,13 @@ public class AuthController {
     }
 
     @GetMapping("/info")
+    @ResponseStatus(HttpStatus.OK)
     public UserInfoDto info() {
-        return authService.info();
+        try{
+            return authService.info();
+        }
+        catch(Exception e){
+            throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "token not Found");
+        }
     }
 }
diff --git a/src/main/java/umc/spring/post/controller/PostController.java b/src/main/java/umc/spring/post/controller/PostController.java
index ef71aec..6a06b5c 100644
--- a/src/main/java/umc/spring/post/controller/PostController.java
+++ b/src/main/java/umc/spring/post/controller/PostController.java
@@ -1,22 +1,16 @@
 package umc.spring.post.controller;
 
-import com.fasterxml.jackson.databind.ObjectMapper;
-import jakarta.servlet.http.HttpServletResponse;
-import jakarta.transaction.Transactional;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
-import org.springframework.stereotype.Controller;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.server.ResponseStatusException;
 import umc.spring.post.data.dto.CommentDto;
 import umc.spring.post.data.dto.PostDto;
 import umc.spring.post.data.dto.PostResDto;
-import umc.spring.post.data.entity.Post;
 import umc.spring.post.service.PostService;
 
 import java.util.List;
 
-import static org.springframework.data.jpa.domain.AbstractPersistable_.id;
 
 @RestController
 public class PostController {
@@ -34,7 +28,12 @@ public class PostController {
     @ResponseStatus(HttpStatus.OK)
     @PostMapping("/post/upload")
     public void upload(@RequestBody PostDto postDto){
-        postService.upload(postDto);
+        try{
+            postService.upload(postDto);
+        }
+        catch(Exception e){
+            throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "token not Found");
+        }
     }
 
     @GetMapping("/post/{id}")
@@ -75,7 +74,13 @@ public class PostController {
     @PostMapping("/post/comments")
     @ResponseStatus(HttpStatus.OK)
     public void addComment(@RequestBody CommentDto commentDto){
-        postService.addComment(commentDto);
+        try{
+            postService.addComment(commentDto);
+        }
+        catch(Exception e){
+            throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "token not found");
+
+        }
     }
 
     @DeleteMapping("/post/comments")
diff --git a/src/main/java/umc/spring/post/data/dto/CommentDto.java b/src/main/java/umc/spring/post/data/dto/CommentDto.java
index 23567b6..3499c67 100644
--- a/src/main/java/umc/spring/post/data/dto/CommentDto.java
+++ b/src/main/java/umc/spring/post/data/dto/CommentDto.java
@@ -6,7 +6,6 @@ import lombok.Data;
 public class CommentDto {
 
     private Long postId;
-    private Long userId;
     private String author;
     private String text;
 
diff --git a/src/main/java/umc/spring/post/data/dto/CommentResDto.java b/src/main/java/umc/spring/post/data/dto/CommentResDto.java
index 18e0d6a..ef005b5 100644
--- a/src/main/java/umc/spring/post/data/dto/CommentResDto.java
+++ b/src/main/java/umc/spring/post/data/dto/CommentResDto.java
@@ -17,6 +17,7 @@ public class CommentResDto {
     private String author;
     private String text;
     private Date timestamp;
+
     public static CommentResDto toDTO(Comment comment){
 
         return CommentResDto.builder()
diff --git a/src/main/java/umc/spring/post/data/dto/PostDto.java b/src/main/java/umc/spring/post/data/dto/PostDto.java
index 0b08be5..54b1fe6 100644
--- a/src/main/java/umc/spring/post/data/dto/PostDto.java
+++ b/src/main/java/umc/spring/post/data/dto/PostDto.java
@@ -6,9 +6,7 @@ import lombok.Setter;
 
 @Data
 public class PostDto {
-    Long userId;
     String title;
-    String author;
     String body;
     String image;
     int likeCount;
diff --git a/src/main/java/umc/spring/post/data/dto/UserInfoDto.java b/src/main/java/umc/spring/post/data/dto/UserInfoDto.java
index ee38d4e..cc6e6c1 100644
--- a/src/main/java/umc/spring/post/data/dto/UserInfoDto.java
+++ b/src/main/java/umc/spring/post/data/dto/UserInfoDto.java
@@ -8,9 +8,8 @@ import lombok.*;
 @ToString
 @Builder
 public class UserInfoDto {
-    private Long id;
+    private Long userId;
     private String userName;
     private String loginId;
     private String memberRole;
-
 }
\ No newline at end of file
diff --git a/src/main/java/umc/spring/post/data/entity/Post.java b/src/main/java/umc/spring/post/data/entity/Post.java
index c3df9aa..5fb5452 100644
--- a/src/main/java/umc/spring/post/data/entity/Post.java
+++ b/src/main/java/umc/spring/post/data/entity/Post.java
@@ -46,4 +46,6 @@ public class Post{
             orphanRemoval = true)
     private List<Comment> comments = new ArrayList<>();
 
+
+
 }
diff --git a/src/main/java/umc/spring/post/service/AuthServiceImpl.java b/src/main/java/umc/spring/post/service/AuthServiceImpl.java
index cb5c1f4..38a2509 100644
--- a/src/main/java/umc/spring/post/service/AuthServiceImpl.java
+++ b/src/main/java/umc/spring/post/service/AuthServiceImpl.java
@@ -2,6 +2,7 @@ package umc.spring.post.service;
 
 
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
 import org.springframework.security.authentication.BadCredentialsException;
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 import org.springframework.security.core.Authentication;
@@ -15,15 +16,17 @@ import org.springframework.stereotype.Service;
 
 import umc.spring.post.data.entity.User;
 import umc.spring.post.config.security.JwtTokenProvider;
-import umc.spring.post.config.security.Role;
-import umc.spring.post.config.security.SecurityUtil;
 import umc.spring.post.config.security.TokenInfo;
 import umc.spring.post.data.dto.UserInfoDto;
 import umc.spring.post.data.dto.UserJoinDto;
 import umc.spring.post.data.dto.UserLoginDto;
 import umc.spring.post.repository.UserRepository;
 
-import java.util.Optional;
+
+import java.security.Principal;
+import java.util.Objects;
+
+import static umc.spring.post.config.security.SecurityUtil.getCurrentMemberId;
 
 
 @Service
@@ -52,11 +55,10 @@ public class AuthServiceImpl implements AuthService, UserDetailsService {
         if (!matches) throw new BadCredentialsException("아이디 혹은 비밀번호를 확인하세요.");
 
         Authentication authentication = new UsernamePasswordAuthenticationToken(user.getLoginId(), user.getPassword(), user.getAuthorities());
-
-        TokenInfo tokenInfo = jwtTokenProvider.generateToken(authentication);
+        TokenInfo tokenInfo = jwtTokenProvider.generateToken(authentication, user.getId(),user.getUsername());
         tokenInfo.setEmail(user.getLoginId());
-
         tokenInfo.setMemberRole(user.getRole().toString());
+
         return tokenInfo;
     }
 
@@ -71,13 +73,10 @@ public class AuthServiceImpl implements AuthService, UserDetailsService {
 
     @Override
     public UserInfoDto info() {
-        UserInfoDto userInfoDto = SecurityUtil.getCurrentMemberId();
-        User user = userRepository.findByLoginId(userInfoDto.getLoginId()).orElseThrow();
-        userInfoDto.setUserName(user.getUsername());
-        userInfoDto.setId(user.getId());
-        return userInfoDto;
+        return getCurrentMemberId();
     }
 
+
     @Override
     public UserDetails loadUserByUsername(String userId) throws UsernameNotFoundException {
         return userRepository.findByLoginId(userId)
diff --git a/src/main/java/umc/spring/post/service/PostServiceImpl.java b/src/main/java/umc/spring/post/service/PostServiceImpl.java
index 89b34d2..fab17bd 100644
--- a/src/main/java/umc/spring/post/service/PostServiceImpl.java
+++ b/src/main/java/umc/spring/post/service/PostServiceImpl.java
@@ -1,13 +1,10 @@
 package umc.spring.post.service;
 
 
-import jakarta.servlet.http.HttpServletResponse;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
-import org.springframework.security.core.userdetails.UsernameNotFoundException;
 import org.springframework.stereotype.Service;
-import umc.spring.post.config.security.SecurityUtil;
+import org.springframework.web.server.ResponseStatusException;
 import umc.spring.post.data.dto.CommentDto;
 import umc.spring.post.data.dto.PostDto;
 import umc.spring.post.data.dto.PostResDto;
@@ -18,9 +15,10 @@ import umc.spring.post.repository.CommentRepository;
 import umc.spring.post.repository.PostRepository;
 import umc.spring.post.repository.UserRepository;
 
-import java.io.IOException;
 import java.util.*;
 
+import static umc.spring.post.config.security.SecurityUtil.getCurrentMemberId;
+
 @Service
 public class PostServiceImpl implements PostService{
 
@@ -30,23 +28,29 @@ public class PostServiceImpl implements PostService{
     @Autowired
     private final CommentRepository commentRepository;
 
-    public PostServiceImpl(PostRepository postRepository, CommentRepository commentRepository) {
+    @Autowired
+    private final UserRepository userRepository;
+
+    public PostServiceImpl(PostRepository postRepository, CommentRepository commentRepository, UserRepository userRepository) {
         this.postRepository = postRepository;
         this.commentRepository = commentRepository;
+        this.userRepository = userRepository;
     }
 
     @Override
     public void upload(PostDto postDto){
-        UserInfoDto userInfoDto = SecurityUtil.getCurrentMemberId();
-
+        UserInfoDto userInfoDto = getCurrentMemberId();
         Post post = new Post();
         setPost(postDto, post);
-        post.setUserId(postDto.getUserId());
+        post.setAuthor(userInfoDto.getUserName());
+        post.setUserId(userInfoDto.getUserId());
         post.setCreatedTime((new Date()));
         post.setModifiedTime(post.getCreatedTime());
+
         postRepository.save(post);
     }
 
+
     @Override
     public List<PostResDto> getAllPost(){
         List<Post> posts = postRepository.findAll();
@@ -64,7 +68,54 @@ public class PostServiceImpl implements PostService{
         Post post = postRepository.findById(id).orElseThrow(() -> new RuntimeException("id가 존재하지 않습니다."));;
         return PostResDto.toDTO(post);
     }
+    @Override
+    public boolean deletePost(Long id) {
+        // 토큰 받은 유저의 post인지 확인하기..
+        Optional<Post> byId = postRepository.findById(id);
+        if(byId.isPresent()){
+            UserInfoDto userInfoDto;
+            try {
+                userInfoDto = getCurrentMemberId();
+            }
+            catch(Exception e){
+                throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "token not found");
+            }
+            if(Objects.equals(byId.get().getUserId(), userInfoDto.getUserId())){
+                postRepository.deleteById(id);
+                return true;
+            }
+            throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "Unauthorized: You do not have permission to delete this post.");
+        }
+        else return false;
+    }
 
+    @Override
+    public boolean editPost(PostDto postDto, Long id) {
+        // 토큰 받은 유저의 post인지
+        Optional<Post> byId = postRepository.findById(id);
+
+        if(byId.isPresent()){
+            UserInfoDto userInfoDto;
+            try {
+                userInfoDto = getCurrentMemberId();
+            }
+            catch(Exception e){
+                throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "token not found");
+            }
+            Post post = byId.get();
+            if(Objects.equals(post.getUserId(), userInfoDto.getUserId())){
+                post.setTitle(postDto.getTitle() != null ? postDto.getTitle() : post.getTitle());
+                post.setBody(postDto.getBody() != null ? postDto.getBody() : post.getBody());
+                post.setImage(postDto.getImage() != null ? postDto.getImage() : post.getImage());
+                post.setModifiedTime(new Date());
+                postRepository.save(post);
+                return true;
+            }
+            throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "Unauthorized: You do not have permission to edit this post.");
+        }
+        else return false;
+
+    }
     @Override
     public void likeCrew(Long id) {
         Post post = postRepository.findById(id).orElseThrow(() -> new RuntimeException("id가 존재하지 않습니다."));
@@ -80,32 +131,10 @@ public class PostServiceImpl implements PostService{
         if(likeCount!=0){
             post.setLikeCount(--likeCount);
             postRepository.save(post);
-
         }
     }
 
-    @Override
-    public boolean deletePost(Long id) {
-        if(postRepository.findById(id).isPresent()){
-            postRepository.deleteById(id);
-            return true;
-        }
-        else return false;
-    }
-    
-    @Override
-    public boolean editPost(PostDto postDto, Long id) {
-        Post post = postRepository.findById(id).orElseThrow(() -> new RuntimeException("id가 존재하지 않습니다."));;
-        if(post!=null){
-            post.setTitle(postDto.getTitle() != null ? postDto.getTitle() : post.getTitle());
-            post.setBody(postDto.getBody() != null ? postDto.getBody() : post.getBody());
-            post.setImage(postDto.getImage() != null ? postDto.getImage() : post.getImage());
-            post.setModifiedTime(new Date());
-            postRepository.save(post);
-            return true;
-        }
-        else return false;
-    }
+
 
     @Override
     public List<PostResDto> search(String title) {
@@ -122,7 +151,11 @@ public class PostServiceImpl implements PostService{
 
     @Override
     public void addComment(CommentDto commentDto){
+        // 토큰 받은 유저가 우리 회원인지
+
+        UserInfoDto userInfoDto = getCurrentMemberId();
         Comment comment = setComment(commentDto);
+        comment.setUserId(userInfoDto.getUserId());
         commentRepository.save(comment);
     }
 
@@ -130,38 +163,46 @@ public class PostServiceImpl implements PostService{
     public boolean deleteComment(Long id) {
         Optional<Comment> option = commentRepository.findById(id);
         if(option.isPresent()){
+            UserInfoDto userInfoDto;
+            try {
+                 userInfoDto = getCurrentMemberId();
+            }
+            catch(Exception e){
+                throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "token not found");
+            }
             Comment comment = option.get();
-            Post post = comment.getPost();
-            if(post!=null){
-                post.getComments().removeIf(data ->
-                        data.getId().equals(id)
-                );
-                postRepository.save(post);
+            if(Objects.equals(userInfoDto.getUserId(), comment.getUserId())){
+                Post post = comment.getPost();
+                if(post!=null){
+                    post.getComments().removeIf(data ->
+                            data.getId().equals(id)
+                    );
+                    postRepository.save(post);
+                }
+                return true;
             }
-            return true;
+            throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "Unauthorized: You do not have permission to delete this comment.");
         }
         else return false;
     }
 
 
     private Comment setComment(CommentDto commentDto) {
+
         Comment comment = new Comment();
         Post post = postRepository.findById(commentDto.getPostId()).orElseThrow(() -> new RuntimeException("id가 존재하지 않습니다."));
-
         post.getComments().add(comment);
-        comment.setPost(post);
-        comment.setUserId(commentDto.getUserId());
-        comment.setTimestamp(new Date());
-        comment.setText(commentDto.getText());
-        comment.setAuthor(commentDto.getAuthor());
         comment.setPostId(commentDto.getPostId());
+        comment.setAuthor(commentDto.getAuthor());
+        comment.setText(commentDto.getText());
+        comment.setTimestamp(new Date());
         return comment;
+
     }
 
     private static void setPost(PostDto postDto, Post post) {
         post.setTitle(postDto.getTitle());
         post.setBody(postDto.getBody());
-        post.setAuthor(postDto.getAuthor());
         post.setLikeCount(postDto.getLikeCount());
         post.setImage(postDto.getImage());
     }
-- 
GitLab