From c34232798c2e11c9f27a992b8354edbd225e0609 Mon Sep 17 00:00:00 2001 From: Jonghak Choi <105853600+JongHak19@users.noreply.github.com> Date: Fri, 30 Jun 2023 12:27:05 +0900 Subject: [PATCH 1/9] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dd10ef6..df20356 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ http://localhost:8080/swagger-ui/index.html | 김남주 | 박상현 | 엄예진 | 최종학 | | :------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------: | | | | | | -| [cmsong111](https://github.com/cmsong111) | [thelight0804](https://github.com/thelight0804) | [yejin-e](https://github.com/yejin-e) | | +| [cmsong111](https://github.com/cmsong111) | [thelight0804](https://github.com/thelight0804) | [yejin-e](https://github.com/yejin-e) | [JongHak19](https://github.com/JongHak19) | ## 라이선스 From 9b151c585be5d917347027dc09186a2982bec8da Mon Sep 17 00:00:00 2001 From: master Date: Mon, 3 Jul 2023 03:53:52 +0900 Subject: [PATCH 2/9] =?UTF-8?q?user=20=EC=97=94=ED=8B=B0=ED=8B=B0=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/gdsc/blog/user/user.java | 49 ++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 src/main/java/com/gdsc/blog/user/user.java diff --git a/src/main/java/com/gdsc/blog/user/user.java b/src/main/java/com/gdsc/blog/user/user.java new file mode 100644 index 0000000..57d821d --- /dev/null +++ b/src/main/java/com/gdsc/blog/user/user.java @@ -0,0 +1,49 @@ +package com.gdsc.blog.user; + +public class user { + private String ID; + private String password; + private String address; + private String phone; + private String gender; + + public String getID() { + return ID; + } + + public void setID(String ID) { + this.ID = ID; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getGender() { + return gender; + } + + public void setGender(String gender) { + this.gender = gender; + } +} From ff5d1f5eda56bf63006f359c18f7ee7ecddcb5e2 Mon Sep 17 00:00:00 2001 From: master Date: Mon, 3 Jul 2023 10:01:54 +0900 Subject: [PATCH 3/9] =?UTF-8?q?user=20=EB=A0=88=ED=8C=8C=EC=A7=80=ED=86=A0?= =?UTF-8?q?=EB=A6=AC=20=EC=83=9D=EC=84=B1,=20=EB=B0=8F=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/gdsc/blog/SwaggerConfig.java | 24 +++++++++++++++++++ .../blog/user/{user.java => Customer.java} | 10 +++++--- .../java/com/gdsc/blog/user/UserService.java | 7 ++++++ .../com/gdsc/blog/user/userController.java | 10 ++++++++ .../com/gdsc/blog/user/userRepository.java | 7 ++++++ .../com/gdsc/blog/BlogApplicationTests.java | 14 ++++++++--- 6 files changed, 66 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/gdsc/blog/SwaggerConfig.java rename src/main/java/com/gdsc/blog/user/{user.java => Customer.java} (89%) create mode 100644 src/main/java/com/gdsc/blog/user/UserService.java create mode 100644 src/main/java/com/gdsc/blog/user/userController.java create mode 100644 src/main/java/com/gdsc/blog/user/userRepository.java diff --git a/src/main/java/com/gdsc/blog/SwaggerConfig.java b/src/main/java/com/gdsc/blog/SwaggerConfig.java new file mode 100644 index 0000000..c6e55c5 --- /dev/null +++ b/src/main/java/com/gdsc/blog/SwaggerConfig.java @@ -0,0 +1,24 @@ +package com.gdsc.blog; +import java.util.HashSet; +import java.util.Set; + +import org.springframework.context.annotation.Configuration; + +@Configuration + +public class SwaggerConfig { + + private Set getConsumeContentTypes() { + Set consumes = new HashSet<>(); + consumes.add("application/json;charset=UTF-8"); + consumes.add("application/x-www-form-urlencoded"); + return consumes; + } + + private Set getProduceContentTypes() { + Set produces = new HashSet<>(); + produces.add("application/json;charset=UTF-8"); + return produces; + } + +} \ No newline at end of file diff --git a/src/main/java/com/gdsc/blog/user/user.java b/src/main/java/com/gdsc/blog/user/Customer.java similarity index 89% rename from src/main/java/com/gdsc/blog/user/user.java rename to src/main/java/com/gdsc/blog/user/Customer.java index 57d821d..310b9bd 100644 --- a/src/main/java/com/gdsc/blog/user/user.java +++ b/src/main/java/com/gdsc/blog/user/Customer.java @@ -1,7 +1,13 @@ package com.gdsc.blog.user; -public class user { +import jakarta.persistence.Entity; +import jakarta.persistence.Id; + +@Entity +public class Customer { + @Id private String ID; + private String password; private String address; private String phone; @@ -10,11 +16,9 @@ public class user { public String getID() { return ID; } - public void setID(String ID) { this.ID = ID; } - public String getPassword() { return password; } diff --git a/src/main/java/com/gdsc/blog/user/UserService.java b/src/main/java/com/gdsc/blog/user/UserService.java new file mode 100644 index 0000000..ccc3442 --- /dev/null +++ b/src/main/java/com/gdsc/blog/user/UserService.java @@ -0,0 +1,7 @@ +package com.gdsc.blog.user; + +import org.springframework.stereotype.Service; + +@Service +public class UserService { +} diff --git a/src/main/java/com/gdsc/blog/user/userController.java b/src/main/java/com/gdsc/blog/user/userController.java new file mode 100644 index 0000000..b1d2aec --- /dev/null +++ b/src/main/java/com/gdsc/blog/user/userController.java @@ -0,0 +1,10 @@ +package com.gdsc.blog.user; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; + +@RequestMapping("/user") +@Controller +public class userController { + +} diff --git a/src/main/java/com/gdsc/blog/user/userRepository.java b/src/main/java/com/gdsc/blog/user/userRepository.java new file mode 100644 index 0000000..c1fa8ae --- /dev/null +++ b/src/main/java/com/gdsc/blog/user/userRepository.java @@ -0,0 +1,7 @@ +package com.gdsc.blog.user; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface userRepository extends JpaRepository { + +} diff --git a/src/test/java/com/gdsc/blog/BlogApplicationTests.java b/src/test/java/com/gdsc/blog/BlogApplicationTests.java index 533a031..86cfee0 100644 --- a/src/test/java/com/gdsc/blog/BlogApplicationTests.java +++ b/src/test/java/com/gdsc/blog/BlogApplicationTests.java @@ -1,14 +1,22 @@ package com.gdsc.blog; +import com.gdsc.blog.user.Customer; +import com.gdsc.blog.user.userRepository; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Profile; @SpringBootTest @Profile("test") class BlogApplicationTests { - + @Autowired + userRepository userRepository; @Test - void contextLoads() {} - + void testJoin() { + Customer user1 = new Customer(); + user1.setID("cj8556"); + user1.setPassword("test1234"); + this.userRepository.save(user1); + } } From 68a2ca99c3403a92f1b177a054792073b245ef23 Mon Sep 17 00:00:00 2001 From: master Date: Mon, 3 Jul 2023 11:45:51 +0900 Subject: [PATCH 4/9] =?UTF-8?q?=EC=8B=9C=ED=81=90=EB=A6=AC=ED=8B=B0=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/gdsc/blog/SecurityConfig.java | 20 +++++++ .../java/com/gdsc/blog/user/Customer.java | 53 ------------------- src/main/java/com/gdsc/blog/user/Member.java | 20 +++++++ .../com/gdsc/blog/user/MemberController.java | 18 +++++++ ...rRepository.java => MemberRepository.java} | 2 +- .../com/gdsc/blog/user/MemberService.java | 24 +++++++++ .../java/com/gdsc/blog/user/UserService.java | 7 --- .../com/gdsc/blog/user/userController.java | 10 ---- .../com/gdsc/blog/BlogApplicationTests.java | 23 ++++++-- 9 files changed, 102 insertions(+), 75 deletions(-) create mode 100644 src/main/java/com/gdsc/blog/SecurityConfig.java delete mode 100644 src/main/java/com/gdsc/blog/user/Customer.java create mode 100644 src/main/java/com/gdsc/blog/user/Member.java create mode 100644 src/main/java/com/gdsc/blog/user/MemberController.java rename src/main/java/com/gdsc/blog/user/{userRepository.java => MemberRepository.java} (62%) create mode 100644 src/main/java/com/gdsc/blog/user/MemberService.java delete mode 100644 src/main/java/com/gdsc/blog/user/UserService.java delete mode 100644 src/main/java/com/gdsc/blog/user/userController.java diff --git a/src/main/java/com/gdsc/blog/SecurityConfig.java b/src/main/java/com/gdsc/blog/SecurityConfig.java new file mode 100644 index 0000000..0caabde --- /dev/null +++ b/src/main/java/com/gdsc/blog/SecurityConfig.java @@ -0,0 +1,20 @@ +package com.gdsc.blog; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; + +@Configuration +@EnableWebSecurity +public class SecurityConfig { + @Bean + SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http.authorizeRequests().requestMatchers( //authorizeHttpRequests -> authorizeRequests + new AntPathRequestMatcher("/**")).permitAll() + ; + return http.build(); + } +} \ No newline at end of file diff --git a/src/main/java/com/gdsc/blog/user/Customer.java b/src/main/java/com/gdsc/blog/user/Customer.java deleted file mode 100644 index 310b9bd..0000000 --- a/src/main/java/com/gdsc/blog/user/Customer.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.gdsc.blog.user; - -import jakarta.persistence.Entity; -import jakarta.persistence.Id; - -@Entity -public class Customer { - @Id - private String ID; - - private String password; - private String address; - private String phone; - private String gender; - - public String getID() { - return ID; - } - public void setID(String ID) { - this.ID = ID; - } - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - public String getAddress() { - return address; - } - - public void setAddress(String address) { - this.address = address; - } - - public String getPhone() { - return phone; - } - - public void setPhone(String phone) { - this.phone = phone; - } - - public String getGender() { - return gender; - } - - public void setGender(String gender) { - this.gender = gender; - } -} diff --git a/src/main/java/com/gdsc/blog/user/Member.java b/src/main/java/com/gdsc/blog/user/Member.java new file mode 100644 index 0000000..9f0d157 --- /dev/null +++ b/src/main/java/com/gdsc/blog/user/Member.java @@ -0,0 +1,20 @@ +package com.gdsc.blog.user; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import lombok.Getter; +import lombok.Setter; + +@Entity +@Getter +@Setter +public class Member { + @Id + private String ID; + + private String password; + private String phone; + @Column(unique = true) + private String email; +} diff --git a/src/main/java/com/gdsc/blog/user/MemberController.java b/src/main/java/com/gdsc/blog/user/MemberController.java new file mode 100644 index 0000000..89a2fc1 --- /dev/null +++ b/src/main/java/com/gdsc/blog/user/MemberController.java @@ -0,0 +1,18 @@ +package com.gdsc.blog.user; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +@RequestMapping("/user") +@Controller +public class MemberController { + + @GetMapping("/login") + @ResponseBody + public String loginUI(){ + return "login 접근 성공"; + } + +} diff --git a/src/main/java/com/gdsc/blog/user/userRepository.java b/src/main/java/com/gdsc/blog/user/MemberRepository.java similarity index 62% rename from src/main/java/com/gdsc/blog/user/userRepository.java rename to src/main/java/com/gdsc/blog/user/MemberRepository.java index c1fa8ae..26cf022 100644 --- a/src/main/java/com/gdsc/blog/user/userRepository.java +++ b/src/main/java/com/gdsc/blog/user/MemberRepository.java @@ -2,6 +2,6 @@ import org.springframework.data.jpa.repository.JpaRepository; -public interface userRepository extends JpaRepository { +public interface MemberRepository extends JpaRepository { } diff --git a/src/main/java/com/gdsc/blog/user/MemberService.java b/src/main/java/com/gdsc/blog/user/MemberService.java new file mode 100644 index 0000000..ea2bac8 --- /dev/null +++ b/src/main/java/com/gdsc/blog/user/MemberService.java @@ -0,0 +1,24 @@ +package com.gdsc.blog.user; + +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.stereotype.Service; + +@Service +public class MemberService { + private final MemberRepository memberRepository; + + public MemberService(MemberRepository memberRepository) { + this.memberRepository = memberRepository; + } + + public Member create(String ID, String pwd, String phone, String email){ + Member member = new Member(); + member.setID(ID); + member.setPhone(phone); + member.setEmail(email); + BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + member.setPassword(passwordEncoder.encode(pwd)); + this.memberRepository.save(member); + return member; + } +} diff --git a/src/main/java/com/gdsc/blog/user/UserService.java b/src/main/java/com/gdsc/blog/user/UserService.java deleted file mode 100644 index ccc3442..0000000 --- a/src/main/java/com/gdsc/blog/user/UserService.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.gdsc.blog.user; - -import org.springframework.stereotype.Service; - -@Service -public class UserService { -} diff --git a/src/main/java/com/gdsc/blog/user/userController.java b/src/main/java/com/gdsc/blog/user/userController.java deleted file mode 100644 index b1d2aec..0000000 --- a/src/main/java/com/gdsc/blog/user/userController.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.gdsc.blog.user; - -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestMapping; - -@RequestMapping("/user") -@Controller -public class userController { - -} diff --git a/src/test/java/com/gdsc/blog/BlogApplicationTests.java b/src/test/java/com/gdsc/blog/BlogApplicationTests.java index 86cfee0..bc5a3a3 100644 --- a/src/test/java/com/gdsc/blog/BlogApplicationTests.java +++ b/src/test/java/com/gdsc/blog/BlogApplicationTests.java @@ -1,22 +1,37 @@ package com.gdsc.blog; -import com.gdsc.blog.user.Customer; -import com.gdsc.blog.user.userRepository; +import com.gdsc.blog.user.Member; +import com.gdsc.blog.user.MemberRepository; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Profile; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.assertEquals; + @SpringBootTest @Profile("test") class BlogApplicationTests { @Autowired - userRepository userRepository; + MemberRepository userRepository; @Test void testJoin() { - Customer user1 = new Customer(); + Member user1 = new Member(); user1.setID("cj8556"); user1.setPassword("test1234"); this.userRepository.save(user1); + + Optional oq = this.userRepository.findById("cj8556"); + if(oq.isPresent()) { + Member q = oq.get(); + assertEquals("cj8556", q.getID()); + } + } + + @Test + void testGetInformation() { + } } From 8eb7b80e47b00d89172ab45a7c499113c8a84823 Mon Sep 17 00:00:00 2001 From: master Date: Mon, 3 Jul 2023 15:14:00 +0900 Subject: [PATCH 5/9] =?UTF-8?q?jwt=20=ED=94=84=EB=A1=9C=EB=B0=94=EC=9D=B4?= =?UTF-8?q?=EB=8D=94=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=ED=9A=8C=EC=9B=90?= =?UTF-8?q?=EA=B0=80=EC=9E=85=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84=20?= =?UTF-8?q?-=20swagger=EC=97=90=EC=84=9C=20=ED=99=95=EC=9D=B8=20=EB=B6=88?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 4 ++ .../java/com/gdsc/blog/JwtTokenProvider.java | 44 +++++++++++++++++++ .../java/com/gdsc/blog/SecurityConfig.java | 7 +++ .../com/gdsc/blog/user/MemberController.java | 20 +++++++-- .../com/gdsc/blog/user/MemberRepository.java | 1 - .../com/gdsc/blog/user/MemberService.java | 29 ++++++++---- 6 files changed, 91 insertions(+), 14 deletions(-) create mode 100644 src/main/java/com/gdsc/blog/JwtTokenProvider.java diff --git a/build.gradle b/build.gradle index 58781da..2c6c90e 100644 --- a/build.gradle +++ b/build.gradle @@ -31,6 +31,10 @@ dependencies { testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.security:spring-security-test' implementation 'org.mariadb.jdbc:mariadb-java-client:3.1.4' + + //JWT + implementation 'io.jsonwebtoken:jjwt:0.9.1' //jjwt 의존성 추가 + implementation 'com.h2database:h2:2.1.214' // TODO : testimplementation으로 변경 } diff --git a/src/main/java/com/gdsc/blog/JwtTokenProvider.java b/src/main/java/com/gdsc/blog/JwtTokenProvider.java new file mode 100644 index 0000000..8dccbd8 --- /dev/null +++ b/src/main/java/com/gdsc/blog/JwtTokenProvider.java @@ -0,0 +1,44 @@ +package com.gdsc.blog; + +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import org.springframework.context.annotation.Bean; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Service; + +import java.util.Date; + +@Service +public class JwtTokenProvider { + + private static final long EXPIRATION_TIME = 864_000_000; // 10일 + private static final String secretKey = "user"; // 임의의 비밀키 문자열 + + public String generateToken(Authentication authentication) { + UserDetails userPrincipal = (UserDetails) authentication.getPrincipal(); + Date now = new Date(); + Date expiryDate = new Date(now.getTime() + EXPIRATION_TIME); + + return Jwts.builder() + .setSubject(userPrincipal.getUsername()) + .setIssuedAt(new Date()) + .setExpiration(expiryDate) + .signWith(SignatureAlgorithm.HS256, secretKey.getBytes()) + .compact(); + } + + public boolean validateToken(String token) { + try { + Jwts.parser().setSigningKey(secretKey.getBytes()).parseClaimsJws(token); + return true; + } catch (Exception e) { + return false; + } + } + + public String getUserIdFromJWT(String token) { + return Jwts.parser().setSigningKey(secretKey.getBytes()).parseClaimsJws(token).getBody().getSubject(); + } + +} diff --git a/src/main/java/com/gdsc/blog/SecurityConfig.java b/src/main/java/com/gdsc/blog/SecurityConfig.java index 0caabde..6d5f882 100644 --- a/src/main/java/com/gdsc/blog/SecurityConfig.java +++ b/src/main/java/com/gdsc/blog/SecurityConfig.java @@ -4,6 +4,8 @@ import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; @@ -17,4 +19,9 @@ SecurityFilterChain filterChain(HttpSecurity http) throws Exception { ; return http.build(); } + + @Bean + PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } } \ No newline at end of file diff --git a/src/main/java/com/gdsc/blog/user/MemberController.java b/src/main/java/com/gdsc/blog/user/MemberController.java index 89a2fc1..8fdae5b 100644 --- a/src/main/java/com/gdsc/blog/user/MemberController.java +++ b/src/main/java/com/gdsc/blog/user/MemberController.java @@ -1,18 +1,30 @@ package com.gdsc.blog.user; +import com.gdsc.blog.JwtTokenProvider; +import io.swagger.v3.oas.annotations.Operation; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.*; @RequestMapping("/user") @Controller public class MemberController { + private MemberService memberService; + private JwtTokenProvider tokenProvider; + + public MemberController(MemberService memberService, JwtTokenProvider tokenProvider) { + this.memberService = memberService; + this.tokenProvider = tokenProvider; + } + @GetMapping("/login") @ResponseBody public String loginUI(){ return "login 접근 성공"; } - + @PostMapping("/register") + public ResponseEntity register(@RequestBody Member member){ + return ResponseEntity.ok(memberService.register(member)); + } } diff --git a/src/main/java/com/gdsc/blog/user/MemberRepository.java b/src/main/java/com/gdsc/blog/user/MemberRepository.java index 26cf022..6d0e558 100644 --- a/src/main/java/com/gdsc/blog/user/MemberRepository.java +++ b/src/main/java/com/gdsc/blog/user/MemberRepository.java @@ -3,5 +3,4 @@ import org.springframework.data.jpa.repository.JpaRepository; public interface MemberRepository extends JpaRepository { - } diff --git a/src/main/java/com/gdsc/blog/user/MemberService.java b/src/main/java/com/gdsc/blog/user/MemberService.java index ea2bac8..e983f4a 100644 --- a/src/main/java/com/gdsc/blog/user/MemberService.java +++ b/src/main/java/com/gdsc/blog/user/MemberService.java @@ -1,24 +1,35 @@ package com.gdsc.blog.user; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; +import java.util.Optional; + @Service public class MemberService { private final MemberRepository memberRepository; + private final PasswordEncoder passwordEncoder; + - public MemberService(MemberRepository memberRepository) { + public MemberService(MemberRepository memberRepository, PasswordEncoder passwordEncoder) { this.memberRepository = memberRepository; + this.passwordEncoder = passwordEncoder; } - public Member create(String ID, String pwd, String phone, String email){ - Member member = new Member(); - member.setID(ID); - member.setPhone(phone); - member.setEmail(email); - BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); - member.setPassword(passwordEncoder.encode(pwd)); + public Member register(Member member){ + member.setID(member.getID()); + member.setPhone(member.getPhone()); + member.setEmail(member.getEmail()); + member.setPassword(passwordEncoder.encode(member.getPassword())); this.memberRepository.save(member); return member; } + public Member login(String ID, String pwd){ + Optional member = memberRepository.findById(ID); + if(member != null && passwordEncoder.matches(pwd, member.get().getPassword())){ + return member.get(); + } + else + return null; + } } From 192cf2c7ff9e830781c5acc941dc158b686baba8 Mon Sep 17 00:00:00 2001 From: master Date: Mon, 3 Jul 2023 17:36:37 +0900 Subject: [PATCH 6/9] =?UTF-8?q?swagger=20ui=EC=97=90=20login=EC=9D=80=20?= =?UTF-8?q?=ED=91=9C=EC=8B=9C=EB=90=98=EB=82=98=20register(=ED=9A=8C?= =?UTF-8?q?=EC=9B=90=EA=B0=80=EC=9E=85=20=ED=91=9C=EC=8B=9Cx)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 4 +++- .../java/com/gdsc/blog/SecurityConfig.java | 3 +-- .../java/com/gdsc/blog/SwaggerConfig.java | 24 ------------------- src/main/java/com/gdsc/blog/user/Member.java | 1 - .../com/gdsc/blog/user/MemberController.java | 19 +++++++++------ 5 files changed, 16 insertions(+), 35 deletions(-) delete mode 100644 src/main/java/com/gdsc/blog/SwaggerConfig.java diff --git a/build.gradle b/build.gradle index 2c6c90e..9548bf7 100644 --- a/build.gradle +++ b/build.gradle @@ -25,7 +25,9 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-web' + //swagger implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.1.0' + compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' @@ -34,7 +36,7 @@ dependencies { //JWT implementation 'io.jsonwebtoken:jjwt:0.9.1' //jjwt 의존성 추가 - + //implementation 'javax.xml.bind:jaxb-api:2.3.1' implementation 'com.h2database:h2:2.1.214' // TODO : testimplementation으로 변경 } diff --git a/src/main/java/com/gdsc/blog/SecurityConfig.java b/src/main/java/com/gdsc/blog/SecurityConfig.java index 6d5f882..e83633e 100644 --- a/src/main/java/com/gdsc/blog/SecurityConfig.java +++ b/src/main/java/com/gdsc/blog/SecurityConfig.java @@ -15,8 +15,7 @@ public class SecurityConfig { @Bean SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.authorizeRequests().requestMatchers( //authorizeHttpRequests -> authorizeRequests - new AntPathRequestMatcher("/**")).permitAll() - ; + new AntPathRequestMatcher("/**")).permitAll(); return http.build(); } diff --git a/src/main/java/com/gdsc/blog/SwaggerConfig.java b/src/main/java/com/gdsc/blog/SwaggerConfig.java deleted file mode 100644 index c6e55c5..0000000 --- a/src/main/java/com/gdsc/blog/SwaggerConfig.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.gdsc.blog; -import java.util.HashSet; -import java.util.Set; - -import org.springframework.context.annotation.Configuration; - -@Configuration - -public class SwaggerConfig { - - private Set getConsumeContentTypes() { - Set consumes = new HashSet<>(); - consumes.add("application/json;charset=UTF-8"); - consumes.add("application/x-www-form-urlencoded"); - return consumes; - } - - private Set getProduceContentTypes() { - Set produces = new HashSet<>(); - produces.add("application/json;charset=UTF-8"); - return produces; - } - -} \ No newline at end of file diff --git a/src/main/java/com/gdsc/blog/user/Member.java b/src/main/java/com/gdsc/blog/user/Member.java index 9f0d157..17c4d64 100644 --- a/src/main/java/com/gdsc/blog/user/Member.java +++ b/src/main/java/com/gdsc/blog/user/Member.java @@ -12,7 +12,6 @@ public class Member { @Id private String ID; - private String password; private String phone; @Column(unique = true) diff --git a/src/main/java/com/gdsc/blog/user/MemberController.java b/src/main/java/com/gdsc/blog/user/MemberController.java index 8fdae5b..7d62a51 100644 --- a/src/main/java/com/gdsc/blog/user/MemberController.java +++ b/src/main/java/com/gdsc/blog/user/MemberController.java @@ -1,7 +1,6 @@ package com.gdsc.blog.user; import com.gdsc.blog.JwtTokenProvider; -import io.swagger.v3.oas.annotations.Operation; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; @@ -12,19 +11,25 @@ public class MemberController { private MemberService memberService; private JwtTokenProvider tokenProvider; - public MemberController(MemberService memberService, JwtTokenProvider tokenProvider) { this.memberService = memberService; this.tokenProvider = tokenProvider; } - @GetMapping("/login") + @GetMapping("/login_ok") + @ResponseBody + public String login_ok(){ + return "로그인 성공"; + } + @PostMapping("/login") @ResponseBody - public String loginUI(){ - return "login 접근 성공"; + public String login(@RequestBody String ID, String pwd){ + memberService.login(ID, pwd); + return "redirect:/user/login_ok"; } @PostMapping("/register") - public ResponseEntity register(@RequestBody Member member){ - return ResponseEntity.ok(memberService.register(member)); + public String register(@RequestBody Member member){ + ResponseEntity.ok(memberService.register(member)); + return "redirect:/user/login"; } } From 1b54fa29bcc61446429a8fcaa918b480df47b17f Mon Sep 17 00:00:00 2001 From: master Date: Mon, 3 Jul 2023 17:50:26 +0900 Subject: [PATCH 7/9] =?UTF-8?q?swagger=20=EC=84=A4=EC=A0=95=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C(=EB=B3=B5=EC=9B=90)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/.gitignore | 8 ++++++++ .idea/.name | 1 + .idea/compiler.xml | 16 +++++++++++++++ .idea/jarRepositories.xml | 20 +++++++++++++++++++ .idea/misc.xml | 8 ++++++++ .idea/vcs.xml | 6 ++++++ build.gradle | 1 + .../com/gdsc/blog/BlogApplicationTests.java | 2 +- 8 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/.name create mode 100644 .idea/compiler.xml create mode 100644 .idea/jarRepositories.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/vcs.xml diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..0d50744 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +blog \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..2fc4ea3 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 0000000..fdc392f --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..de0c428 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/build.gradle b/build.gradle index 9548bf7..668d9e7 100644 --- a/build.gradle +++ b/build.gradle @@ -27,6 +27,7 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' //swagger implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.1.0' + //implementation 'io.springfox:springfox-boot-starter:3.0.0' //swagger로 회원가입 API(post)가 표시되게 하기위해 추가 compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' diff --git a/src/test/java/com/gdsc/blog/BlogApplicationTests.java b/src/test/java/com/gdsc/blog/BlogApplicationTests.java index bc5a3a3..a54af91 100644 --- a/src/test/java/com/gdsc/blog/BlogApplicationTests.java +++ b/src/test/java/com/gdsc/blog/BlogApplicationTests.java @@ -17,7 +17,7 @@ class BlogApplicationTests { @Autowired MemberRepository userRepository; @Test - void testJoin() { + void testJoin() { //회원가입 테스트 Member user1 = new Member(); user1.setID("cj8556"); user1.setPassword("test1234"); From f2738fa49b01cc46bf1d07c9ae2659a2898a4f13 Mon Sep 17 00:00:00 2001 From: master Date: Mon, 3 Jul 2023 20:34:03 +0900 Subject: [PATCH 8/9] =?UTF-8?q?security=20=EC=84=A4=EC=A0=95=20csrf=20?= =?UTF-8?q?=EB=B3=B4=ED=98=B8=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/gdsc/blog/SecurityConfig.java | 4 +++- src/main/java/com/gdsc/blog/user/MemberController.java | 5 +++-- src/main/java/com/gdsc/blog/user/MemberService.java | 2 +- src/test/java/com/gdsc/blog/BlogApplicationTests.java | 1 + 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/gdsc/blog/SecurityConfig.java b/src/main/java/com/gdsc/blog/SecurityConfig.java index e83633e..fcb259b 100644 --- a/src/main/java/com/gdsc/blog/SecurityConfig.java +++ b/src/main/java/com/gdsc/blog/SecurityConfig.java @@ -15,7 +15,9 @@ public class SecurityConfig { @Bean SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.authorizeRequests().requestMatchers( //authorizeHttpRequests -> authorizeRequests - new AntPathRequestMatcher("/**")).permitAll(); + new AntPathRequestMatcher("/**")).permitAll() + .and() + .csrf(csrf->csrf.disable()); return http.build(); } diff --git a/src/main/java/com/gdsc/blog/user/MemberController.java b/src/main/java/com/gdsc/blog/user/MemberController.java index 7d62a51..7a878c2 100644 --- a/src/main/java/com/gdsc/blog/user/MemberController.java +++ b/src/main/java/com/gdsc/blog/user/MemberController.java @@ -23,11 +23,12 @@ public String login_ok(){ } @PostMapping("/login") @ResponseBody - public String login(@RequestBody String ID, String pwd){ - memberService.login(ID, pwd); + public String login(@RequestBody Member member){ + memberService.login(member.getID(), member.getPassword()); return "redirect:/user/login_ok"; } @PostMapping("/register") + @ResponseBody public String register(@RequestBody Member member){ ResponseEntity.ok(memberService.register(member)); return "redirect:/user/login"; diff --git a/src/main/java/com/gdsc/blog/user/MemberService.java b/src/main/java/com/gdsc/blog/user/MemberService.java index e983f4a..55c00a2 100644 --- a/src/main/java/com/gdsc/blog/user/MemberService.java +++ b/src/main/java/com/gdsc/blog/user/MemberService.java @@ -8,7 +8,7 @@ @Service public class MemberService { private final MemberRepository memberRepository; - private final PasswordEncoder passwordEncoder; + private final PasswordEncoder passwordEncoder; //비밀번호 암호화 public MemberService(MemberRepository memberRepository, PasswordEncoder passwordEncoder) { diff --git a/src/test/java/com/gdsc/blog/BlogApplicationTests.java b/src/test/java/com/gdsc/blog/BlogApplicationTests.java index a54af91..4c1d341 100644 --- a/src/test/java/com/gdsc/blog/BlogApplicationTests.java +++ b/src/test/java/com/gdsc/blog/BlogApplicationTests.java @@ -27,6 +27,7 @@ void testJoin() { //회원가입 테스트 if(oq.isPresent()) { Member q = oq.get(); assertEquals("cj8556", q.getID()); + assertEquals("test1234",q.getPassword()); } } From a942884de578a805c79cfb4998cd14a00875f6d9 Mon Sep 17 00:00:00 2001 From: master Date: Mon, 10 Jul 2023 04:08:01 +0900 Subject: [PATCH 9/9] Article CRUD --- build.gradle | 4 +- .../java/com/gdsc/blog/JwtTokenProvider.java | 44 --------- .../java/com/gdsc/blog/SecurityConfig.java | 28 ------ .../article/controller/ArticleController.java | 92 +++++++++++++++++++ .../com/gdsc/blog/article/dto/CreateDto.java | 18 ++++ .../com/gdsc/blog/article/dto/UpdateDto.java | 18 ++++ .../com/gdsc/blog/article/entity/Article.java | 33 +++++++ .../article/repository/ArticleRepository.java | 8 ++ .../blog/article/service/ArticleService.java | 74 +++++++++++++++ .../blog/configuration/SecurityConfig.java | 3 +- .../blog/security/jwt/JwtTokenFilter.java | 16 +++- src/main/java/com/gdsc/blog/user/Member.java | 19 ---- .../com/gdsc/blog/user/MemberController.java | 36 -------- .../com/gdsc/blog/user/MemberRepository.java | 6 -- .../com/gdsc/blog/user/MemberService.java | 35 ------- .../blog/user/controller/UserController.java | 6 +- .../java/com/gdsc/blog/user/entity/User.java | 20 ++-- .../gdsc/blog/user/service/UserService.java | 41 +++++++-- .../com/gdsc/blog/BlogApplicationTests.java | 16 +--- 19 files changed, 303 insertions(+), 214 deletions(-) delete mode 100644 src/main/java/com/gdsc/blog/JwtTokenProvider.java delete mode 100644 src/main/java/com/gdsc/blog/SecurityConfig.java create mode 100644 src/main/java/com/gdsc/blog/article/controller/ArticleController.java create mode 100644 src/main/java/com/gdsc/blog/article/dto/CreateDto.java create mode 100644 src/main/java/com/gdsc/blog/article/dto/UpdateDto.java create mode 100644 src/main/java/com/gdsc/blog/article/entity/Article.java create mode 100644 src/main/java/com/gdsc/blog/article/repository/ArticleRepository.java create mode 100644 src/main/java/com/gdsc/blog/article/service/ArticleService.java delete mode 100644 src/main/java/com/gdsc/blog/user/Member.java delete mode 100644 src/main/java/com/gdsc/blog/user/MemberController.java delete mode 100644 src/main/java/com/gdsc/blog/user/MemberRepository.java delete mode 100644 src/main/java/com/gdsc/blog/user/MemberService.java diff --git a/build.gradle b/build.gradle index 1a15eef..d61a579 100644 --- a/build.gradle +++ b/build.gradle @@ -27,17 +27,15 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' //swagger implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.1.0' - //implementation 'io.springfox:springfox-boot-starter:3.0.0' //swagger로 회원가입 API(post)가 표시되게 하기위해 추가 compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.security:spring-security-test' implementation 'org.mariadb.jdbc:mariadb-java-client:3.1.4' - + implementation 'javax.xml.bind:jaxb-api:2.3.0' //javax.xml.bind.JAXBException 에러 방지용 //JWT implementation 'io.jsonwebtoken:jjwt:0.9.1' //jjwt 의존성 추가 - //implementation 'javax.xml.bind:jaxb-api:2.3.1' implementation 'com.h2database:h2:2.1.214' // TODO : testimplementation으로 변경 implementation 'io.jsonwebtoken:jjwt-api:0.11.2' diff --git a/src/main/java/com/gdsc/blog/JwtTokenProvider.java b/src/main/java/com/gdsc/blog/JwtTokenProvider.java deleted file mode 100644 index 8dccbd8..0000000 --- a/src/main/java/com/gdsc/blog/JwtTokenProvider.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.gdsc.blog; - -import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.SignatureAlgorithm; -import org.springframework.context.annotation.Bean; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.stereotype.Service; - -import java.util.Date; - -@Service -public class JwtTokenProvider { - - private static final long EXPIRATION_TIME = 864_000_000; // 10일 - private static final String secretKey = "user"; // 임의의 비밀키 문자열 - - public String generateToken(Authentication authentication) { - UserDetails userPrincipal = (UserDetails) authentication.getPrincipal(); - Date now = new Date(); - Date expiryDate = new Date(now.getTime() + EXPIRATION_TIME); - - return Jwts.builder() - .setSubject(userPrincipal.getUsername()) - .setIssuedAt(new Date()) - .setExpiration(expiryDate) - .signWith(SignatureAlgorithm.HS256, secretKey.getBytes()) - .compact(); - } - - public boolean validateToken(String token) { - try { - Jwts.parser().setSigningKey(secretKey.getBytes()).parseClaimsJws(token); - return true; - } catch (Exception e) { - return false; - } - } - - public String getUserIdFromJWT(String token) { - return Jwts.parser().setSigningKey(secretKey.getBytes()).parseClaimsJws(token).getBody().getSubject(); - } - -} diff --git a/src/main/java/com/gdsc/blog/SecurityConfig.java b/src/main/java/com/gdsc/blog/SecurityConfig.java deleted file mode 100644 index fcb259b..0000000 --- a/src/main/java/com/gdsc/blog/SecurityConfig.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.gdsc.blog; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.security.web.SecurityFilterChain; -import org.springframework.security.web.util.matcher.AntPathRequestMatcher; - -@Configuration -@EnableWebSecurity -public class SecurityConfig { - @Bean - SecurityFilterChain filterChain(HttpSecurity http) throws Exception { - http.authorizeRequests().requestMatchers( //authorizeHttpRequests -> authorizeRequests - new AntPathRequestMatcher("/**")).permitAll() - .and() - .csrf(csrf->csrf.disable()); - return http.build(); - } - - @Bean - PasswordEncoder passwordEncoder() { - return new BCryptPasswordEncoder(); - } -} \ No newline at end of file diff --git a/src/main/java/com/gdsc/blog/article/controller/ArticleController.java b/src/main/java/com/gdsc/blog/article/controller/ArticleController.java new file mode 100644 index 0000000..d10608f --- /dev/null +++ b/src/main/java/com/gdsc/blog/article/controller/ArticleController.java @@ -0,0 +1,92 @@ +package com.gdsc.blog.article.controller; + +import com.gdsc.blog.article.dto.CreateDto; +import com.gdsc.blog.article.dto.UpdateDto; +import com.gdsc.blog.article.entity.Article; +import com.gdsc.blog.article.service.ArticleService; +import com.gdsc.blog.user.entity.User; +import com.gdsc.blog.user.service.UserService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.crossstore.ChangeSetPersister; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.bind.annotation.*; + +import java.nio.file.AccessDeniedException; +import java.util.List; + +@RestController +@Slf4j +@Tag(name = "Article", description = "Article API") +@RequestMapping("/api/article") +public class ArticleController { + private final ArticleService articleService; //private final로 안넣은 이유?? + private final UserService userService; + + public ArticleController(ArticleService articleService, UserService userService){ + this.articleService = articleService; + this.userService = userService; + } + + @PostMapping("/create") + @Operation(summary = "게시글 생성") + @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_CLIENT')") + + public Article create(@RequestBody CreateDto article) { + + Article article1 = Article.builder().subject(article.getSubject()).content(article.getContent()).build(); + + //현재 인증된 사용자 가져오기 + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String username = authentication.getName(); // 현재 인증된 사용자의 이름(아이디)를 가져온다. + + User user = userService.getUserByName(username); // DB에서 사용자를 조회합니다. + return articleService.create(article1, user); + } + @GetMapping("/myallarticle") //내 게시글 목록 + @Operation(summary = "내 게시글 목록") + @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_CLIENT')") + public List
myallarticle(HttpServletRequest req) { + User user = userService.whoami(req); + return articleService.myAllArticle(user); + } + + @GetMapping("article/{id}") // 특정 게시글 + @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_CLIENT')") + @Operation(summary = "특정 게시글") + public Article getarticle(HttpServletRequest req, @PathVariable("id") Long id){ + return articleService.getArticle(id); + } + + @GetMapping("/allarticle") // 모든 게시글 조회 + @Operation(summary = "모든 게시글 조회 ") + @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_CLIENT')") + public List
allarticle(HttpServletRequest req) { + return articleService.getAllAriticle(); + } + + @PutMapping("/update/{id}") + @Operation(summary = "게시글 업데이트") + @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_CLIENT')") + public Article updateArticle(@PathVariable("id") Long id, @RequestBody UpdateDto article, HttpServletRequest req) throws ChangeSetPersister.NotFoundException, AccessDeniedException { + // 현재 인증된 사용자 가져오기 + User user = userService.whoami(req); + Article updatedArticle = articleService.updateArticle(id, article, user); + return updatedArticle; + } + + @DeleteMapping("/delete/{id}") + @Operation(summary = "게시글 삭제") + @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_CLIENT')") + public ResponseEntity deleteArticle(@PathVariable("id") Long id, HttpServletRequest req) throws ChangeSetPersister.NotFoundException, AccessDeniedException { + // 현재 인증된 사용자 가져오기 + User user = userService.whoami(req); + articleService.deleteArticle(id, user); + return ResponseEntity.ok().build(); + } +} diff --git a/src/main/java/com/gdsc/blog/article/dto/CreateDto.java b/src/main/java/com/gdsc/blog/article/dto/CreateDto.java new file mode 100644 index 0000000..b16a056 --- /dev/null +++ b/src/main/java/com/gdsc/blog/article/dto/CreateDto.java @@ -0,0 +1,18 @@ +package com.gdsc.blog.article.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class CreateDto { + @Schema(description = "제목", example = "첫 게시글 입니다.") + public String subject; + + @Schema(description = "내용", example = "안녕하세요.") + public String content; +} diff --git a/src/main/java/com/gdsc/blog/article/dto/UpdateDto.java b/src/main/java/com/gdsc/blog/article/dto/UpdateDto.java new file mode 100644 index 0000000..1de63cf --- /dev/null +++ b/src/main/java/com/gdsc/blog/article/dto/UpdateDto.java @@ -0,0 +1,18 @@ +package com.gdsc.blog.article.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class UpdateDto { + @Schema(description = "제목", example = "수정될 제목") + public String subject; + + @Schema(description = "내용", example = "수정될 내용") + public String content; +} \ No newline at end of file diff --git a/src/main/java/com/gdsc/blog/article/entity/Article.java b/src/main/java/com/gdsc/blog/article/entity/Article.java new file mode 100644 index 0000000..cfcce5d --- /dev/null +++ b/src/main/java/com/gdsc/blog/article/entity/Article.java @@ -0,0 +1,33 @@ +package com.gdsc.blog.article.entity; + +import com.fasterxml.jackson.annotation.JsonBackReference; +import com.gdsc.blog.user.entity.User; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@Entity +@Data +@Builder //빌더 패턴을 사용 +@AllArgsConstructor //모든 필드를 매개변수로 받는 생성자 +@NoArgsConstructor //매개변수가 없는 기본 생성자 +public class Article { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long idx; + + private String subject; + + private String content; + + private LocalDateTime createDate; + + @JsonBackReference + @ManyToOne + private User user; +} diff --git a/src/main/java/com/gdsc/blog/article/repository/ArticleRepository.java b/src/main/java/com/gdsc/blog/article/repository/ArticleRepository.java new file mode 100644 index 0000000..0fbfb74 --- /dev/null +++ b/src/main/java/com/gdsc/blog/article/repository/ArticleRepository.java @@ -0,0 +1,8 @@ +package com.gdsc.blog.article.repository; + +import com.gdsc.blog.article.entity.Article; +import org.springframework.data.jpa.repository.JpaRepository; + + +public interface ArticleRepository extends JpaRepository { +} diff --git a/src/main/java/com/gdsc/blog/article/service/ArticleService.java b/src/main/java/com/gdsc/blog/article/service/ArticleService.java new file mode 100644 index 0000000..2615328 --- /dev/null +++ b/src/main/java/com/gdsc/blog/article/service/ArticleService.java @@ -0,0 +1,74 @@ +package com.gdsc.blog.article.service; + +import com.gdsc.blog.article.dto.UpdateDto; +import com.gdsc.blog.article.entity.Article; +import com.gdsc.blog.article.repository.ArticleRepository; +import com.gdsc.blog.user.entity.User; +import com.gdsc.blog.user.service.UserService; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.time.LocalDateTime; +import java.util.List; + +@RequiredArgsConstructor +@Service +@Slf4j +public class ArticleService { + + private final ArticleRepository articleRepository; + + public Article create(Article article, User user){ + article.setCreateDate(LocalDateTime.now()); + article.setUser(user); + return articleRepository.save(article); + } + + public Article getArticle(Long id){ + List
Allarticle = articleRepository.findAll(); + + for(Article article: Allarticle){ + if(article.getIdx() == id){ + return article; + } + } + throw new RuntimeException("No article"); + } + + public List
myAllArticle(User user){ + return user.getArticleList(); + } + + public List
getAllAriticle(){ + return articleRepository.findAll(); + } + + public Article updateArticle(Long idx, UpdateDto newarticle, User user){ + List
Allarticle = articleRepository.findAll(); + + for(Article article: Allarticle){ + if(article.getIdx() == idx){ + article.setSubject(newarticle.getSubject()); + article.setContent(newarticle.getContent()); + return articleRepository.save(article); + } + } + throw new RuntimeException("No article"); + } + + public void deleteArticle(Long id, User user){ + // find article by id + Article article = articleRepository.findById(id) + .orElseThrow(() -> new RuntimeException("Article not found")); + + // check if the logged-in user is the owner of the article + if (!article.getUser().getIdx().equals(user.getIdx())) { + throw new RuntimeException("Permission denied"); + } + + // delete the article + articleRepository.delete(article); + } +} diff --git a/src/main/java/com/gdsc/blog/configuration/SecurityConfig.java b/src/main/java/com/gdsc/blog/configuration/SecurityConfig.java index d194f30..9acd9ba 100644 --- a/src/main/java/com/gdsc/blog/configuration/SecurityConfig.java +++ b/src/main/java/com/gdsc/blog/configuration/SecurityConfig.java @@ -31,10 +31,9 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti "/v3/api-docs/**", "/api/user/signin", "/api/user/signup") .permitAll().anyRequest().authenticated(); - http.csrf().disable(); + http.csrf(csrf->csrf.disable()); http.apply(new JwtTokenFilterConfigurer(jwtTokenProvider)); - return http.build(); } } diff --git a/src/main/java/com/gdsc/blog/security/jwt/JwtTokenFilter.java b/src/main/java/com/gdsc/blog/security/jwt/JwtTokenFilter.java index 31e0c3d..e82448a 100644 --- a/src/main/java/com/gdsc/blog/security/jwt/JwtTokenFilter.java +++ b/src/main/java/com/gdsc/blog/security/jwt/JwtTokenFilter.java @@ -4,6 +4,7 @@ import java.io.IOException; +import lombok.extern.slf4j.Slf4j; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; @@ -16,6 +17,7 @@ // We should use OncePerRequestFilter since we are doing a database call, there is no point in doing // this more than once +@Slf4j public class JwtTokenFilter extends OncePerRequestFilter { private JwtTokenProvider jwtTokenProvider; @@ -25,23 +27,29 @@ public JwtTokenFilter(JwtTokenProvider jwtTokenProvider) { } @Override + protected void doFilterInternal(HttpServletRequest httpServletRequest, - HttpServletResponse httpServletResponse, FilterChain filterChain) - throws ServletException, IOException { + HttpServletResponse httpServletResponse, FilterChain filterChain) + throws ServletException, IOException { + logger.info("doFilterInternal 시작"); String token = jwtTokenProvider.resolveToken(httpServletRequest); try { + logger.info("토큰 확인: " + token); if (token != null && jwtTokenProvider.validateToken(token)) { + logger.info("토큰 검증 통과"); Authentication auth = jwtTokenProvider.getAuthentication(token); SecurityContextHolder.getContext().setAuthentication(auth); } } catch (Exception ex) { - // this is very important, since it guarantees the user is not authenticated at all + logger.error("예외 발생: " + ex.getMessage()); SecurityContextHolder.clearContext(); httpServletResponse.sendError(HttpServletResponse.SC_BAD_REQUEST, ex.getMessage()); + logger.info("sendError 호출 후"); return; } - + logger.info("filterChain.doFilter 호출 전"); filterChain.doFilter(httpServletRequest, httpServletResponse); + logger.info("filterChain.doFilter 호출 후"); } } diff --git a/src/main/java/com/gdsc/blog/user/Member.java b/src/main/java/com/gdsc/blog/user/Member.java deleted file mode 100644 index 17c4d64..0000000 --- a/src/main/java/com/gdsc/blog/user/Member.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.gdsc.blog.user; - -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import lombok.Getter; -import lombok.Setter; - -@Entity -@Getter -@Setter -public class Member { - @Id - private String ID; - private String password; - private String phone; - @Column(unique = true) - private String email; -} diff --git a/src/main/java/com/gdsc/blog/user/MemberController.java b/src/main/java/com/gdsc/blog/user/MemberController.java deleted file mode 100644 index 7a878c2..0000000 --- a/src/main/java/com/gdsc/blog/user/MemberController.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.gdsc.blog.user; - -import com.gdsc.blog.JwtTokenProvider; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.*; - -@RequestMapping("/user") -@Controller -public class MemberController { - - private MemberService memberService; - private JwtTokenProvider tokenProvider; - public MemberController(MemberService memberService, JwtTokenProvider tokenProvider) { - this.memberService = memberService; - this.tokenProvider = tokenProvider; - } - - @GetMapping("/login_ok") - @ResponseBody - public String login_ok(){ - return "로그인 성공"; - } - @PostMapping("/login") - @ResponseBody - public String login(@RequestBody Member member){ - memberService.login(member.getID(), member.getPassword()); - return "redirect:/user/login_ok"; - } - @PostMapping("/register") - @ResponseBody - public String register(@RequestBody Member member){ - ResponseEntity.ok(memberService.register(member)); - return "redirect:/user/login"; - } -} diff --git a/src/main/java/com/gdsc/blog/user/MemberRepository.java b/src/main/java/com/gdsc/blog/user/MemberRepository.java deleted file mode 100644 index 6d0e558..0000000 --- a/src/main/java/com/gdsc/blog/user/MemberRepository.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.gdsc.blog.user; - -import org.springframework.data.jpa.repository.JpaRepository; - -public interface MemberRepository extends JpaRepository { -} diff --git a/src/main/java/com/gdsc/blog/user/MemberService.java b/src/main/java/com/gdsc/blog/user/MemberService.java deleted file mode 100644 index 55c00a2..0000000 --- a/src/main/java/com/gdsc/blog/user/MemberService.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.gdsc.blog.user; - -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.stereotype.Service; - -import java.util.Optional; - -@Service -public class MemberService { - private final MemberRepository memberRepository; - private final PasswordEncoder passwordEncoder; //비밀번호 암호화 - - - public MemberService(MemberRepository memberRepository, PasswordEncoder passwordEncoder) { - this.memberRepository = memberRepository; - this.passwordEncoder = passwordEncoder; - } - - public Member register(Member member){ - member.setID(member.getID()); - member.setPhone(member.getPhone()); - member.setEmail(member.getEmail()); - member.setPassword(passwordEncoder.encode(member.getPassword())); - this.memberRepository.save(member); - return member; - } - public Member login(String ID, String pwd){ - Optional member = memberRepository.findById(ID); - if(member != null && passwordEncoder.matches(pwd, member.get().getPassword())){ - return member.get(); - } - else - return null; - } -} diff --git a/src/main/java/com/gdsc/blog/user/controller/UserController.java b/src/main/java/com/gdsc/blog/user/controller/UserController.java index 463a5d9..bf29c7a 100644 --- a/src/main/java/com/gdsc/blog/user/controller/UserController.java +++ b/src/main/java/com/gdsc/blog/user/controller/UserController.java @@ -1,7 +1,5 @@ package com.gdsc.blog.user.controller; -import java.util.HashSet; -import java.util.Set; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -18,7 +16,6 @@ import com.gdsc.blog.user.dto.SignUpDto; import com.gdsc.blog.user.dto.Token; import com.gdsc.blog.user.entity.User; -import com.gdsc.blog.user.entity.UserRole; @Slf4j @Tag(name = "User", description = "User API") @@ -49,13 +46,12 @@ public Token signin(@RequestBody SignInDto signInDto) { Token token = Token.builder() .token(userService.singin(signInDto.getEmail(), signInDto.getPassword())).build(); return token; - } @GetMapping("/getUser/{email}") @PreAuthorize("hasRole('ROLE_ADMIN')") public User getUser(@PathVariable("email") String email) { - return userService.getUser(email); + return userService.getUserByEmail(email); } @GetMapping("/whoisme") diff --git a/src/main/java/com/gdsc/blog/user/entity/User.java b/src/main/java/com/gdsc/blog/user/entity/User.java index 28cddab..df6e8e1 100644 --- a/src/main/java/com/gdsc/blog/user/entity/User.java +++ b/src/main/java/com/gdsc/blog/user/entity/User.java @@ -4,19 +4,14 @@ import java.util.List; import java.util.Set; import java.util.stream.Collectors; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonManagedReference; +import com.gdsc.blog.article.entity.Article; +import jakarta.persistence.*; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; -import jakarta.persistence.Column; -import jakarta.persistence.ElementCollection; -import jakarta.persistence.Entity; -import jakarta.persistence.EnumType; -import jakarta.persistence.Enumerated; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.Table; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -27,7 +22,7 @@ @Builder @AllArgsConstructor @NoArgsConstructor -@Table(name = "users") +@Table(name = "users") //데이터 베이스에 존재하는 user랑 안겹치게? public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -43,4 +38,7 @@ public class User { @ElementCollection(fetch = FetchType.EAGER) List roles; + @JsonManagedReference + @OneToMany(mappedBy = "user", cascade = CascadeType.REMOVE) + private List
articleList; } diff --git a/src/main/java/com/gdsc/blog/user/service/UserService.java b/src/main/java/com/gdsc/blog/user/service/UserService.java index 3160448..6fb9f61 100644 --- a/src/main/java/com/gdsc/blog/user/service/UserService.java +++ b/src/main/java/com/gdsc/blog/user/service/UserService.java @@ -1,12 +1,8 @@ package com.gdsc.blog.user.service; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; -import java.util.Optional; -import java.util.Set; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.authentication.AuthenticationManager; + import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import com.gdsc.blog.security.jwt.JwtTokenProvider; @@ -57,8 +53,12 @@ public User signup(User user) { return userRepository.save(user); } - public User getUser(String username) { - return userRepository.findByEmail(username).get(); + public User getUserByEmail(String useremail) { + return userRepository.findByEmail(useremail).get(); + } + + public User getUserByName(String username){ + return userRepository.findByUsername(username).get(); } public User whoami(HttpServletRequest req) { @@ -67,5 +67,30 @@ public User whoami(HttpServletRequest req) { .findByUsername(jwtTokenProvider.getUsername(jwtTokenProvider.resolveToken(req))) .get(); } - + /////////////////////////admin 유저 생성///////////////// +// @EventListener +// public void createAdminAccount(ApplicationReadyEvent event) { +// // 관리자 계정 정보 +// String adminEmail = "admin"; +// String adminPassword = "admin"; +// String adminUsername = "admin"; +// +// // 이미 관리자 계정이 존재하는지 확인 +// if (userRepository.existsByEmail(adminEmail)) { +// return; // 이미 관리자 계정이 존재하면 생성하지 않음 +// } +// +// // 관리자 계정 생성 +// User adminUser = new User(); +// adminUser.setEmail(adminEmail); +// adminUser.setPassword(passwordEncoder.encode(adminPassword)); +// adminUser.setUsername(adminUsername); +// // admin 역할 부여 +// if (adminUser.getRoles() == null) { +// List set = new ArrayList(); +// set.add(UserRole.ROLE_ADMIN); +// adminUser.setRoles(set); +// } +// userRepository.save(adminUser); +// } } diff --git a/src/test/java/com/gdsc/blog/BlogApplicationTests.java b/src/test/java/com/gdsc/blog/BlogApplicationTests.java index 4c1d341..fb65d4a 100644 --- a/src/test/java/com/gdsc/blog/BlogApplicationTests.java +++ b/src/test/java/com/gdsc/blog/BlogApplicationTests.java @@ -1,7 +1,7 @@ package com.gdsc.blog; -import com.gdsc.blog.user.Member; -import com.gdsc.blog.user.MemberRepository; +import com.gdsc.blog.user.entity.User; +import com.gdsc.blog.user.repository.UserRepository; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @@ -15,20 +15,10 @@ @Profile("test") class BlogApplicationTests { @Autowired - MemberRepository userRepository; + UserRepository userRepository; @Test void testJoin() { //회원가입 테스트 - Member user1 = new Member(); - user1.setID("cj8556"); - user1.setPassword("test1234"); - this.userRepository.save(user1); - Optional oq = this.userRepository.findById("cj8556"); - if(oq.isPresent()) { - Member q = oq.get(); - assertEquals("cj8556", q.getID()); - assertEquals("test1234",q.getPassword()); - } } @Test