diff --git a/build.gradle b/build.gradle index 602dd89..377bf63 100644 --- a/build.gradle +++ b/build.gradle @@ -1,3 +1,4 @@ + plugins { id 'org.springframework.boot' version '2.4.2' id 'io.spring.dependency-management' version '1.0.11.RELEASE' @@ -14,7 +15,24 @@ repositories { dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.mariadb.jdbc:mariadb-java-client' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + + //db - mysql + runtimeOnly 'mysql:mysql-connector-java' + + //security + implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'io.jsonwebtoken:jjwt:0.9.1' + + compileOnly 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' + + testImplementation('org.springframework.boot:spring-boot-starter-test') { + exclude group: 'org.junit.vintage', module: 'junit-vintage-engine' + } testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation 'org.springframework.security:spring-security-test' } test { diff --git a/src/main/java/com/backdev/happy/wblserver/bakery/controller/BakeryController.java b/src/main/java/com/backdev/happy/wblserver/bakery/controller/BakeryController.java new file mode 100644 index 0000000..38d9a02 --- /dev/null +++ b/src/main/java/com/backdev/happy/wblserver/bakery/controller/BakeryController.java @@ -0,0 +1,27 @@ +package com.backdev.happy.wblserver.bakery.controller; + +import com.backdev.happy.wblserver.bakery.domain.Bakery; +import com.backdev.happy.wblserver.bakery.dto.BakeryDto; +import com.backdev.happy.wblserver.bakery.repository.BakeryRepository; +import com.backdev.happy.wblserver.bakery.service.BakeryManageService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/bakeries") +@RequiredArgsConstructor +public class BakeryController { + + private final BakeryManageService bakeryManageService; + + @PostMapping + public ResponseEntity createBakery(@RequestBody BakeryDto.CreateRequest request) { + final Bakery bakery = bakeryManageService.createBakery(request); + return new ResponseEntity<>("success to create bakery: "+bakery.toString(), HttpStatus.OK); + } +} diff --git a/src/main/java/com/backdev/happy/wblserver/bakery/domain/Bakery.java b/src/main/java/com/backdev/happy/wblserver/bakery/domain/Bakery.java new file mode 100644 index 0000000..7103037 --- /dev/null +++ b/src/main/java/com/backdev/happy/wblserver/bakery/domain/Bakery.java @@ -0,0 +1,54 @@ +package com.backdev.happy.wblserver.bakery.domain; + +import com.backdev.happy.wblserver.global.config.security.UserRole; +import com.backdev.happy.wblserver.member.domain.Member; +import lombok.*; +import org.hibernate.annotations.CreationTimestamp; +import org.hibernate.annotations.UpdateTimestamp; + +import javax.persistence.*; +import java.util.Date; + +@Getter +@ToString(of = {"member", "name"}) +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Table(name = "bakeries") +@Entity +public class Bakery { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @OneToOne //(cascade={CascadeType.ALL}) + @JoinColumn(name = "member_id", referencedColumnName = "id") + private Member member; // 외래키가 있는 곳이 연관관계 주인 -> 하나의 member는 여러 개의 bakery를 가질 수 있으므로(one to many 관계) 주인을 seller로 설정 + + @Column(nullable = false, length = 45) + private String name; + + @Column(nullable = false, length = 255) + private String img; + + @Column(nullable = false, length = 45) + private String address; + + @Column(columnDefinition = "TEXT") + private String introduction; + + @CreationTimestamp + @Column(name = "create_time") + private Date createTime; + + @UpdateTimestamp + @Column(name = "update_time") + private Date updateTime; + + @Builder + public Bakery(final Long memberId, final String name, final String img, final String address, final String introduction){ + this.member = new Member(memberId); + this.name = name; + this.img = img; + this.address = address; + this.introduction = introduction; + } +} diff --git a/src/main/java/com/backdev/happy/wblserver/bakery/dto/BakeryDto.java b/src/main/java/com/backdev/happy/wblserver/bakery/dto/BakeryDto.java new file mode 100644 index 0000000..16f8726 --- /dev/null +++ b/src/main/java/com/backdev/happy/wblserver/bakery/dto/BakeryDto.java @@ -0,0 +1,41 @@ +package com.backdev.happy.wblserver.bakery.dto; + +import com.backdev.happy.wblserver.bakery.domain.Bakery; +import com.backdev.happy.wblserver.global.config.security.UserRole; +import com.backdev.happy.wblserver.member.domain.Member; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.springframework.security.crypto.password.PasswordEncoder; + +public class BakeryDto { + + @Getter + @NoArgsConstructor + public static class CreateRequest { + private Long memberId; + private String name; + private String img; + private String address; + private String introduction; + + @Builder + public CreateRequest(Long memberId, String name, String img, String address, String introduction){ + this.memberId = memberId; + this.name = name; + this.img = img; + this.address = address; + this.introduction = introduction; + } + + public Bakery toEntity(){ + return Bakery.builder() + .memberId(memberId) + .name(name) + .img(img) + .address(address) + .introduction(introduction) + .build(); + } + } +} diff --git a/src/main/java/com/backdev/happy/wblserver/bakery/repository/BakeryRepository.java b/src/main/java/com/backdev/happy/wblserver/bakery/repository/BakeryRepository.java new file mode 100644 index 0000000..4cd0c80 --- /dev/null +++ b/src/main/java/com/backdev/happy/wblserver/bakery/repository/BakeryRepository.java @@ -0,0 +1,8 @@ +package com.backdev.happy.wblserver.bakery.repository; + +import com.backdev.happy.wblserver.bakery.domain.Bakery; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface BakeryRepository extends JpaRepository { + +} diff --git a/src/main/java/com/backdev/happy/wblserver/bakery/service/BakeryManageService.java b/src/main/java/com/backdev/happy/wblserver/bakery/service/BakeryManageService.java new file mode 100644 index 0000000..e34396d --- /dev/null +++ b/src/main/java/com/backdev/happy/wblserver/bakery/service/BakeryManageService.java @@ -0,0 +1,9 @@ +package com.backdev.happy.wblserver.bakery.service; + +import com.backdev.happy.wblserver.bakery.domain.Bakery; +import com.backdev.happy.wblserver.bakery.dto.BakeryDto; +import com.backdev.happy.wblserver.bakery.repository.BakeryRepository; + +public interface BakeryManageService { + Bakery createBakery(final BakeryDto.CreateRequest dto); +} diff --git a/src/main/java/com/backdev/happy/wblserver/bakery/service/impl/BakeryManageServiceImpl.java b/src/main/java/com/backdev/happy/wblserver/bakery/service/impl/BakeryManageServiceImpl.java new file mode 100644 index 0000000..9abd4a6 --- /dev/null +++ b/src/main/java/com/backdev/happy/wblserver/bakery/service/impl/BakeryManageServiceImpl.java @@ -0,0 +1,20 @@ +package com.backdev.happy.wblserver.bakery.service.impl; + +import com.backdev.happy.wblserver.bakery.domain.Bakery; +import com.backdev.happy.wblserver.bakery.dto.BakeryDto; +import com.backdev.happy.wblserver.bakery.repository.BakeryRepository; +import com.backdev.happy.wblserver.bakery.service.BakeryManageService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class BakeryManageServiceImpl implements BakeryManageService { + + private final BakeryRepository bakeryRepository; + + @Override + public Bakery createBakery(BakeryDto.CreateRequest dto) { + return bakeryRepository.save(dto.toEntity()); + } +} diff --git a/src/main/java/com/backdev/happy/wblserver/global/config/security/SecurityConfiguration.java b/src/main/java/com/backdev/happy/wblserver/global/config/security/SecurityConfiguration.java new file mode 100644 index 0000000..d2876b7 --- /dev/null +++ b/src/main/java/com/backdev/happy/wblserver/global/config/security/SecurityConfiguration.java @@ -0,0 +1,57 @@ +package com.backdev.happy.wblserver.global.config.security; + + +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.builders.WebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.crypto.factory.PasswordEncoderFactories; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +@RequiredArgsConstructor +@Configuration +public class SecurityConfiguration extends WebSecurityConfigurerAdapter { + + private final JwtTokenProvider jwtTokenProvider; + + @Bean + @Override + public AuthenticationManager authenticationManagerBean() throws Exception { + return super.authenticationManagerBean(); + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .httpBasic().disable() // rest api 이므로 기본설정 사용안함. 기본설정은 비인증시 로그인폼 화면으로 리다이렉트 된다. + .csrf().disable() // rest api이므로 csrf 보안이 필요없으므로 disable처리. + .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) // jwt token으로 인증하므로 세션은 필요없으므로 생성안함. + .and() + .authorizeRequests() // 다음 리퀘스트에 대한 사용권한 체크 + .antMatchers("/members/signin", "members/signup").permitAll() // 가입 및 인증 주소는 누구나 접근가능 + .antMatchers("/bakeries/**","/members/*/hello").hasRole("SELLER") + //.anyRequest().hasRole("CUSTOMER") // 그외 나머지 요청은 모두 인증된 회원만 접근 가능 + .and() + .addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider), UsernamePasswordAuthenticationFilter.class); // jwt token 필터를 id/password 인증 필터 전에 넣는다 + + } + + @Override // ignore check swagger resource + public void configure(WebSecurity web) { +// web.ignoring().antMatchers("/v2/api-docs", "/swagger-resources/**", +// "/swagger-ui.html", "/webjars/**", "/swagger/**"); + + } + + @Bean + public PasswordEncoder passwordEncoder(){ + return PasswordEncoderFactories.createDelegatingPasswordEncoder(); + } + +} \ No newline at end of file diff --git a/src/main/java/com/backdev/happy/wblserver/global/config/security/UserRole.java b/src/main/java/com/backdev/happy/wblserver/global/config/security/UserRole.java new file mode 100644 index 0000000..e204d15 --- /dev/null +++ b/src/main/java/com/backdev/happy/wblserver/global/config/security/UserRole.java @@ -0,0 +1,25 @@ +package com.backdev.happy.wblserver.global.config.security; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +@AllArgsConstructor +@Getter +public enum UserRole { + USER("ROLE_USER"), + ADMIN("ROLE_ADMIN"), + CUSTOMER("ROLE_CUSTOMER"), + SELLER("ROLE_SELLER"); + + private String value; + + public static UserRole findByString(String val) { + return Arrays.stream(values()) + .filter(v -> val.equals(v.value)) + .findFirst() + .orElseThrow(() -> new RuntimeException(val + " is not supported")); + } + +} diff --git a/src/main/java/com/backdev/happy/wblserver/member/controller/MemberController.java b/src/main/java/com/backdev/happy/wblserver/member/controller/MemberController.java new file mode 100644 index 0000000..e59b74a --- /dev/null +++ b/src/main/java/com/backdev/happy/wblserver/member/controller/MemberController.java @@ -0,0 +1,38 @@ +package com.backdev.happy.wblserver.member.controller; + +import com.backdev.happy.wblserver.global.config.security.JwtTokenProvider; +import com.backdev.happy.wblserver.member.domain.Member; +import com.backdev.happy.wblserver.member.dto.MemberDto; +import com.backdev.happy.wblserver.member.service.MemberManageService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/members") +@RequiredArgsConstructor +public class MemberController { + + private final MemberManageService memberManageService; + private final JwtTokenProvider jwtTokenProvider; // jwt 쓴다면 이용할 예정 + + @GetMapping(value = "/a/hello") + public String hello(){ + return "hello world"; + } + + @PostMapping(value = "/signin") + public ResponseEntity signIn(@RequestBody MemberDto.SignInRequest request) { + Member member = memberManageService.signInMember(request); + // return responseService.getSingleResult(jwtTokenProvider.createToken(String.valueOf(customer.getMsrl()), customer.getRoles())); + return new ResponseEntity<>("success to sign in: "+member.toString(), HttpStatus.OK); + } + + @PostMapping(value = "/signup") + public ResponseEntity signUp(@RequestBody MemberDto.SignUpRequest request) { + Member member = memberManageService.signUpMember(request); + return new ResponseEntity<>("success to sign up: "+member.toString(), HttpStatus.OK); + } + +} \ No newline at end of file diff --git a/src/main/java/com/backdev/happy/wblserver/member/domain/Member.java b/src/main/java/com/backdev/happy/wblserver/member/domain/Member.java new file mode 100644 index 0000000..07866b7 --- /dev/null +++ b/src/main/java/com/backdev/happy/wblserver/member/domain/Member.java @@ -0,0 +1,102 @@ +package com.backdev.happy.wblserver.member.domain; + +import com.backdev.happy.wblserver.global.config.security.UserRole; +import lombok.*; +import org.hibernate.annotations.CreationTimestamp; +import org.hibernate.annotations.UpdateTimestamp; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import javax.persistence.*; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; + + +@ToString(of = {"id", "phone", "name"}) +@Getter +@EqualsAndHashCode(of = {"id"}) +@NoArgsConstructor(access = AccessLevel.PROTECTED) // JPA에서는 프록시 객체가 필요함 -> 기본 생성자 하나가 반드시 있어야 함 +@Table(name = "members") +@Entity +public class Member implements UserDetails { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false, length = 100) + private String pw; + + @Column(nullable = false, length = 15) + private String name; + + @Column(nullable = false, length = 11, unique = true) + private String phone; + + @CreationTimestamp + @Column(name = "create_time") + private Date createTime; + + @UpdateTimestamp + @Column(name = "update_time") + private Date updateTime; + + @Column(nullable = false, length = 50) + @Enumerated(EnumType.STRING) + private UserRole role; //회원은 여러 개의 권한을 가질 수 있어서 collection 이 옳지만 일만 단일 권한만 + + @Builder + public Member(String pw, String name, String phone, UserRole role){ + this.pw = pw; + this.name = name; + this.phone = phone; + this.role = role; + } + + public Member(Long id){ + this.id = id; + } + + public void updateProfile(final String name, final String phone) { + this.name = name; + this.phone = phone; + } + + @Override + public Collection getAuthorities() { + return Collections.singletonList(new SimpleGrantedAuthority(role.getValue())); + } + + @Override + public String getPassword() { + return pw; + } + + @Override + public String getUsername() { // unique하게 회원 구분 + return phone; + } + + @Override + public boolean isAccountNonExpired() { //계정이 만료가 안되었는지 + return true; // 사용안해서 true + } + + @Override + public boolean isAccountNonLocked() { //계정이 잠기지 않았는지 + return true; + } + + @Override + public boolean isCredentialsNonExpired() { //계정 패스워드가 만료되진 않았는지 + return true; + } + + @Override + public boolean isEnabled() { //계정이 사용가능한지 + return true; + } +} diff --git a/src/main/java/com/backdev/happy/wblserver/member/dto/MemberDto.java b/src/main/java/com/backdev/happy/wblserver/member/dto/MemberDto.java new file mode 100644 index 0000000..3774d8f --- /dev/null +++ b/src/main/java/com/backdev/happy/wblserver/member/dto/MemberDto.java @@ -0,0 +1,50 @@ +package com.backdev.happy.wblserver.member.dto; + +import com.backdev.happy.wblserver.global.config.security.UserRole; +import com.backdev.happy.wblserver.member.domain.Member; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.springframework.security.crypto.password.PasswordEncoder; + +public class MemberDto { + + @Getter + @NoArgsConstructor + public static class SignUpRequest { + private String phone; + private String name; + private String password; + private String role; + + @Builder + public SignUpRequest(String phone, String name, String password, String role){ + this.phone = phone; + this.name = name; + this.password = password; + this.role = role; + } + + public Member toEntity(PasswordEncoder encoder){ + return Member.builder() + .pw(encoder.encode(password)) + .phone(phone) + .name(name) + .role(UserRole.findByString(role)) + .build(); + } + } + + @Getter + @NoArgsConstructor + public static class SignInRequest { + private String phone; + private String password; + + @Builder + public SignInRequest(String phone, String password){ + this.phone = phone; + this.password = password; + } + } +} diff --git a/src/main/java/com/backdev/happy/wblserver/member/repository/MemberRepository.java b/src/main/java/com/backdev/happy/wblserver/member/repository/MemberRepository.java new file mode 100644 index 0000000..c18aaba --- /dev/null +++ b/src/main/java/com/backdev/happy/wblserver/member/repository/MemberRepository.java @@ -0,0 +1,10 @@ +package com.backdev.happy.wblserver.member.repository; + +import com.backdev.happy.wblserver.member.domain.Member; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +public interface MemberRepository extends JpaRepository { + Optional findByPhone(String phone); +} diff --git a/src/main/java/com/backdev/happy/wblserver/member/service/CustomUserDetailService.java b/src/main/java/com/backdev/happy/wblserver/member/service/CustomUserDetailService.java new file mode 100644 index 0000000..6547239 --- /dev/null +++ b/src/main/java/com/backdev/happy/wblserver/member/service/CustomUserDetailService.java @@ -0,0 +1,18 @@ +package com.backdev.happy.wblserver.member.service; + +import com.backdev.happy.wblserver.member.repository.MemberRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.stereotype.Service; + +@RequiredArgsConstructor +@Service +public class CustomUserDetailService implements UserDetailsService { + + private final MemberRepository memberRepository; + + public UserDetails loadUserByUsername(String userPk) { + return memberRepository.findById(Long.valueOf(userPk)).get(); + } +} \ No newline at end of file diff --git a/src/main/java/com/backdev/happy/wblserver/member/service/MemberManageService.java b/src/main/java/com/backdev/happy/wblserver/member/service/MemberManageService.java new file mode 100644 index 0000000..c90519c --- /dev/null +++ b/src/main/java/com/backdev/happy/wblserver/member/service/MemberManageService.java @@ -0,0 +1,9 @@ +package com.backdev.happy.wblserver.member.service; + +import com.backdev.happy.wblserver.member.domain.Member; +import com.backdev.happy.wblserver.member.dto.MemberDto; + +public interface MemberManageService { + Member signUpMember(MemberDto.SignUpRequest dto); + Member signInMember(MemberDto.SignInRequest dto); +} diff --git a/src/main/java/com/backdev/happy/wblserver/member/service/impl/MemberManageServiceImpl.java b/src/main/java/com/backdev/happy/wblserver/member/service/impl/MemberManageServiceImpl.java new file mode 100644 index 0000000..b95eb11 --- /dev/null +++ b/src/main/java/com/backdev/happy/wblserver/member/service/impl/MemberManageServiceImpl.java @@ -0,0 +1,34 @@ +package com.backdev.happy.wblserver.member.service.impl; + +import com.backdev.happy.wblserver.member.domain.Member; +import com.backdev.happy.wblserver.member.dto.MemberDto; +import com.backdev.happy.wblserver.member.repository.MemberRepository; +import com.backdev.happy.wblserver.member.service.MemberManageService; +import lombok.NoArgsConstructor; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; + +@RequiredArgsConstructor +@Service +public class MemberManageServiceImpl implements MemberManageService { + + private final PasswordEncoder passwordEncoder; + private final MemberRepository memberRepository; + + @Override + public Member signUpMember(MemberDto.SignUpRequest dto) { + return memberRepository.save(dto.toEntity(passwordEncoder)); + } + + @Override + public Member signInMember(MemberDto.SignInRequest dto) { + Member member = memberRepository.findByPhone(dto.getPhone()).orElseThrow(() -> new RuntimeException("fail to sign in")); + if (!passwordEncoder.matches(dto.getPassword(), dto.getPassword())) { + throw new RuntimeException("different password"); + } + return member; + } +} diff --git a/src/test/java/com/backdev/happy/wblserver/bakery/controller/BakeryControllerTests.java b/src/test/java/com/backdev/happy/wblserver/bakery/controller/BakeryControllerTests.java new file mode 100644 index 0000000..02d4327 --- /dev/null +++ b/src/test/java/com/backdev/happy/wblserver/bakery/controller/BakeryControllerTests.java @@ -0,0 +1,100 @@ +package com.backdev.happy.wblserver.bakery.controller; + +import com.backdev.happy.wblserver.bakery.dto.BakeryDto; +import com.backdev.happy.wblserver.member.controller.MemberController; +import com.backdev.happy.wblserver.member.dto.MemberDto; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers; +import org.springframework.security.web.FilterChainProxy; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest +@ExtendWith(SpringExtension.class) +@AutoConfigureMockMvc +public class BakeryControllerTests { + + @Autowired + private MockMvc mockMvc; + + @Autowired + FilterChainProxy springSecurityFilterChain; + + @Autowired + BakeryController bakeryController; + + @Autowired + ObjectMapper objectMapper; + + @BeforeEach + void setup(){ + mockMvc = MockMvcBuilders.standaloneSetup(bakeryController) + .apply(SecurityMockMvcConfigurers.springSecurity(springSecurityFilterChain)) + .build(); + objectMapper = new ObjectMapper(); + } + + @Test + @DisplayName("(valid) ROLE_SELLER 유저의 Bakery 등록") + @WithMockUser(roles = "SELLER") + void register_bakery_by_valid_seller() throws Exception { + + //given + BakeryDto.CreateRequest request = BakeryDto.CreateRequest.builder() + .memberId(18L) + .name("test bakery") + .address("test address") + .img("test img") + .introduction("test introduction") + .build(); + //when + ResultActions result = mockMvc.perform(post("/bakeries") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request)) + ); + + //then + result.andExpect(status().isOk()) + .andDo(print()); + } + + @Test + @DisplayName("(invalid) ROLE_CUSTOMER 유저의 Bakery 등록") + @WithMockUser(roles = "CUSTOMER") + void register_bakery_by_invalid_customer() throws Exception { + //given + BakeryDto.CreateRequest request = BakeryDto.CreateRequest.builder() + .memberId(18L) + .name("test bakery") + .address("test address") + .img("test img") + .introduction("test introduction") + .build(); + + //when + ResultActions result = mockMvc.perform(post("/bakeries") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request)) + ); + + //then + result.andExpect(status().isForbidden()) + .andDo(print()); + } + +} diff --git a/src/test/java/com/backdev/happy/wblserver/member/controller/MemberControllerTests.java b/src/test/java/com/backdev/happy/wblserver/member/controller/MemberControllerTests.java new file mode 100644 index 0000000..47d9bda --- /dev/null +++ b/src/test/java/com/backdev/happy/wblserver/member/controller/MemberControllerTests.java @@ -0,0 +1,145 @@ +package com.backdev.happy.wblserver.member.controller; + + +import com.backdev.happy.wblserver.member.dto.MemberDto; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers; +import org.springframework.security.web.FilterChainProxy; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user; +import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest +@ExtendWith(SpringExtension.class) +@AutoConfigureMockMvc +public class MemberControllerTests { + + @Autowired + FilterChainProxy springSecurityFilterChain; + + @Autowired + private MockMvc mockMvc; + + @Autowired + MemberController memberController; + + @Autowired + ObjectMapper objectMapper; + + @BeforeEach + void setup(){ + mockMvc = MockMvcBuilders.standaloneSetup(memberController) + .apply(SecurityMockMvcConfigurers.springSecurity(springSecurityFilterChain)) + .build(); + objectMapper = new ObjectMapper(); + } + + @Test + @WithMockUser(roles = "SELLER") + void hello() throws Exception { + mockMvc.perform(get("/members/a/hello") + //.with(user("rob").roles("USER")) + ) + .andExpect(status().isOk()) + .andDo(print()); + } + + @Test + @DisplayName("ROLE_CUSTOMER 타입 계정 등록") + void sign_up_customer() throws Exception { + + //given + MemberDto.SignUpRequest request = MemberDto.SignUpRequest.builder() + .name("test name") + .role("ROLE_CUSTOMER") + .phone("01033333333") + .password("testpw") + .build(); + + //when + ResultActions result = mockMvc.perform(post("/members/signup") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request)) + ); + + //then + result.andExpect(status().isOk()) + .andDo(print()); + } + + @Test + @DisplayName("ROLE_SELLER 타입 계정 등록") + void sign_up_seller() throws Exception { + + //given + MemberDto.SignUpRequest request = MemberDto.SignUpRequest.builder() + .name("test name") + .role("ROLE_SELLER") + .phone("01012213222") + .password("testpw") + .build(); + + //when + ResultActions result = mockMvc.perform(post("/members/signup") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request)) + ); + + //then + result.andExpect(status().isOk()) + .andDo(print()); + } + + @Test + @DisplayName("계정 로그인") + void sign_in_valid_user() throws Exception { + + //given + MemberDto.SignInRequest request = MemberDto.SignInRequest.builder() + .phone("01022222222") + .password("testpw") + .build(); + + //when + ResultActions result = mockMvc.perform(post("/members/signin") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request)) + ); + + //then + result.andExpect(status().isOk()) + .andDo(print()); + } + + @Test + @DisplayName("틀린 비밀번호로 로그인 시도") + void sign_in_wrongpasserd() throws Exception { + + //given, when + ResultActions result = mockMvc.perform(post("/members/signin") + .param("password","wrongpassword") + .param("phone","01033326847")); + + //then + result.andExpect(status().isBadRequest()) + .andDo(print()); + } + +}