Skip to content

Commit

Permalink
Merge branch 'main' into feature/brand
Browse files Browse the repository at this point in the history
  • Loading branch information
limsubinn committed Aug 5, 2023
2 parents 47b89c6 + 014abfe commit 660cca5
Show file tree
Hide file tree
Showing 15 changed files with 181 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
Expand All @@ -19,6 +20,7 @@
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
@EnableMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class SecurityConfig {
private final MemberDetailService memberDetailService;
private final JwtTokenProvider jwtProvider;
Expand All @@ -35,9 +37,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(AbstractHttpConfigurer::disable)
.formLogin(FormLoginConfigurer::disable)

.authorizeHttpRequests((auth) -> auth.requestMatchers("/auth/login").permitAll())
.authorizeHttpRequests((auth) -> auth.requestMatchers("/admin/**").hasRole("ADMIN"))
.authorizeHttpRequests((auth) -> auth.anyRequest().permitAll())
.sessionManagement(httpSecuritySessionManagementConfigurer ->
httpSecuritySessionManagementConfigurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@
import com.example.couphoneserver.common.interceptor.JwtAuthInterceptor;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

@EnableMethodSecurity(securedEnabled = true)
@Configuration
@RequiredArgsConstructor
public class WebConfig implements WebMvcConfigurer {
Expand All @@ -21,7 +23,7 @@ public class WebConfig implements WebMvcConfigurer {
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(jwtAuthenticationInterceptor)
.order(1)
.addPathPatterns("/auth", "/brands", "/users","/stores")
.addPathPatterns("/auth", "/brands", "/users", "/stores", "/coupons")
.excludePathPatterns("/auth/login");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ public class AuthController {
@PostMapping("/login")
@Operation(summary = "로그인(Oauth2)", description =
"""
request body 에 이메일, 이름을 포함시켜서 보내주세요.
request body 에 이메일, 이름, 권한(member, admin)을 포함시켜서 보내주세요.
- [권한 상관 없이 동작하는 API 입니다.]
- 권한의 default 값은 member 입니다.
access token 과 refresh token 을 반환합니다.
- 신규 회원이면 회원 가입 처리 후 로그인 처리합니다.
- 기존 회원이면 바로 로그인 처리 합니다.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
package com.example.couphoneserver.controller;

import com.example.couphoneserver.common.annotation.NoAuth;
import com.example.couphoneserver.common.exception.BadRequestException;
import com.example.couphoneserver.common.response.BaseResponse;
import com.example.couphoneserver.dto.brand.GetBrandDetailResponse;
import com.example.couphoneserver.dto.brand.GetBrandResponse;
import com.example.couphoneserver.dto.brand.PostBrandRequest;
import com.example.couphoneserver.dto.brand.PostBrandResponse;
import com.example.couphoneserver.service.BrandService;
import com.example.couphoneserver.service.MemberService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

import java.security.Principal;
Expand All @@ -28,24 +27,29 @@
@RequestMapping("/brands")
public class BrandController {
private final BrandService brandService;
private final MemberService memberService;

@PreAuthorize("hasRole('ADMIN')")
@PostMapping("")
@NoAuth
@Operation(summary = "브랜드 등록",
description = "Request Body에 브랜드 이름, 보상 설명, 이미지 url, 카테고리 id 담아서 보내주세요!")
public BaseResponse<PostBrandResponse> postBrand(@RequestBody PostBrandRequest request){
description = """
Request Body에 브랜드 이름, 보상 설명, 이미지 url, 카테고리 id 담아서 보내주세요!
- [ROLE_ADMIN ONLY]
""",
security = @SecurityRequirement(name = "bearerAuth"))
public BaseResponse<PostBrandResponse> postBrand(@RequestBody PostBrandRequest request) {
return new BaseResponse<>(brandService.saveBrand(request));
}

@PreAuthorize("hasRole('MEMBER') or hasRole('ADMIN')")
@GetMapping("")
@Operation(summary = "브랜드 조회",
description =
"""
브랜드를 검색어 또는 카테고리별로 조회합니다. Header에 Access Token 담아주세요!
Query String으로 카테고리 ID를 담아서 보내주시면 카테고리별로 브랜드를 조회하고,
검색어 담아서 보내주시면 검색한 이름에 따라 브랜드를 조회합니다.
""",
브랜드를 검색어 또는 카테고리별로 조회합니다.
- [ROLE_MEMBER OR ROLE_ADMIN]
- Header에 Access Token 담아주세요!
- Query String으로 카테고리 ID를 담아서 보내주시면 카테고리별로 브랜드를 조회하고, 검색어 담아서 보내주시면 검색한 이름에 따라 브랜드를 조회합니다.
""",
security = @SecurityRequirement(name = "bearerAuth"))
public BaseResponse<List<GetBrandResponse>> getBrand(@RequestParam(required = false, value = "category-id") Long categoryId,
@RequestParam(required = false, value = "name") String name,
Expand All @@ -62,9 +66,14 @@ public BaseResponse<List<GetBrandResponse>> getBrand(@RequestParam(required = fa
throw new BadRequestException(BAD_REQUEST);
}

@PreAuthorize("hasRole('MEMBER') or hasRole('ADMIN')")
@GetMapping("/{brand-id}")
@Operation(summary = "브랜드 상세 조회",
description = "브랜드를 상세 조회합니다. Path Vriable로 브랜드 ID, Header에 Access Token 담아서 보내주세요!",
description = """
브랜드를 상세 조회합니다.
- [ROLE_MEMBER OR ROLE_ADMIN]
- Path Vriable로 브랜드 ID, Header에 Access Token 담아서 보내주세요!
""",
security = @SecurityRequirement(name = "bearerAuth"))
public BaseResponse<GetBrandDetailResponse> getBrandDetail(@PathVariable(value = "brand-id") Long brandId,
Principal principal) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
Expand All @@ -29,14 +31,19 @@ public class CategoryController {

private final CategoryService categoryService;

@PreAuthorize("hasRole('MEMBER') or hasRole('ADMIN')")
@GetMapping("")
@Operation(summary = "카테고리 정보 제공", description = "카테고리 정보를 제공합니다.", responses = {
@Operation(summary = "카테고리 정보 제공", description = """
카테고리 정보를 제공합니다.
- [ROLE_MEMBER OR ROLE_ADMIN]
""", responses = {
@ApiResponse(responseCode = "200", description = "쿼리가 null인 경우, 모든 카테고리 조회 성공", content = @Content(schema = @Schema(implementation = GetCategoryResponse.class))),
@ApiResponse(responseCode = "200", description = "쿼리에 이름을 넣은 경우, 카테고리 조회 성공", content = @Content(schema = @Schema(implementation = GetCategoryResponse.class))),
@ApiResponse(responseCode = "400", description = "존재하지 않은 리소스 접근", content = @Content(schema = @Schema(implementation = BaseErrorResponse.class)))
})
}, security = @SecurityRequirement(name = "bearerAuth"))

public BaseResponse<List<GetCategoryResponse>> getCategory(@Parameter(name = "query", description = "카테고리의 이름(선택)", in = ParameterIn.QUERY)
@RequestParam(required = false) String query){
@RequestParam(required = false) String query) {
return new BaseResponse<>(categoryService.findCategory(query));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
import com.example.couphoneserver.dto.coupon.*;
import com.example.couphoneserver.service.CouponService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

@Tag(name = "coupons", description = "쿠폰 관련 API")
Expand All @@ -14,21 +16,35 @@
@RequestMapping("/coupons")
public class CouponController {
private final CouponService couponService;

@PreAuthorize("hasRole('ADMIN')")
@PostMapping("")
@Operation(summary = "쿠폰 등록", description = "Request Body에 브랜드 ID, 멤버 ID 넣어주세요!")
@Operation(summary = "쿠폰 등록", description = """
Request Body에 브랜드 ID, 멤버 ID 넣어주세요!
- [ROLE_ADMIN ONLY]
""",
security = @SecurityRequirement(name = "bearerAuth"))
public BaseResponse<PostCouponResponse> postCouponItem(@RequestBody PostCouponRequest request){
return new BaseResponse<>(couponService.saveCoupon(request));
}

@PreAuthorize("hasRole('ADMIN')")
@PatchMapping("/stamp/{coupon-id}")
@Operation(summary = "쿠폰 스탬프 적립", description = "Path Variable로 쿠폰 ID 넣어주세요!")
@Operation(summary = "쿠폰 스탬프 적립", description = """
Path Variable로 쿠폰 ID 넣어주세요!
- [ROLE_ADMIN ONLY]
""",
security = @SecurityRequirement(name = "bearerAuth"))
public BaseResponse<PatchCouponCountResponse> patchCouponItemCount(@PathVariable("coupon-id") Long couponId) {
return new BaseResponse<>(couponService.collectStamp(couponId));
}

@PreAuthorize("hasRole('MEMBER') or hasRole('ADMIN')")
@PatchMapping("/status/{coupon-id}")
@Operation(summary = "쿠폰 사용하기", description = "Path Variable로 쿠폰 ID 넣어주세요!")
@Operation(summary = "쿠폰 사용하기", description = """
Path Variable로 쿠폰 ID 넣어주세요!
- [ROLE_MEMBER OR ROLE_ADMIN]
""",
security = @SecurityRequirement(name = "bearerAuth"))
public BaseResponse<PatchCouponStatusResponse> patchCouponItemStatus(@PathVariable("coupon-id") Long couponId) {
return new BaseResponse<>(couponService.useCoupon(couponId));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,44 +7,64 @@
import com.example.couphoneserver.dto.member.response.PatchMemberResponse;
import com.example.couphoneserver.service.MemberService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

@Tag(name = "회원 컨트롤러", description = "회원 관련 API 입니다.")
import java.security.Principal;


@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/users")
public class MemberController {
private final MemberService memberService;

@PatchMapping("/{member-id}")
@PreAuthorize("hasRole('MEMBER') or hasRole('ADMIN')")
@PatchMapping("")
@Operation(summary = "회원 탈퇴", description =
"회원의 상태를 TERMINATED 으로 변경합니다. path variable 로 멤버 id 담아서 보내주세요!")
public BaseResponse<PatchMemberResponse> delete(@PathVariable("member-id") Long memberId) {
"""
회원의 상태를 TERMINATED 으로 변경합니다.
- [ROLE_MEMBER OR ROLE_ADMIN]
- access token 을 반드시 포함해서 보내주세요!
""",
security = @SecurityRequirement(name = "bearerAuth"))
public BaseResponse<PatchMemberResponse> delete(Principal principal) {
Long memberId = memberService.findMemberIdByPrincipal(principal);
Member member = memberService.findOneById(memberId);
return new BaseResponse<>(memberService.delete(member));
}

@GetMapping("/{member-id}")
@PreAuthorize("hasRole('MEMBER') or hasRole('ADMIN')")
@GetMapping("")
@Operation(summary = "회원 정보 조회", description =
"회원 정보를 조회합니다. path variable 로 멤버 id 담아서 보내주세요!")
public BaseResponse<GetMemberResponse> show(@PathVariable("member-id") Long memberId) {
"""
회원 정보를 조회합니다.
- [ROLE_MEMBER OR ROLE_ADMIN]
- access token 을 반드시 포함해서 보내주세요!
""",
security = @SecurityRequirement(name = "bearerAuth"))
public BaseResponse<GetMemberResponse> show(Principal principal) {
Long memberId = memberService.findMemberIdByPrincipal(principal);
Member member = memberService.findOneById(memberId);
return new BaseResponse<>(memberService.getMemberInfo(member));
}

@PreAuthorize("hasRole('MEMBER') or hasRole('ADMIN')")
@GetMapping("/{member-id}/brands")
@Operation(summary = "정렬 조건에 따른 브랜드 조회", description =
"""
path variable 으로 member-id 를 보내면 해당 회원이 가지고 있는 쿠폰 브랜드 리스트를 반환합니다.
정렬 조건은 query string 으로 sort 값을 보내주세요. {1, 2, 3} 에 따라 달라집니다.
- [ROLE_MEMBER OR ROLE_ADMIN]
- 1(default)번 옵션은 쿠폰 많은 순, 생성 시간이 이른 순
- 2번 옵션은 생성 시간이 이른 순, 쿠폰 많은 순
- 3번 옵션은 브랜드 이름 순으로 정렬하여 데이터를 반환합니다.
""")
""",
security = @SecurityRequirement(name = "bearerAuth"))
public BaseResponse<GetMemberCouponBrandsResponse> getBrands(
@PathVariable("member-id") Long memberId,
@RequestParam(required = false, defaultValue = "1", value = "sort") String sort) {
Expand Down
Loading

0 comments on commit 660cca5

Please sign in to comment.