From b199287054f89554da6e177cbc9e8fb7a86ae174 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=ED=98=84=EC=88=98?= Date: Mon, 15 Aug 2022 17:46:19 +0900 Subject: [PATCH 01/27] =?UTF-8?q?feat:=201=EB=8B=A8=EA=B3=84=20=EC=B4=88?= =?UTF-8?q?=EA=B8=B0=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 8 ++ src/main/java/nextstep/DataLoader.java | 12 +++ .../BearerTokenAuthenticationFilter.java | 24 +++++- .../UsernamePasswordAuthenticationFilter.java | 35 +++++++- .../nextstep/member/ui/MemberController.java | 2 +- .../nextstep/subway/ui/LineController.java | 6 ++ .../nextstep/subway/ui/StationController.java | 3 + .../subway/acceptance/AuthAcceptanceTest.java | 27 +++++-- .../subway/acceptance/LineAcceptanceTest.java | 33 ++++---- .../acceptance/LineSectionAcceptanceTest.java | 33 ++++---- .../nextstep/subway/acceptance/LineSteps.java | 67 ---------------- .../acceptance/MemberAcceptanceTest.java | 3 +- .../subway/acceptance/PathAcceptanceTest.java | 18 +++-- .../acceptance/SectionAcceptanceTest.java | 21 ++--- .../acceptance/StationAcceptanceTest.java | 19 ++--- .../{ => support}/AcceptanceTest.java | 16 +++- .../support/AcceptanceTestSteps.java | 17 ++++ .../subway/acceptance/support/LineSteps.java | 80 +++++++++++++++++++ .../acceptance/{ => support}/MemberSteps.java | 4 +- .../{ => support}/StationSteps.java | 16 ++-- 20 files changed, 294 insertions(+), 150 deletions(-) delete mode 100644 src/test/java/nextstep/subway/acceptance/LineSteps.java rename src/test/java/nextstep/subway/acceptance/{ => support}/AcceptanceTest.java (61%) create mode 100644 src/test/java/nextstep/subway/acceptance/support/AcceptanceTestSteps.java create mode 100644 src/test/java/nextstep/subway/acceptance/support/LineSteps.java rename src/test/java/nextstep/subway/acceptance/{ => support}/MemberSteps.java (97%) rename src/test/java/nextstep/subway/acceptance/{ => support}/StationSteps.java (58%) diff --git a/build.gradle b/build.gradle index d12875bc9..ab965f91a 100644 --- a/build.gradle +++ b/build.gradle @@ -31,6 +31,14 @@ dependencies { testImplementation 'io.rest-assured:rest-assured:4.2.0' runtimeOnly 'com.h2database:h2' + + compileOnly 'org.projectlombok:lombok:1.18.24' + annotationProcessor 'org.projectlombok:lombok:1.18.24' + + testCompileOnly 'org.projectlombok:lombok:1.18.24' + testAnnotationProcessor 'org.projectlombok:lombok:1.18.24' + + implementation 'com.google.guava:guava:31.1-jre' } test { diff --git a/src/main/java/nextstep/DataLoader.java b/src/main/java/nextstep/DataLoader.java index d3cd1cfc7..98bc8b7b8 100644 --- a/src/main/java/nextstep/DataLoader.java +++ b/src/main/java/nextstep/DataLoader.java @@ -1,9 +1,21 @@ package nextstep; +import nextstep.member.domain.Member; +import nextstep.member.domain.MemberRepository; +import nextstep.member.domain.RoleType; import org.springframework.stereotype.Component; +import com.google.common.collect.Lists; @Component public class DataLoader { + private MemberRepository memberRepository; + + public DataLoader(MemberRepository memberRepository) { + this.memberRepository = memberRepository; + } + public void loadData() { + memberRepository.save(new Member("admin@email.com", "password", 20, Lists.newArrayList(RoleType.ROLE_ADMIN.name()))); + memberRepository.save(new Member("member@email.com", "password", 20, Lists.newArrayList(RoleType.ROLE_MEMBER.name()))); } } diff --git a/src/main/java/nextstep/auth/authentication/BearerTokenAuthenticationFilter.java b/src/main/java/nextstep/auth/authentication/BearerTokenAuthenticationFilter.java index c4f72f149..609dbb070 100644 --- a/src/main/java/nextstep/auth/authentication/BearerTokenAuthenticationFilter.java +++ b/src/main/java/nextstep/auth/authentication/BearerTokenAuthenticationFilter.java @@ -1,10 +1,13 @@ package nextstep.auth.authentication; +import nextstep.auth.context.Authentication; +import nextstep.auth.context.SecurityContextHolder; import nextstep.auth.token.JwtTokenProvider; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import java.util.List; public class BearerTokenAuthenticationFilter implements HandlerInterceptor { private JwtTokenProvider jwtTokenProvider; @@ -15,7 +18,24 @@ public BearerTokenAuthenticationFilter(JwtTokenProvider jwtTokenProvider) { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { - // TODO: 구현하세요. - return true; + try { + String authCredentials = AuthorizationExtractor.extract(request, AuthorizationType.BEARER); + AuthenticationToken token = new AuthenticationToken(authCredentials, authCredentials); + + if (!jwtTokenProvider.validateToken(token.getPrincipal())) { + throw new AuthenticationException(); + } + + String principal = jwtTokenProvider.getPrincipal(token.getPrincipal()); + List roles = jwtTokenProvider.getRoles(token.getPrincipal()); + + Authentication authentication = new Authentication(principal, roles); + + SecurityContextHolder.getContext().setAuthentication(authentication); + + return true; + } catch (Exception e) { + return true; + } } } diff --git a/src/main/java/nextstep/auth/authentication/UsernamePasswordAuthenticationFilter.java b/src/main/java/nextstep/auth/authentication/UsernamePasswordAuthenticationFilter.java index f0f531cf7..4f4faf9dc 100644 --- a/src/main/java/nextstep/auth/authentication/UsernamePasswordAuthenticationFilter.java +++ b/src/main/java/nextstep/auth/authentication/UsernamePasswordAuthenticationFilter.java @@ -1,12 +1,19 @@ package nextstep.auth.authentication; +import nextstep.auth.context.Authentication; +import nextstep.auth.context.SecurityContextHolder; import nextstep.member.application.LoginMemberService; +import nextstep.member.domain.LoginMember; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import java.util.Map; public class UsernamePasswordAuthenticationFilter implements HandlerInterceptor { + public static final String USERNAME_FIELD = "username"; + public static final String PASSWORD_FIELD = "password"; + private LoginMemberService loginMemberService; public UsernamePasswordAuthenticationFilter(LoginMemberService loginMemberService) { @@ -15,7 +22,31 @@ public UsernamePasswordAuthenticationFilter(LoginMemberService loginMemberServic @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { - // TODO: 구현하세요. - return true; + try { + Map paramMap = request.getParameterMap(); + String username = paramMap.get(USERNAME_FIELD)[0]; + String password = paramMap.get(PASSWORD_FIELD)[0]; + + AuthenticationToken token = new AuthenticationToken(username, password); + + String principal = token.getPrincipal(); + LoginMember loginMember = loginMemberService.loadUserByUsername(principal); + + if (loginMember == null) { + throw new AuthenticationException(); + } + + if (!loginMember.checkPassword(token.getCredentials())) { + throw new AuthenticationException(); + } + + Authentication authentication = new Authentication(loginMember.getEmail(), loginMember.getAuthorities()); + + SecurityContextHolder.getContext().setAuthentication(authentication); + + return false; + } catch (Exception e) { + return true; + } } } diff --git a/src/main/java/nextstep/member/ui/MemberController.java b/src/main/java/nextstep/member/ui/MemberController.java index 742acdb17..c24d48243 100644 --- a/src/main/java/nextstep/member/ui/MemberController.java +++ b/src/main/java/nextstep/member/ui/MemberController.java @@ -43,7 +43,7 @@ public ResponseEntity deleteMember(@PathVariable Long id) { } @GetMapping("/members/me") - public ResponseEntity findMemberOfMine(@AuthenticationPrincipal LoginMember loginMember) { + public ResponseEntity findMemberOfMine(@ AuthenticationPrincipal LoginMember loginMember) { MemberResponse member = memberService.findMember(loginMember.getEmail()); return ResponseEntity.ok().body(member); } diff --git a/src/main/java/nextstep/subway/ui/LineController.java b/src/main/java/nextstep/subway/ui/LineController.java index 34066048b..81ec1afda 100644 --- a/src/main/java/nextstep/subway/ui/LineController.java +++ b/src/main/java/nextstep/subway/ui/LineController.java @@ -1,5 +1,6 @@ package nextstep.subway.ui; +import nextstep.auth.secured.Secured; import nextstep.subway.applicaion.LineService; import nextstep.subway.applicaion.dto.LineRequest; import nextstep.subway.applicaion.dto.LineResponse; @@ -19,6 +20,7 @@ public LineController(LineService lineService) { this.lineService = lineService; } + @Secured("ROLE_ADMIN") @PostMapping public ResponseEntity createLine(@RequestBody LineRequest lineRequest) { LineResponse line = lineService.saveLine(lineRequest); @@ -37,24 +39,28 @@ public ResponseEntity getLine(@PathVariable Long id) { return ResponseEntity.ok().body(lineResponse); } + @Secured("ROLE_ADMIN") @PutMapping("/{id}") public ResponseEntity updateLine(@PathVariable Long id, @RequestBody LineRequest lineRequest) { lineService.updateLine(id, lineRequest); return ResponseEntity.ok().build(); } + @Secured("ROLE_ADMIN") @DeleteMapping("/{id}") public ResponseEntity updateLine(@PathVariable Long id) { lineService.deleteLine(id); return ResponseEntity.noContent().build(); } + @Secured("ROLE_ADMIN") @PostMapping("/{lineId}/sections") public ResponseEntity addSection(@PathVariable Long lineId, @RequestBody SectionRequest sectionRequest) { lineService.addSection(lineId, sectionRequest); return ResponseEntity.ok().build(); } + @Secured("ROLE_ADMIN") @DeleteMapping("/{lineId}/sections") public ResponseEntity deleteSection(@PathVariable Long lineId, @RequestParam Long stationId) { lineService.deleteSection(lineId, stationId); diff --git a/src/main/java/nextstep/subway/ui/StationController.java b/src/main/java/nextstep/subway/ui/StationController.java index 956375927..08224b7f0 100644 --- a/src/main/java/nextstep/subway/ui/StationController.java +++ b/src/main/java/nextstep/subway/ui/StationController.java @@ -1,5 +1,6 @@ package nextstep.subway.ui; +import nextstep.auth.secured.Secured; import nextstep.subway.applicaion.StationService; import nextstep.subway.applicaion.dto.StationRequest; import nextstep.subway.applicaion.dto.StationResponse; @@ -18,6 +19,7 @@ public StationController(StationService stationService) { this.stationService = stationService; } + @Secured("ROLE_ADMIN") @PostMapping("/stations") public ResponseEntity createStation(@RequestBody StationRequest stationRequest) { StationResponse station = stationService.saveStation(stationRequest); @@ -29,6 +31,7 @@ public ResponseEntity> showStations() { return ResponseEntity.ok().body(stationService.findAllStations()); } + @Secured("ROLE_ADMIN") @DeleteMapping("/stations/{id}") public ResponseEntity deleteStation(@PathVariable Long id) { stationService.deleteStationById(id); diff --git a/src/test/java/nextstep/subway/acceptance/AuthAcceptanceTest.java b/src/test/java/nextstep/subway/acceptance/AuthAcceptanceTest.java index 07366c9eb..5346d7e95 100644 --- a/src/test/java/nextstep/subway/acceptance/AuthAcceptanceTest.java +++ b/src/test/java/nextstep/subway/acceptance/AuthAcceptanceTest.java @@ -1,11 +1,16 @@ package nextstep.subway.acceptance; +import io.restassured.RestAssured; +import io.restassured.authentication.FormAuthConfig; import io.restassured.response.ExtractableResponse; import io.restassured.response.Response; +import nextstep.subway.acceptance.support.AcceptanceTest; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; -import static nextstep.subway.acceptance.MemberSteps.*; +import static nextstep.subway.acceptance.support.MemberSteps.*; class AuthAcceptanceTest extends AcceptanceTest { @@ -39,11 +44,23 @@ void myInfoWithBearerAuth() { 회원_정보_조회됨(response, EMAIL, AGE); } - private ExtractableResponse 폼_로그인_후_내_회원_정보_조회_요청(String email, String password) { - return null; + public static ExtractableResponse 폼_로그인_후_내_회원_정보_조회_요청(String email, String password) { + return RestAssured + .given().log().all() + .auth().form(email, password, new FormAuthConfig("/login/form", USERNAME_FIELD, PASSWORD_FIELD)) + .accept(MediaType.APPLICATION_JSON_VALUE) + .when().get("/members/me") + .then().log().all() + .statusCode(HttpStatus.OK.value()).extract(); } - private ExtractableResponse 베어러_인증으로_내_회원_정보_조회_요청(String accessToken) { - return null; + public static ExtractableResponse 베어러_인증으로_내_회원_정보_조회_요청(String accessToken) { + return RestAssured.given().log().all() + .auth().oauth2(accessToken) + .accept(MediaType.APPLICATION_JSON_VALUE) + .when().get("/members/me") + .then().log().all() + .statusCode(HttpStatus.OK.value()) + .extract(); } } diff --git a/src/test/java/nextstep/subway/acceptance/LineAcceptanceTest.java b/src/test/java/nextstep/subway/acceptance/LineAcceptanceTest.java index d93594071..68ba4c3b2 100644 --- a/src/test/java/nextstep/subway/acceptance/LineAcceptanceTest.java +++ b/src/test/java/nextstep/subway/acceptance/LineAcceptanceTest.java @@ -3,6 +3,7 @@ import io.restassured.RestAssured; import io.restassured.response.ExtractableResponse; import io.restassured.response.Response; +import nextstep.subway.acceptance.support.AcceptanceTest; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.http.HttpStatus; @@ -11,7 +12,11 @@ import java.util.HashMap; import java.util.Map; -import static nextstep.subway.acceptance.LineSteps.*; +import static nextstep.subway.acceptance.support.LineSteps.지하철_노선_목록_조회_요청; +import static nextstep.subway.acceptance.support.LineSteps.지하철_노선_삭제_요청; +import static nextstep.subway.acceptance.support.LineSteps.지하철_노선_생성_요청; +import static nextstep.subway.acceptance.support.LineSteps.지하철_노선_수정_요청; +import static nextstep.subway.acceptance.support.LineSteps.지하철_노선_조회_요청; import static org.assertj.core.api.Assertions.assertThat; @DisplayName("지하철 노선 관리 기능") @@ -24,7 +29,7 @@ class LineAcceptanceTest extends AcceptanceTest { @Test void createLine() { // when - ExtractableResponse response = 지하철_노선_생성_요청("2호선", "green"); + ExtractableResponse response = 지하철_노선_생성_요청(관리자, "2호선", "green"); // then assertThat(response.statusCode()).isEqualTo(HttpStatus.CREATED.value()); @@ -42,8 +47,8 @@ void createLine() { @Test void getLines() { // given - 지하철_노선_생성_요청("2호선", "green"); - 지하철_노선_생성_요청("3호선", "orange"); + 지하철_노선_생성_요청(관리자, "2호선", "green"); + 지하철_노선_생성_요청(관리자, "3호선", "orange"); // when ExtractableResponse response = 지하철_노선_목록_조회_요청(); @@ -62,7 +67,7 @@ void getLines() { @Test void getLine() { // given - ExtractableResponse createResponse = 지하철_노선_생성_요청("2호선", "green"); + ExtractableResponse createResponse = 지하철_노선_생성_요청(관리자, "2호선", "green"); // when ExtractableResponse response = 지하철_노선_조회_요청(createResponse); @@ -81,17 +86,10 @@ void getLine() { @Test void updateLine() { // given - ExtractableResponse createResponse = 지하철_노선_생성_요청("2호선", "green"); + ExtractableResponse createResponse = 지하철_노선_생성_요청(관리자, "2호선", "green"); // when - Map params = new HashMap<>(); - params.put("color", "red"); - RestAssured - .given().log().all() - .body(params) - .contentType(MediaType.APPLICATION_JSON_VALUE) - .when().put(createResponse.header("location")) - .then().log().all().extract(); + 지하철_노선_수정_요청(관리자, createResponse.header("location")); // then ExtractableResponse response = 지하철_노선_조회_요청(createResponse); @@ -108,13 +106,10 @@ void updateLine() { @Test void deleteLine() { // given - ExtractableResponse createResponse = 지하철_노선_생성_요청("2호선", "green"); + ExtractableResponse createResponse = 지하철_노선_생성_요청(관리자, "2호선", "green"); // when - ExtractableResponse response = RestAssured - .given().log().all() - .when().delete(createResponse.header("location")) - .then().log().all().extract(); + ExtractableResponse response = 지하철_노선_삭제_요청(관리자, createResponse.header("location")); // then assertThat(response.statusCode()).isEqualTo(HttpStatus.NO_CONTENT.value()); diff --git a/src/test/java/nextstep/subway/acceptance/LineSectionAcceptanceTest.java b/src/test/java/nextstep/subway/acceptance/LineSectionAcceptanceTest.java index 52d39b89d..ad31aaac6 100644 --- a/src/test/java/nextstep/subway/acceptance/LineSectionAcceptanceTest.java +++ b/src/test/java/nextstep/subway/acceptance/LineSectionAcceptanceTest.java @@ -2,6 +2,7 @@ import io.restassured.response.ExtractableResponse; import io.restassured.response.Response; +import nextstep.subway.acceptance.support.AcceptanceTest; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -10,8 +11,8 @@ import java.util.HashMap; import java.util.Map; -import static nextstep.subway.acceptance.LineSteps.*; -import static nextstep.subway.acceptance.StationSteps.지하철역_생성_요청; +import static nextstep.subway.acceptance.support.LineSteps.*; +import static nextstep.subway.acceptance.support.StationSteps.지하철역_생성_요청; import static org.assertj.core.api.Assertions.assertThat; @DisplayName("지하철 구간 관리 기능") @@ -28,11 +29,11 @@ class LineSectionAcceptanceTest extends AcceptanceTest { public void setUp() { super.setUp(); - 강남역 = 지하철역_생성_요청("강남역").jsonPath().getLong("id"); - 양재역 = 지하철역_생성_요청("양재역").jsonPath().getLong("id"); + 강남역 = 지하철역_생성_요청(관리자, "강남역").jsonPath().getLong("id"); + 양재역 = 지하철역_생성_요청(관리자, "양재역").jsonPath().getLong("id"); Map lineCreateParams = createLineCreateParams(강남역, 양재역); - 신분당선 = 지하철_노선_생성_요청(lineCreateParams).jsonPath().getLong("id"); + 신분당선 = 지하철_노선_생성_요청(관리자, lineCreateParams).jsonPath().getLong("id"); } /** @@ -43,8 +44,8 @@ public void setUp() { @Test void addLineSection() { // when - Long 정자역 = 지하철역_생성_요청("정자역").jsonPath().getLong("id"); - 지하철_노선에_지하철_구간_생성_요청(신분당선, createSectionCreateParams(양재역, 정자역)); + Long 정자역 = 지하철역_생성_요청(관리자, "정자역").jsonPath().getLong("id"); + 지하철_노선에_지하철_구간_생성_요청(관리자, 신분당선, createSectionCreateParams(양재역, 정자역)); // then ExtractableResponse response = 지하철_노선_조회_요청(신분당선); @@ -60,8 +61,8 @@ void addLineSection() { @Test void addLineSectionMiddle() { // when - Long 정자역 = 지하철역_생성_요청("정자역").jsonPath().getLong("id"); - 지하철_노선에_지하철_구간_생성_요청(신분당선, createSectionCreateParams(강남역, 정자역)); + Long 정자역 = 지하철역_생성_요청(관리자, "정자역").jsonPath().getLong("id"); + 지하철_노선에_지하철_구간_생성_요청(관리자, 신분당선, createSectionCreateParams(강남역, 정자역)); // then ExtractableResponse response = 지하철_노선_조회_요청(신분당선); @@ -77,7 +78,7 @@ void addLineSectionMiddle() { @Test void addSectionAlreadyIncluded() { // when - ExtractableResponse response = 지하철_노선에_지하철_구간_생성_요청(신분당선, createSectionCreateParams(강남역, 양재역)); + ExtractableResponse response = 지하철_노선에_지하철_구간_생성_요청(관리자, 신분당선, createSectionCreateParams(강남역, 양재역)); // then assertThat(response.statusCode()).isEqualTo(HttpStatus.BAD_REQUEST.value()); @@ -92,11 +93,11 @@ void addSectionAlreadyIncluded() { @Test void removeLineSection() { // given - Long 정자역 = 지하철역_생성_요청("정자역").jsonPath().getLong("id"); - 지하철_노선에_지하철_구간_생성_요청(신분당선, createSectionCreateParams(양재역, 정자역)); + Long 정자역 = 지하철역_생성_요청(관리자, "정자역").jsonPath().getLong("id"); + 지하철_노선에_지하철_구간_생성_요청(관리자, 신분당선, createSectionCreateParams(양재역, 정자역)); // when - 지하철_노선에_지하철_구간_제거_요청(신분당선, 정자역); + 지하철_노선에_지하철_구간_제거_요청(관리자, 신분당선, 정자역); // then ExtractableResponse response = 지하철_노선_조회_요청(신분당선); @@ -113,11 +114,11 @@ void removeLineSection() { @Test void removeLineSectionInMiddle() { // given - Long 정자역 = 지하철역_생성_요청("정자역").jsonPath().getLong("id"); - 지하철_노선에_지하철_구간_생성_요청(신분당선, createSectionCreateParams(양재역, 정자역)); + Long 정자역 = 지하철역_생성_요청(관리자, "정자역").jsonPath().getLong("id"); + 지하철_노선에_지하철_구간_생성_요청(관리자, 신분당선, createSectionCreateParams(양재역, 정자역)); // when - 지하철_노선에_지하철_구간_제거_요청(신분당선, 양재역); + 지하철_노선에_지하철_구간_제거_요청(관리자, 신분당선, 양재역); // then ExtractableResponse response = 지하철_노선_조회_요청(신분당선); diff --git a/src/test/java/nextstep/subway/acceptance/LineSteps.java b/src/test/java/nextstep/subway/acceptance/LineSteps.java deleted file mode 100644 index 1c93c1265..000000000 --- a/src/test/java/nextstep/subway/acceptance/LineSteps.java +++ /dev/null @@ -1,67 +0,0 @@ -package nextstep.subway.acceptance; - -import io.restassured.RestAssured; -import io.restassured.response.ExtractableResponse; -import io.restassured.response.Response; -import org.springframework.http.MediaType; - -import java.util.HashMap; -import java.util.Map; - -public class LineSteps { - public static ExtractableResponse 지하철_노선_생성_요청(String name, String color) { - Map params = new HashMap<>(); - params.put("name", name); - params.put("color", color); - return RestAssured - .given().log().all() - .body(params) - .contentType(MediaType.APPLICATION_JSON_VALUE) - .when().post("/lines") - .then().log().all().extract(); - } - - public static ExtractableResponse 지하철_노선_목록_조회_요청() { - return RestAssured - .given().log().all() - .when().get("/lines") - .then().log().all().extract(); - } - - public static ExtractableResponse 지하철_노선_조회_요청(ExtractableResponse createResponse) { - return RestAssured - .given().log().all() - .when().get(createResponse.header("location")) - .then().log().all().extract(); - } - - public static ExtractableResponse 지하철_노선_조회_요청(Long id) { - return RestAssured - .given().log().all() - .when().get("/lines/{id}", id) - .then().log().all().extract(); - } - - public static ExtractableResponse 지하철_노선_생성_요청(Map params) { - return RestAssured - .given().log().all() - .body(params) - .contentType(MediaType.APPLICATION_JSON_VALUE) - .when().post("/lines") - .then().log().all().extract(); - } - - public static ExtractableResponse 지하철_노선에_지하철_구간_생성_요청(Long lineId, Map params) { - return RestAssured.given().log().all() - .contentType(MediaType.APPLICATION_JSON_VALUE) - .body(params) - .when().post("/lines/{lineId}/sections", lineId) - .then().log().all().extract(); - } - - public static ExtractableResponse 지하철_노선에_지하철_구간_제거_요청(Long lineId, Long stationId) { - return RestAssured.given().log().all() - .when().delete("/lines/{lineId}/sections?stationId={stationId}", lineId, stationId) - .then().log().all().extract(); - } -} diff --git a/src/test/java/nextstep/subway/acceptance/MemberAcceptanceTest.java b/src/test/java/nextstep/subway/acceptance/MemberAcceptanceTest.java index aedeedb1c..f89bbb056 100644 --- a/src/test/java/nextstep/subway/acceptance/MemberAcceptanceTest.java +++ b/src/test/java/nextstep/subway/acceptance/MemberAcceptanceTest.java @@ -2,11 +2,12 @@ import io.restassured.response.ExtractableResponse; import io.restassured.response.Response; +import nextstep.subway.acceptance.support.AcceptanceTest; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.http.HttpStatus; -import static nextstep.subway.acceptance.MemberSteps.*; +import static nextstep.subway.acceptance.support.MemberSteps.*; import static org.assertj.core.api.Assertions.assertThat; class MemberAcceptanceTest extends AcceptanceTest { diff --git a/src/test/java/nextstep/subway/acceptance/PathAcceptanceTest.java b/src/test/java/nextstep/subway/acceptance/PathAcceptanceTest.java index d572687c7..009a8fe6f 100644 --- a/src/test/java/nextstep/subway/acceptance/PathAcceptanceTest.java +++ b/src/test/java/nextstep/subway/acceptance/PathAcceptanceTest.java @@ -3,6 +3,8 @@ import io.restassured.RestAssured; import io.restassured.response.ExtractableResponse; import io.restassured.response.Response; +import nextstep.subway.acceptance.support.AcceptanceTest; +import nextstep.subway.acceptance.support.LineSteps; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -11,8 +13,8 @@ import java.util.HashMap; import java.util.Map; -import static nextstep.subway.acceptance.LineSteps.지하철_노선에_지하철_구간_생성_요청; -import static nextstep.subway.acceptance.StationSteps.지하철역_생성_요청; +import static nextstep.subway.acceptance.support.LineSteps.지하철_노선에_지하철_구간_생성_요청; +import static nextstep.subway.acceptance.support.StationSteps.지하철역_생성_요청; import static org.assertj.core.api.Assertions.assertThat; @DisplayName("지하철 경로 검색") @@ -36,16 +38,16 @@ class PathAcceptanceTest extends AcceptanceTest { public void setUp() { super.setUp(); - 교대역 = 지하철역_생성_요청("교대역").jsonPath().getLong("id"); - 강남역 = 지하철역_생성_요청("강남역").jsonPath().getLong("id"); - 양재역 = 지하철역_생성_요청("양재역").jsonPath().getLong("id"); - 남부터미널역 = 지하철역_생성_요청("남부터미널역").jsonPath().getLong("id"); + 교대역 = 지하철역_생성_요청(관리자, "교대역").jsonPath().getLong("id"); + 강남역 = 지하철역_생성_요청(관리자, "강남역").jsonPath().getLong("id"); + 양재역 = 지하철역_생성_요청(관리자, "양재역").jsonPath().getLong("id"); + 남부터미널역 = 지하철역_생성_요청(관리자, "남부터미널역").jsonPath().getLong("id"); 이호선 = 지하철_노선_생성_요청("2호선", "green", 교대역, 강남역, 10); 신분당선 = 지하철_노선_생성_요청("신분당선", "red", 강남역, 양재역, 10); 삼호선 = 지하철_노선_생성_요청("3호선", "orange", 교대역, 남부터미널역, 2); - 지하철_노선에_지하철_구간_생성_요청(삼호선, createSectionCreateParams(남부터미널역, 양재역, 3)); + 지하철_노선에_지하철_구간_생성_요청(관리자, 삼호선, createSectionCreateParams(남부터미널역, 양재역, 3)); } @DisplayName("두 역의 최단 거리 경로를 조회한다.") @@ -75,7 +77,7 @@ void findPathByDistance() { lineCreateParams.put("downStationId", downStation + ""); lineCreateParams.put("distance", distance + ""); - return LineSteps.지하철_노선_생성_요청(lineCreateParams).jsonPath().getLong("id"); + return LineSteps.지하철_노선_생성_요청(관리자, lineCreateParams).jsonPath().getLong("id"); } private Map createSectionCreateParams(Long upStationId, Long downStationId, int distance) { diff --git a/src/test/java/nextstep/subway/acceptance/SectionAcceptanceTest.java b/src/test/java/nextstep/subway/acceptance/SectionAcceptanceTest.java index 84a731622..5bacb2bfd 100644 --- a/src/test/java/nextstep/subway/acceptance/SectionAcceptanceTest.java +++ b/src/test/java/nextstep/subway/acceptance/SectionAcceptanceTest.java @@ -2,6 +2,7 @@ import io.restassured.response.ExtractableResponse; import io.restassured.response.Response; +import nextstep.subway.acceptance.support.AcceptanceTest; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -10,8 +11,8 @@ import java.util.HashMap; import java.util.Map; -import static nextstep.subway.acceptance.LineSteps.*; -import static nextstep.subway.acceptance.StationSteps.지하철역_생성_요청; +import static nextstep.subway.acceptance.support.LineSteps.*; +import static nextstep.subway.acceptance.support.StationSteps.지하철역_생성_요청; import static org.assertj.core.api.Assertions.assertThat; @DisplayName("지하철 구간 관리 기능") @@ -28,11 +29,11 @@ class SectionAcceptanceTest extends AcceptanceTest { public void setUp() { super.setUp(); - 강남역 = 지하철역_생성_요청("강남역").jsonPath().getLong("id"); - 양재역 = 지하철역_생성_요청("양재역").jsonPath().getLong("id"); + 강남역 = 지하철역_생성_요청(관리자, "강남역").jsonPath().getLong("id"); + 양재역 = 지하철역_생성_요청(관리자, "양재역").jsonPath().getLong("id"); Map lineCreateParams = createLineCreateParams(강남역, 양재역); - 신분당선 = 지하철_노선_생성_요청(lineCreateParams).jsonPath().getLong("id"); + 신분당선 = 지하철_노선_생성_요청(관리자, lineCreateParams).jsonPath().getLong("id"); } /** @@ -43,8 +44,8 @@ public void setUp() { @Test void addLineSection() { // when - Long 정자역 = 지하철역_생성_요청("정자역").jsonPath().getLong("id"); - 지하철_노선에_지하철_구간_생성_요청(신분당선, createSectionCreateParams(양재역, 정자역)); + Long 정자역 = 지하철역_생성_요청(관리자, "정자역").jsonPath().getLong("id"); + 지하철_노선에_지하철_구간_생성_요청(관리자, 신분당선, createSectionCreateParams(양재역, 정자역)); // then ExtractableResponse response = 지하철_노선_조회_요청(신분당선); @@ -61,11 +62,11 @@ void addLineSection() { @Test void removeLineSection() { // given - Long 정자역 = 지하철역_생성_요청("정자역").jsonPath().getLong("id"); - 지하철_노선에_지하철_구간_생성_요청(신분당선, createSectionCreateParams(양재역, 정자역)); + Long 정자역 = 지하철역_생성_요청(관리자, "정자역").jsonPath().getLong("id"); + 지하철_노선에_지하철_구간_생성_요청(관리자, 신분당선, createSectionCreateParams(양재역, 정자역)); // when - 지하철_노선에_지하철_구간_제거_요청(신분당선, 정자역); + 지하철_노선에_지하철_구간_제거_요청(관리자, 신분당선, 정자역); // then ExtractableResponse response = 지하철_노선_조회_요청(신분당선); diff --git a/src/test/java/nextstep/subway/acceptance/StationAcceptanceTest.java b/src/test/java/nextstep/subway/acceptance/StationAcceptanceTest.java index 52fd9cb7d..814d91444 100644 --- a/src/test/java/nextstep/subway/acceptance/StationAcceptanceTest.java +++ b/src/test/java/nextstep/subway/acceptance/StationAcceptanceTest.java @@ -3,6 +3,7 @@ import io.restassured.RestAssured; import io.restassured.response.ExtractableResponse; import io.restassured.response.Response; +import nextstep.subway.acceptance.support.AcceptanceTest; import nextstep.subway.applicaion.dto.StationResponse; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -10,7 +11,8 @@ import java.util.List; -import static nextstep.subway.acceptance.StationSteps.지하철역_생성_요청; +import static nextstep.subway.acceptance.support.StationSteps.지하철역_삭제_요청; +import static nextstep.subway.acceptance.support.StationSteps.지하철역_생성_요청; import static org.assertj.core.api.Assertions.assertThat; @DisplayName("지하철역 관련 기능") @@ -25,7 +27,7 @@ public class StationAcceptanceTest extends AcceptanceTest { @Test void createStation() { // when - ExtractableResponse response = 지하철역_생성_요청("강남역"); + ExtractableResponse response = 지하철역_생성_요청(관리자, "강남역"); // then assertThat(response.statusCode()).isEqualTo(HttpStatus.CREATED.value()); @@ -48,8 +50,8 @@ void createStation() { @Test void getStations() { // given - 지하철역_생성_요청("강남역"); - 지하철역_생성_요청("역삼역"); + 지하철역_생성_요청(관리자, "강남역"); + 지하철역_생성_요청(관리자, "역삼역"); // when ExtractableResponse stationResponse = RestAssured.given().log().all() @@ -71,15 +73,10 @@ void getStations() { @Test void deleteStation() { // given - ExtractableResponse createResponse = 지하철역_생성_요청("강남역"); + ExtractableResponse createResponse = 지하철역_생성_요청(관리자, "강남역"); // when - String location = createResponse.header("location"); - RestAssured.given().log().all() - .when() - .delete(location) - .then().log().all() - .extract(); + 지하철역_삭제_요청(관리자, createResponse.header("location")); // then List stationNames = diff --git a/src/test/java/nextstep/subway/acceptance/AcceptanceTest.java b/src/test/java/nextstep/subway/acceptance/support/AcceptanceTest.java similarity index 61% rename from src/test/java/nextstep/subway/acceptance/AcceptanceTest.java rename to src/test/java/nextstep/subway/acceptance/support/AcceptanceTest.java index 2187652c9..e4e6f1be5 100644 --- a/src/test/java/nextstep/subway/acceptance/AcceptanceTest.java +++ b/src/test/java/nextstep/subway/acceptance/support/AcceptanceTest.java @@ -1,6 +1,7 @@ -package nextstep.subway.acceptance; +package nextstep.subway.acceptance.support; import io.restassured.RestAssured; +import nextstep.DataLoader; import nextstep.subway.utils.DatabaseCleanup; import org.junit.jupiter.api.BeforeEach; import org.springframework.beans.factory.annotation.Autowired; @@ -8,18 +9,31 @@ import org.springframework.boot.web.server.LocalServerPort; import org.springframework.test.context.ActiveProfiles; +import static nextstep.subway.acceptance.support.MemberSteps.로그인_되어_있음; + @ActiveProfiles("test") @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class AcceptanceTest { + private static final String EMAIL = "admin@email.com"; + private static final String PASSWORD = "password"; + @LocalServerPort int port; @Autowired private DatabaseCleanup databaseCleanup; + @Autowired + private DataLoader dataLoader; + + protected String 관리자; + @BeforeEach public void setUp() { RestAssured.port = port; databaseCleanup.execute(); + dataLoader.loadData(); + + 관리자 = 로그인_되어_있음(EMAIL, PASSWORD); } } diff --git a/src/test/java/nextstep/subway/acceptance/support/AcceptanceTestSteps.java b/src/test/java/nextstep/subway/acceptance/support/AcceptanceTestSteps.java new file mode 100644 index 000000000..f6a483981 --- /dev/null +++ b/src/test/java/nextstep/subway/acceptance/support/AcceptanceTestSteps.java @@ -0,0 +1,17 @@ +package nextstep.subway.acceptance.support; + +import io.restassured.RestAssured; +import io.restassured.specification.RequestSpecification; + +public class AcceptanceTestSteps { + static RequestSpecification given() { + return RestAssured + .given().log().all(); + } + + static RequestSpecification given(String token) { + return RestAssured + .given().log().all() + .auth().oauth2(token); + } +} diff --git a/src/test/java/nextstep/subway/acceptance/support/LineSteps.java b/src/test/java/nextstep/subway/acceptance/support/LineSteps.java new file mode 100644 index 000000000..06d4e931a --- /dev/null +++ b/src/test/java/nextstep/subway/acceptance/support/LineSteps.java @@ -0,0 +1,80 @@ +package nextstep.subway.acceptance.support; + +import io.restassured.RestAssured; +import io.restassured.response.ExtractableResponse; +import io.restassured.response.Response; +import org.springframework.http.MediaType; + +import java.util.HashMap; +import java.util.Map; + +public class LineSteps extends AcceptanceTestSteps { + public static ExtractableResponse 지하철_노선_생성_요청(String token, String name, String color) { + Map params = new HashMap<>(); + params.put("name", name); + params.put("color", color); + return given(token) + .body(params) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .when().post("/lines") + .then().log().all().extract(); + } + + public static ExtractableResponse 지하철_노선_목록_조회_요청() { + return given() + .when().get("/lines") + .then().log().all().extract(); + } + + public static ExtractableResponse 지하철_노선_조회_요청(ExtractableResponse createResponse) { + return given() + .when().get(createResponse.header("location")) + .then().log().all().extract(); + } + + public static ExtractableResponse 지하철_노선_조회_요청(Long id) { + return given() + .when().get("/lines/{id}", id) + .then().log().all().extract(); + } + + public static ExtractableResponse 지하철_노선_생성_요청(String token, Map params) { + return given(token) + .body(params) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .when().post("/lines") + .then().log().all().extract(); + } + + public static ExtractableResponse 지하철_노선_수정_요청(String token, String location) { + Map params = new HashMap<>(); + params.put("color", "red"); + return given(token) + .body(params) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .when().put(location) + .then().log().all().extract(); + } + + + public static ExtractableResponse 지하철_노선_삭제_요청(String token, String location) { + ExtractableResponse response = given(token) + .when().delete(location) + .then().log().all().extract(); + return response; + } + + public static ExtractableResponse 지하철_노선에_지하철_구간_생성_요청(String token, Long lineId, Map params) { + return given(token) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .body(params) + .when().post("/lines/{lineId}/sections", lineId) + .then().log().all().extract(); + } + + public static ExtractableResponse 지하철_노선에_지하철_구간_제거_요청(String token, Long lineId, Long stationId) { + return given(token) + .when().delete("/lines/{lineId}/sections?stationId={stationId}", lineId, stationId) + .then().log().all().extract(); + } +} diff --git a/src/test/java/nextstep/subway/acceptance/MemberSteps.java b/src/test/java/nextstep/subway/acceptance/support/MemberSteps.java similarity index 97% rename from src/test/java/nextstep/subway/acceptance/MemberSteps.java rename to src/test/java/nextstep/subway/acceptance/support/MemberSteps.java index ff370ad00..9e116df67 100644 --- a/src/test/java/nextstep/subway/acceptance/MemberSteps.java +++ b/src/test/java/nextstep/subway/acceptance/support/MemberSteps.java @@ -1,4 +1,4 @@ -package nextstep.subway.acceptance; +package nextstep.subway.acceptance.support; import io.restassured.RestAssured; import io.restassured.response.ExtractableResponse; @@ -11,7 +11,7 @@ import static org.assertj.core.api.Assertions.assertThat; -public class MemberSteps { +public class MemberSteps extends AcceptanceTestSteps{ public static final String USERNAME_FIELD = "username"; public static final String PASSWORD_FIELD = "password"; diff --git a/src/test/java/nextstep/subway/acceptance/StationSteps.java b/src/test/java/nextstep/subway/acceptance/support/StationSteps.java similarity index 58% rename from src/test/java/nextstep/subway/acceptance/StationSteps.java rename to src/test/java/nextstep/subway/acceptance/support/StationSteps.java index 92078c1ac..431bd44d9 100644 --- a/src/test/java/nextstep/subway/acceptance/StationSteps.java +++ b/src/test/java/nextstep/subway/acceptance/support/StationSteps.java @@ -1,6 +1,5 @@ -package nextstep.subway.acceptance; +package nextstep.subway.acceptance.support; -import io.restassured.RestAssured; import io.restassured.response.ExtractableResponse; import io.restassured.response.Response; import org.springframework.http.MediaType; @@ -8,11 +7,11 @@ import java.util.HashMap; import java.util.Map; -public class StationSteps { - public static ExtractableResponse 지하철역_생성_요청(String name) { +public class StationSteps extends AcceptanceTestSteps{ + public static ExtractableResponse 지하철역_생성_요청(String token, String name) { Map params = new HashMap<>(); params.put("name", name); - return RestAssured.given().log().all() + return given(token) .body(params) .contentType(MediaType.APPLICATION_JSON_VALUE) .when() @@ -20,4 +19,11 @@ public class StationSteps { .then().log().all() .extract(); } + + public static ExtractableResponse 지하철역_삭제_요청(String token, String location) { + return given(token) + .delete(location) + .then().log().all() + .extract(); + } } From 7730e747f886c37d11dd89a4e3e245accb04a125 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=ED=98=84=EC=88=98?= Date: Mon, 15 Aug 2022 17:55:51 +0900 Subject: [PATCH 02/27] =?UTF-8?q?feat:=20=EA=B6=8C=ED=95=9C=20enum=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/nextstep/auth/secured/Secured.java | 4 +++- .../auth/secured/SecuredAnnotationChecker.java | 2 +- src/main/java/nextstep/subway/ui/LineController.java | 11 ++++++----- .../java/nextstep/subway/ui/StationController.java | 5 +++-- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/main/java/nextstep/auth/secured/Secured.java b/src/main/java/nextstep/auth/secured/Secured.java index 5be255e51..d98916eb3 100644 --- a/src/main/java/nextstep/auth/secured/Secured.java +++ b/src/main/java/nextstep/auth/secured/Secured.java @@ -1,5 +1,7 @@ package nextstep.auth.secured; +import nextstep.member.domain.RoleType; + import java.lang.annotation.*; @Target({ElementType.METHOD, ElementType.TYPE}) @@ -7,5 +9,5 @@ @Inherited @Documented public @interface Secured { - String[] value(); + RoleType[] value(); } diff --git a/src/main/java/nextstep/auth/secured/SecuredAnnotationChecker.java b/src/main/java/nextstep/auth/secured/SecuredAnnotationChecker.java index ee6f2e3f5..9a729d1bc 100644 --- a/src/main/java/nextstep/auth/secured/SecuredAnnotationChecker.java +++ b/src/main/java/nextstep/auth/secured/SecuredAnnotationChecker.java @@ -21,7 +21,7 @@ public void checkAuthorities(JoinPoint joinPoint) { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); Secured secured = method.getAnnotation(Secured.class); - List values = Arrays.stream(secured.value()).collect(Collectors.toList()); + List values = Arrays.stream(secured.value()).map(Enum::name).collect(Collectors.toList()); Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); authentication.getAuthorities().stream() diff --git a/src/main/java/nextstep/subway/ui/LineController.java b/src/main/java/nextstep/subway/ui/LineController.java index 81ec1afda..809203102 100644 --- a/src/main/java/nextstep/subway/ui/LineController.java +++ b/src/main/java/nextstep/subway/ui/LineController.java @@ -1,6 +1,7 @@ package nextstep.subway.ui; import nextstep.auth.secured.Secured; +import nextstep.member.domain.RoleType; import nextstep.subway.applicaion.LineService; import nextstep.subway.applicaion.dto.LineRequest; import nextstep.subway.applicaion.dto.LineResponse; @@ -20,7 +21,7 @@ public LineController(LineService lineService) { this.lineService = lineService; } - @Secured("ROLE_ADMIN") + @Secured(RoleType.ROLE_ADMIN) @PostMapping public ResponseEntity createLine(@RequestBody LineRequest lineRequest) { LineResponse line = lineService.saveLine(lineRequest); @@ -39,28 +40,28 @@ public ResponseEntity getLine(@PathVariable Long id) { return ResponseEntity.ok().body(lineResponse); } - @Secured("ROLE_ADMIN") + @Secured(RoleType.ROLE_ADMIN) @PutMapping("/{id}") public ResponseEntity updateLine(@PathVariable Long id, @RequestBody LineRequest lineRequest) { lineService.updateLine(id, lineRequest); return ResponseEntity.ok().build(); } - @Secured("ROLE_ADMIN") + @Secured(RoleType.ROLE_ADMIN) @DeleteMapping("/{id}") public ResponseEntity updateLine(@PathVariable Long id) { lineService.deleteLine(id); return ResponseEntity.noContent().build(); } - @Secured("ROLE_ADMIN") + @Secured(RoleType.ROLE_ADMIN) @PostMapping("/{lineId}/sections") public ResponseEntity addSection(@PathVariable Long lineId, @RequestBody SectionRequest sectionRequest) { lineService.addSection(lineId, sectionRequest); return ResponseEntity.ok().build(); } - @Secured("ROLE_ADMIN") + @Secured(RoleType.ROLE_ADMIN) @DeleteMapping("/{lineId}/sections") public ResponseEntity deleteSection(@PathVariable Long lineId, @RequestParam Long stationId) { lineService.deleteSection(lineId, stationId); diff --git a/src/main/java/nextstep/subway/ui/StationController.java b/src/main/java/nextstep/subway/ui/StationController.java index 08224b7f0..39009d2d3 100644 --- a/src/main/java/nextstep/subway/ui/StationController.java +++ b/src/main/java/nextstep/subway/ui/StationController.java @@ -1,6 +1,7 @@ package nextstep.subway.ui; import nextstep.auth.secured.Secured; +import nextstep.member.domain.RoleType; import nextstep.subway.applicaion.StationService; import nextstep.subway.applicaion.dto.StationRequest; import nextstep.subway.applicaion.dto.StationResponse; @@ -19,7 +20,7 @@ public StationController(StationService stationService) { this.stationService = stationService; } - @Secured("ROLE_ADMIN") + @Secured(RoleType.ROLE_ADMIN) @PostMapping("/stations") public ResponseEntity createStation(@RequestBody StationRequest stationRequest) { StationResponse station = stationService.saveStation(stationRequest); @@ -31,7 +32,7 @@ public ResponseEntity> showStations() { return ResponseEntity.ok().body(stationService.findAllStations()); } - @Secured("ROLE_ADMIN") + @Secured(RoleType.ROLE_ADMIN) @DeleteMapping("/stations/{id}") public ResponseEntity deleteStation(@PathVariable Long id) { stationService.deleteStationById(id); From bf6f801741e5f1eb449545a9c4ba7eb8600b1e9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=ED=98=84=EC=88=98?= Date: Mon, 15 Aug 2022 21:46:39 +0900 Subject: [PATCH 03/27] =?UTF-8?q?refactor:=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=20=EC=9C=84=EC=B9=98=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../subway/acceptance/AuthAcceptanceTest.java | 20 ------------------ .../acceptance/support/MemberSteps.java | 21 +++++++++++++++++++ 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/src/test/java/nextstep/subway/acceptance/AuthAcceptanceTest.java b/src/test/java/nextstep/subway/acceptance/AuthAcceptanceTest.java index 5346d7e95..b3f10c6e6 100644 --- a/src/test/java/nextstep/subway/acceptance/AuthAcceptanceTest.java +++ b/src/test/java/nextstep/subway/acceptance/AuthAcceptanceTest.java @@ -43,24 +43,4 @@ void myInfoWithBearerAuth() { 회원_정보_조회됨(response, EMAIL, AGE); } - - public static ExtractableResponse 폼_로그인_후_내_회원_정보_조회_요청(String email, String password) { - return RestAssured - .given().log().all() - .auth().form(email, password, new FormAuthConfig("/login/form", USERNAME_FIELD, PASSWORD_FIELD)) - .accept(MediaType.APPLICATION_JSON_VALUE) - .when().get("/members/me") - .then().log().all() - .statusCode(HttpStatus.OK.value()).extract(); - } - - public static ExtractableResponse 베어러_인증으로_내_회원_정보_조회_요청(String accessToken) { - return RestAssured.given().log().all() - .auth().oauth2(accessToken) - .accept(MediaType.APPLICATION_JSON_VALUE) - .when().get("/members/me") - .then().log().all() - .statusCode(HttpStatus.OK.value()) - .extract(); - } } diff --git a/src/test/java/nextstep/subway/acceptance/support/MemberSteps.java b/src/test/java/nextstep/subway/acceptance/support/MemberSteps.java index 9e116df67..a24d593ae 100644 --- a/src/test/java/nextstep/subway/acceptance/support/MemberSteps.java +++ b/src/test/java/nextstep/subway/acceptance/support/MemberSteps.java @@ -1,6 +1,7 @@ package nextstep.subway.acceptance.support; import io.restassured.RestAssured; +import io.restassured.authentication.FormAuthConfig; import io.restassured.response.ExtractableResponse; import io.restassured.response.Response; import org.springframework.http.HttpStatus; @@ -91,6 +92,26 @@ public class MemberSteps extends AcceptanceTestSteps{ .extract(); } + public static ExtractableResponse 폼_로그인_후_내_회원_정보_조회_요청(String email, String password) { + return RestAssured + .given().log().all() + .auth().form(email, password, new FormAuthConfig("/login/form", USERNAME_FIELD, PASSWORD_FIELD)) + .accept(MediaType.APPLICATION_JSON_VALUE) + .when().get("/members/me") + .then().log().all() + .statusCode(HttpStatus.OK.value()).extract(); + } + + public static ExtractableResponse 베어러_인증으로_내_회원_정보_조회_요청(String accessToken) { + return RestAssured.given().log().all() + .auth().oauth2(accessToken) + .accept(MediaType.APPLICATION_JSON_VALUE) + .when().get("/members/me") + .then().log().all() + .statusCode(HttpStatus.OK.value()) + .extract(); + } + public static void 회원_정보_조회됨(ExtractableResponse response, String email, int age) { assertThat(response.jsonPath().getString("id")).isNotNull(); assertThat(response.jsonPath().getString("email")).isEqualTo(email); From 0ebcbd64590826f6299bd77578a7ab0e63354bcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=ED=98=84=EC=88=98?= Date: Wed, 17 Aug 2022 01:50:08 +0900 Subject: [PATCH 04/27] =?UTF-8?q?feat:=20custom=20exception=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../secured/SecuredAnnotationChecker.java | 13 ++- .../java/nextstep/common/CommonResponse.java | 28 +++++++ .../common/exception/CustomException.java | 14 ++++ .../exception/GlobalExceptionHandler.java | 62 ++++++++++++++ .../common/exception/code/CommonCode.java | 19 +++++ .../common/exception/code/LineCode.java | 18 ++++ .../common/exception/code/PathCode.java | 17 ++++ .../common/exception/code/ResponseCode.java | 8 ++ .../common/exception/code/SectionCode.java | 18 ++++ .../common/exception/code/StationCode.java | 17 ++++ .../nextstep/member/ui/MemberController.java | 2 +- .../subway/acceptance/LineAcceptanceTest.java | 54 +++++++----- .../acceptance/support/MemberSteps.java | 83 +++++++++---------- 13 files changed, 287 insertions(+), 66 deletions(-) create mode 100644 src/main/java/nextstep/common/CommonResponse.java create mode 100644 src/main/java/nextstep/common/exception/CustomException.java create mode 100644 src/main/java/nextstep/common/exception/GlobalExceptionHandler.java create mode 100644 src/main/java/nextstep/common/exception/code/CommonCode.java create mode 100644 src/main/java/nextstep/common/exception/code/LineCode.java create mode 100644 src/main/java/nextstep/common/exception/code/PathCode.java create mode 100644 src/main/java/nextstep/common/exception/code/ResponseCode.java create mode 100644 src/main/java/nextstep/common/exception/code/SectionCode.java create mode 100644 src/main/java/nextstep/common/exception/code/StationCode.java diff --git a/src/main/java/nextstep/auth/secured/SecuredAnnotationChecker.java b/src/main/java/nextstep/auth/secured/SecuredAnnotationChecker.java index 9a729d1bc..755955a04 100644 --- a/src/main/java/nextstep/auth/secured/SecuredAnnotationChecker.java +++ b/src/main/java/nextstep/auth/secured/SecuredAnnotationChecker.java @@ -2,6 +2,8 @@ import nextstep.auth.context.Authentication; import nextstep.auth.context.SecurityContextHolder; +import nextstep.common.exception.CustomException; +import nextstep.common.exception.code.CommonCode; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; @@ -11,6 +13,7 @@ import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; +import java.util.Optional; import java.util.stream.Collectors; @Aspect @@ -23,10 +26,12 @@ public void checkAuthorities(JoinPoint joinPoint) { Secured secured = method.getAnnotation(Secured.class); List values = Arrays.stream(secured.value()).map(Enum::name).collect(Collectors.toList()); - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + Authentication authentication = Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication()) + .orElseThrow(() -> new CustomException(CommonCode.AUTH_INVALID)); + authentication.getAuthorities().stream() - .filter(values::contains) - .findFirst() - .orElseThrow(() -> new RoleAuthenticationException("권한이 없습니다.")); + .filter(values::contains) + .findFirst() + .orElseThrow(() -> new CustomException(CommonCode.AUTH_INVALID)); } } diff --git a/src/main/java/nextstep/common/CommonResponse.java b/src/main/java/nextstep/common/CommonResponse.java new file mode 100644 index 000000000..4c0131bdb --- /dev/null +++ b/src/main/java/nextstep/common/CommonResponse.java @@ -0,0 +1,28 @@ +package nextstep.common; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import nextstep.common.exception.code.ResponseCode; + +@Getter +@JsonInclude(JsonInclude.Include.NON_NULL) +@AllArgsConstructor +@Builder +public class CommonResponse { + private int code; + private String message; + T data; + + public CommonResponse(ResponseCode responseCode) { + this.code = responseCode.getCode(); + this.message = responseCode.getMessage(); + } + + public CommonResponse(ResponseCode responseCode, T data) { + this.code = responseCode.getCode(); + this.message = responseCode.getMessage(); + this.data = data; + } +} \ No newline at end of file diff --git a/src/main/java/nextstep/common/exception/CustomException.java b/src/main/java/nextstep/common/exception/CustomException.java new file mode 100644 index 000000000..e5f80d750 --- /dev/null +++ b/src/main/java/nextstep/common/exception/CustomException.java @@ -0,0 +1,14 @@ +package nextstep.common.exception; + +import lombok.Getter; +import nextstep.common.exception.code.ResponseCode; + +@Getter +public class CustomException extends RuntimeException { + private final ResponseCode responseCode; + + public CustomException(ResponseCode responseCode) { + super(responseCode.toString()); + this.responseCode = responseCode; + } +} diff --git a/src/main/java/nextstep/common/exception/GlobalExceptionHandler.java b/src/main/java/nextstep/common/exception/GlobalExceptionHandler.java new file mode 100644 index 000000000..1cfb56cf7 --- /dev/null +++ b/src/main/java/nextstep/common/exception/GlobalExceptionHandler.java @@ -0,0 +1,62 @@ +package nextstep.common.exception; + +import lombok.extern.slf4j.Slf4j; +import nextstep.common.CommonResponse; +import nextstep.common.exception.code.CommonCode; +import nextstep.common.exception.code.ResponseCode; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.FieldError; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import java.util.ArrayList; +import java.util.List; + +@Slf4j +@RestControllerAdvice +public class GlobalExceptionHandler { + /** + * 비즈니스 로직 exception 처리 + */ + @ExceptionHandler(value = {CustomException.class}) + public ResponseEntity handleCustomException(CustomException ex) { + ResponseCode responseCode = ex.getResponseCode(); + CommonResponse response = new CommonResponse<>(responseCode); + return ResponseEntity.status(HttpStatus.OK).body(response); + } + + /** + * 파라미터 유효성관련 exception 처리 + */ + @ExceptionHandler(value = {MethodArgumentNotValidException.class}) + public ResponseEntity handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { + List fieldErrorMessage = getFieldErrorMessage(e.getFieldErrors()); + CommonResponse response = new CommonResponse<>(CommonCode.PARAM_INVALID, fieldErrorMessage); + return ResponseEntity.status(HttpStatus.OK).body(response); + } + + @ExceptionHandler(DataIntegrityViolationException.class) + public ResponseEntity handleIllegalArgsException(DataIntegrityViolationException e) { + return ResponseEntity.badRequest().build(); + } + + /** + * 알 수 없는 exception 처리 + */ + @ExceptionHandler(value = {Exception.class}) + public ResponseEntity handleException(Exception ex) { + log.error("--- 알 수 없는 오류 감지. ", ex); + return ResponseEntity.ok(new CommonResponse<>(CommonCode.ETC)); + } + + private List getFieldErrorMessage(List fieldErrors) { + List errors = new ArrayList<>(); + for (FieldError error : fieldErrors) { + errors.add(error.getField() + ": " + error.getDefaultMessage()); + } + return errors; + } +} diff --git a/src/main/java/nextstep/common/exception/code/CommonCode.java b/src/main/java/nextstep/common/exception/code/CommonCode.java new file mode 100644 index 000000000..c296fa80a --- /dev/null +++ b/src/main/java/nextstep/common/exception/code/CommonCode.java @@ -0,0 +1,19 @@ +package nextstep.common.exception.code; + +import lombok.Getter; + +@Getter +public enum CommonCode implements ResponseCode { + ETC(1000, "알 수 없는 오류입니다."), + PARAM_INVALID(1001, "올바르지 않은 파라미터입니다."), + AUTH_INVALID(1002, "올바르지 않는 사용자입니다."); + + private final int code; + + private final String message; + + CommonCode(int code, String message) { + this.code = code; + this.message = message; + } +} diff --git a/src/main/java/nextstep/common/exception/code/LineCode.java b/src/main/java/nextstep/common/exception/code/LineCode.java new file mode 100644 index 000000000..de309ca25 --- /dev/null +++ b/src/main/java/nextstep/common/exception/code/LineCode.java @@ -0,0 +1,18 @@ +package nextstep.common.exception.code; + +import lombok.Getter; + +@Getter +public enum LineCode implements ResponseCode { + LINE_NOT_FOUND(2000, "노선이 존재하지 않습니다."), + LINE_STATION_DUPLICATE(2001, "노선에 이미 존재하는 역입니다."); + + private final int code; + + private final String message; + + LineCode(int code, String message) { + this.code = code; + this.message = message; + } +} diff --git a/src/main/java/nextstep/common/exception/code/PathCode.java b/src/main/java/nextstep/common/exception/code/PathCode.java new file mode 100644 index 000000000..427ccd533 --- /dev/null +++ b/src/main/java/nextstep/common/exception/code/PathCode.java @@ -0,0 +1,17 @@ +package nextstep.common.exception.code; + +import lombok.Getter; + +@Getter +public enum PathCode implements ResponseCode { + NOT_LINKED(5000, "지하철역이 서로 연결되어있지 않습니다."); + + private final int code; + + private final String message; + + PathCode(int code, String message) { + this.code = code; + this.message = message; + } +} diff --git a/src/main/java/nextstep/common/exception/code/ResponseCode.java b/src/main/java/nextstep/common/exception/code/ResponseCode.java new file mode 100644 index 000000000..26bf00fd2 --- /dev/null +++ b/src/main/java/nextstep/common/exception/code/ResponseCode.java @@ -0,0 +1,8 @@ +package nextstep.common.exception.code; + + +public interface ResponseCode { + int getCode(); + + String getMessage(); +} diff --git a/src/main/java/nextstep/common/exception/code/SectionCode.java b/src/main/java/nextstep/common/exception/code/SectionCode.java new file mode 100644 index 000000000..95a0ac78c --- /dev/null +++ b/src/main/java/nextstep/common/exception/code/SectionCode.java @@ -0,0 +1,18 @@ +package nextstep.common.exception.code; + +import lombok.Getter; + +@Getter +public enum SectionCode implements ResponseCode { + SECTION_NOT_MATCH(4000, "구간 상행선과 노선 하행선과 일치하지 않습니다."), + SECTION_REMOVE_INVALID(4001, "구간을 삭제할 수 없습니다."); + + private final int code; + + private final String message; + + SectionCode(int code, String message) { + this.code = code; + this.message = message; + } +} diff --git a/src/main/java/nextstep/common/exception/code/StationCode.java b/src/main/java/nextstep/common/exception/code/StationCode.java new file mode 100644 index 000000000..5c23b7aef --- /dev/null +++ b/src/main/java/nextstep/common/exception/code/StationCode.java @@ -0,0 +1,17 @@ +package nextstep.common.exception.code; + +import lombok.Getter; + +@Getter +public enum StationCode implements ResponseCode { + STATION_NOT_FOUND(3000, "지하철역이 존재하지 않습니다."); + + private final int code; + + private final String message; + + StationCode(int code, String message) { + this.code = code; + this.message = message; + } +} diff --git a/src/main/java/nextstep/member/ui/MemberController.java b/src/main/java/nextstep/member/ui/MemberController.java index c24d48243..742acdb17 100644 --- a/src/main/java/nextstep/member/ui/MemberController.java +++ b/src/main/java/nextstep/member/ui/MemberController.java @@ -43,7 +43,7 @@ public ResponseEntity deleteMember(@PathVariable Long id) { } @GetMapping("/members/me") - public ResponseEntity findMemberOfMine(@ AuthenticationPrincipal LoginMember loginMember) { + public ResponseEntity findMemberOfMine(@AuthenticationPrincipal LoginMember loginMember) { MemberResponse member = memberService.findMember(loginMember.getEmail()); return ResponseEntity.ok().body(member); } diff --git a/src/test/java/nextstep/subway/acceptance/LineAcceptanceTest.java b/src/test/java/nextstep/subway/acceptance/LineAcceptanceTest.java index 68ba4c3b2..0cf5258eb 100644 --- a/src/test/java/nextstep/subway/acceptance/LineAcceptanceTest.java +++ b/src/test/java/nextstep/subway/acceptance/LineAcceptanceTest.java @@ -1,16 +1,12 @@ package nextstep.subway.acceptance; -import io.restassured.RestAssured; import io.restassured.response.ExtractableResponse; import io.restassured.response.Response; import nextstep.subway.acceptance.support.AcceptanceTest; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; - -import java.util.HashMap; -import java.util.Map; import static nextstep.subway.acceptance.support.LineSteps.지하철_노선_목록_조회_요청; import static nextstep.subway.acceptance.support.LineSteps.지하철_노선_삭제_요청; @@ -21,21 +17,41 @@ @DisplayName("지하철 노선 관리 기능") class LineAcceptanceTest extends AcceptanceTest { - /** - * When 지하철 노선을 생성하면 - * Then 지하철 노선 목록 조회 시 생성한 노선을 찾을 수 있다 - */ - @DisplayName("지하철 노선 생성") - @Test - void createLine() { - // when - ExtractableResponse response = 지하철_노선_생성_요청(관리자, "2호선", "green"); - - // then - assertThat(response.statusCode()).isEqualTo(HttpStatus.CREATED.value()); - ExtractableResponse listResponse = 지하철_노선_목록_조회_요청(); - assertThat(listResponse.jsonPath().getList("name")).contains("2호선"); + @Nested + class createLine{ + /** + * When 지하철 노선을 생성하면 + * Then 지하철 노선 목록 조회 시 생성한 노선을 찾을 수 있다 + */ + @DisplayName("지하철 노선 생성") + @Test + void success() { + // when + ExtractableResponse response = 지하철_노선_생성_요청(관리자, "2호선", "green"); + + // then + assertThat(response.statusCode()).isEqualTo(HttpStatus.CREATED.value()); + ExtractableResponse listResponse = 지하철_노선_목록_조회_요청(); + + assertThat(listResponse.jsonPath().getList("name")).contains("2호선"); + } + + /** + * When 관리자가 아닌 사용자가 지하철 노선을 생성하면 + * Then 예외를 발생한다. + */ + @DisplayName("지하철 노선 생성시 관리자 아닌 경우") + @Test + void throwExceptionWhenNotAdmin() { + // when + final String notAdmin = "9999"; + ExtractableResponse response = 지하철_노선_생성_요청(notAdmin, "2호선", "green"); + + // then + assertThat(response.statusCode()).isEqualTo(HttpStatus.OK.value()); + assertThat(response.jsonPath().getLong("code")).isEqualTo(1002); + } } /** diff --git a/src/test/java/nextstep/subway/acceptance/support/MemberSteps.java b/src/test/java/nextstep/subway/acceptance/support/MemberSteps.java index a24d593ae..6fb3c6bd6 100644 --- a/src/test/java/nextstep/subway/acceptance/support/MemberSteps.java +++ b/src/test/java/nextstep/subway/acceptance/support/MemberSteps.java @@ -12,7 +12,7 @@ import static org.assertj.core.api.Assertions.assertThat; -public class MemberSteps extends AcceptanceTestSteps{ +public class MemberSteps extends AcceptanceTestSteps { public static final String USERNAME_FIELD = "username"; public static final String PASSWORD_FIELD = "password"; @@ -26,12 +26,12 @@ public class MemberSteps extends AcceptanceTestSteps{ params.put("email", email); params.put("password", password); - return RestAssured.given().log().all() - .contentType(MediaType.APPLICATION_JSON_VALUE) - .body(params) - .when().post("/login/token") - .then().log().all() - .statusCode(HttpStatus.OK.value()).extract(); + return given() + .contentType(MediaType.APPLICATION_JSON_VALUE) + .body(params) + .when().post("/login/token") + .then().log().all() + .statusCode(HttpStatus.OK.value()).extract(); } public static ExtractableResponse 회원_생성_요청(String email, String password, Integer age) { @@ -40,25 +40,27 @@ public class MemberSteps extends AcceptanceTestSteps{ params.put("password", password); params.put("age", age + ""); - return RestAssured - .given().log().all() - .contentType(MediaType.APPLICATION_JSON_VALUE) - .body(params) - .when().post("/members") - .then().log().all().extract(); + return given() + .contentType(MediaType.APPLICATION_JSON_VALUE) + .body(params) + .when().post("/members") + .then().log().all().extract(); } public static ExtractableResponse 회원_정보_조회_요청(ExtractableResponse response) { String uri = response.header("Location"); - return RestAssured.given().log().all() - .accept(MediaType.APPLICATION_JSON_VALUE) - .when().get(uri) - .then().log().all() - .extract(); + return given() + .accept(MediaType.APPLICATION_JSON_VALUE) + .when().get(uri) + .then().log().all() + .extract(); } - public static ExtractableResponse 회원_정보_수정_요청(ExtractableResponse response, String email, String password, Integer age) { + public static ExtractableResponse 회원_정보_수정_요청(ExtractableResponse response, + String email, + String password, + Integer age) { String uri = response.header("Location"); Map params = new HashMap<>(); @@ -66,30 +68,28 @@ public class MemberSteps extends AcceptanceTestSteps{ params.put("password", password); params.put("age", age + ""); - return RestAssured - .given().log().all() - .contentType(MediaType.APPLICATION_JSON_VALUE) - .body(params) - .when().put(uri) - .then().log().all().extract(); + return given() + .contentType(MediaType.APPLICATION_JSON_VALUE) + .body(params) + .when().put(uri) + .then().log().all().extract(); } public static ExtractableResponse 회원_삭제_요청(ExtractableResponse response) { String uri = response.header("Location"); - return RestAssured - .given().log().all() - .when().delete(uri) - .then().log().all().extract(); + return given() + .when().delete(uri) + .then().log().all().extract(); } public static ExtractableResponse 베이직_인증으로_내_회원_정보_조회_요청(String username, String password) { return RestAssured.given().log().all() - .auth().preemptive().basic(username, password) - .accept(MediaType.APPLICATION_JSON_VALUE) - .when().get("/members/me") - .then().log().all() - .statusCode(HttpStatus.OK.value()) - .extract(); + .auth().preemptive().basic(username, password) + .accept(MediaType.APPLICATION_JSON_VALUE) + .when().get("/members/me") + .then().log().all() + .statusCode(HttpStatus.OK.value()) + .extract(); } public static ExtractableResponse 폼_로그인_후_내_회원_정보_조회_요청(String email, String password) { @@ -103,13 +103,12 @@ public class MemberSteps extends AcceptanceTestSteps{ } public static ExtractableResponse 베어러_인증으로_내_회원_정보_조회_요청(String accessToken) { - return RestAssured.given().log().all() - .auth().oauth2(accessToken) - .accept(MediaType.APPLICATION_JSON_VALUE) - .when().get("/members/me") - .then().log().all() - .statusCode(HttpStatus.OK.value()) - .extract(); + return given(accessToken) + .accept(MediaType.APPLICATION_JSON_VALUE) + .when().get("/members/me") + .then().log().all() + .statusCode(HttpStatus.OK.value()) + .extract(); } public static void 회원_정보_조회됨(ExtractableResponse response, String email, int age) { From ea1eca319637950be9affe8e0fd26fd426449aed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=ED=98=84=EC=88=98?= Date: Sun, 4 Sep 2022 20:03:41 +0900 Subject: [PATCH 05/27] =?UTF-8?q?fix:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nextstep/subway/acceptance/LineSectionAcceptanceTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/java/nextstep/subway/acceptance/LineSectionAcceptanceTest.java b/src/test/java/nextstep/subway/acceptance/LineSectionAcceptanceTest.java index ad31aaac6..f471b79ba 100644 --- a/src/test/java/nextstep/subway/acceptance/LineSectionAcceptanceTest.java +++ b/src/test/java/nextstep/subway/acceptance/LineSectionAcceptanceTest.java @@ -81,7 +81,8 @@ void addSectionAlreadyIncluded() { ExtractableResponse response = 지하철_노선에_지하철_구간_생성_요청(관리자, 신분당선, createSectionCreateParams(강남역, 양재역)); // then - assertThat(response.statusCode()).isEqualTo(HttpStatus.BAD_REQUEST.value()); + assertThat(response.statusCode()).isEqualTo(HttpStatus.OK.value()); + assertThat(response.jsonPath().getLong("code")).isEqualTo(1000); } /** From dcb9838dd08efa112c49005e30876f0a54dde4b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=ED=98=84=EC=88=98?= Date: Mon, 5 Sep 2022 23:27:23 +0900 Subject: [PATCH 06/27] =?UTF-8?q?refactor:=20XXXAuthenticationFilter?= =?UTF-8?q?=EC=9D=98=20=EA=B5=AC=EC=A1=B0=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/nextstep/auth/AuthConfig.java | 16 +++--- .../auth/authentication/BasicAuthFilter.java | 46 ++++++++++++++++ .../BasicAuthenticationFilter.java | 50 ------------------ .../authentication/BearerTokenAuthFilter.java | 37 +++++++++++++ .../BearerTokenAuthenticationFilter.java | 41 --------------- .../UsernamePasswordAuthFilter.java | 44 ++++++++++++++++ .../UsernamePasswordAuthenticationFilter.java | 52 ------------------- ...rceptor.java => TokenAuthInterceptor.java} | 22 ++++---- .../interceptor/NotProgressInterceptor.java | 21 ++++++++ .../interceptor/ProgressInterceptor.java | 21 ++++++++ .../subway/acceptance/AuthAcceptanceTest.java | 4 -- 11 files changed, 187 insertions(+), 167 deletions(-) create mode 100644 src/main/java/nextstep/auth/authentication/BasicAuthFilter.java delete mode 100644 src/main/java/nextstep/auth/authentication/BasicAuthenticationFilter.java create mode 100644 src/main/java/nextstep/auth/authentication/BearerTokenAuthFilter.java delete mode 100644 src/main/java/nextstep/auth/authentication/BearerTokenAuthenticationFilter.java create mode 100644 src/main/java/nextstep/auth/authentication/UsernamePasswordAuthFilter.java delete mode 100644 src/main/java/nextstep/auth/authentication/UsernamePasswordAuthenticationFilter.java rename src/main/java/nextstep/auth/token/{TokenAuthenticationInterceptor.java => TokenAuthInterceptor.java} (71%) create mode 100644 src/main/java/nextstep/common/interceptor/NotProgressInterceptor.java create mode 100644 src/main/java/nextstep/common/interceptor/ProgressInterceptor.java diff --git a/src/main/java/nextstep/auth/AuthConfig.java b/src/main/java/nextstep/auth/AuthConfig.java index 53e4e9835..60a75c625 100644 --- a/src/main/java/nextstep/auth/AuthConfig.java +++ b/src/main/java/nextstep/auth/AuthConfig.java @@ -1,12 +1,12 @@ package nextstep.auth; -import nextstep.auth.authentication.BasicAuthenticationFilter; -import nextstep.auth.authentication.BearerTokenAuthenticationFilter; -import nextstep.auth.authentication.UsernamePasswordAuthenticationFilter; +import nextstep.auth.authentication.UsernamePasswordAuthFilter; import nextstep.auth.authorization.AuthenticationPrincipalArgumentResolver; import nextstep.auth.context.SecurityContextPersistenceFilter; +import nextstep.auth.authentication.BasicAuthFilter; +import nextstep.auth.authentication.BearerTokenAuthFilter; +import nextstep.auth.token.TokenAuthInterceptor; import nextstep.auth.token.JwtTokenProvider; -import nextstep.auth.token.TokenAuthenticationInterceptor; import nextstep.member.application.LoginMemberService; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; @@ -27,10 +27,10 @@ public AuthConfig(LoginMemberService loginMemberService, JwtTokenProvider jwtTok @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new SecurityContextPersistenceFilter()); - registry.addInterceptor(new UsernamePasswordAuthenticationFilter(loginMemberService)).addPathPatterns("/login/form"); - registry.addInterceptor(new TokenAuthenticationInterceptor(loginMemberService, jwtTokenProvider)).addPathPatterns("/login/token"); - registry.addInterceptor(new BasicAuthenticationFilter(loginMemberService)); - registry.addInterceptor(new BearerTokenAuthenticationFilter(jwtTokenProvider)); + registry.addInterceptor(new UsernamePasswordAuthFilter(loginMemberService)).addPathPatterns("/login/form"); + registry.addInterceptor(new TokenAuthInterceptor(loginMemberService, jwtTokenProvider)).addPathPatterns("/login/token"); + registry.addInterceptor(new BasicAuthFilter(loginMemberService)); + registry.addInterceptor(new BearerTokenAuthFilter(jwtTokenProvider)); } @Override diff --git a/src/main/java/nextstep/auth/authentication/BasicAuthFilter.java b/src/main/java/nextstep/auth/authentication/BasicAuthFilter.java new file mode 100644 index 000000000..6ee0abbe1 --- /dev/null +++ b/src/main/java/nextstep/auth/authentication/BasicAuthFilter.java @@ -0,0 +1,46 @@ +package nextstep.auth.authentication; + +import lombok.RequiredArgsConstructor; +import nextstep.auth.authentication.AuthenticationException; +import nextstep.auth.authentication.AuthenticationToken; +import nextstep.auth.authentication.AuthorizationExtractor; +import nextstep.auth.authentication.AuthorizationType; +import nextstep.auth.context.Authentication; +import nextstep.auth.context.SecurityContextHolder; +import nextstep.common.interceptor.ProgressInterceptor; +import nextstep.member.application.LoginMemberService; +import nextstep.member.domain.LoginMember; +import org.apache.tomcat.util.codec.binary.Base64; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +@RequiredArgsConstructor +public class BasicAuthFilter extends ProgressInterceptor { + private final LoginMemberService loginMemberService; + + @Override + protected void execute(final HttpServletRequest request, final HttpServletResponse response) { + String authCredentials = AuthorizationExtractor.extract(request, AuthorizationType.BASIC); + String authHeader = new String(Base64.decodeBase64(authCredentials)); + + String[] splits = authHeader.split(":"); + String principal = splits[0]; + String credentials = splits[1]; + + AuthenticationToken token = new AuthenticationToken(principal, credentials); + + LoginMember loginMember = loginMemberService.loadUserByUsername(token.getPrincipal()); + if (loginMember == null) { + throw new AuthenticationException(); + } + + if (!loginMember.checkPassword(token.getCredentials())) { + throw new AuthenticationException(); + } + + Authentication authentication = new Authentication(loginMember.getEmail(), loginMember.getAuthorities()); + + SecurityContextHolder.getContext().setAuthentication(authentication); + } +} diff --git a/src/main/java/nextstep/auth/authentication/BasicAuthenticationFilter.java b/src/main/java/nextstep/auth/authentication/BasicAuthenticationFilter.java deleted file mode 100644 index 21b304050..000000000 --- a/src/main/java/nextstep/auth/authentication/BasicAuthenticationFilter.java +++ /dev/null @@ -1,50 +0,0 @@ -package nextstep.auth.authentication; - -import nextstep.auth.context.Authentication; -import nextstep.auth.context.SecurityContextHolder; -import nextstep.member.application.LoginMemberService; -import nextstep.member.domain.LoginMember; -import org.apache.tomcat.util.codec.binary.Base64; -import org.springframework.web.servlet.HandlerInterceptor; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -public class BasicAuthenticationFilter implements HandlerInterceptor { - private LoginMemberService loginMemberService; - - public BasicAuthenticationFilter(LoginMemberService loginMemberService) { - this.loginMemberService = loginMemberService; - } - - @Override - public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { - try { - String authCredentials = AuthorizationExtractor.extract(request, AuthorizationType.BASIC); - String authHeader = new String(Base64.decodeBase64(authCredentials)); - - String[] splits = authHeader.split(":"); - String principal = splits[0]; - String credentials = splits[1]; - - AuthenticationToken token = new AuthenticationToken(principal, credentials); - - LoginMember loginMember = loginMemberService.loadUserByUsername(token.getPrincipal()); - if (loginMember == null) { - throw new AuthenticationException(); - } - - if (!loginMember.checkPassword(token.getCredentials())) { - throw new AuthenticationException(); - } - - Authentication authentication = new Authentication(loginMember.getEmail(), loginMember.getAuthorities()); - - SecurityContextHolder.getContext().setAuthentication(authentication); - - return true; - } catch (Exception e) { - return true; - } - } -} diff --git a/src/main/java/nextstep/auth/authentication/BearerTokenAuthFilter.java b/src/main/java/nextstep/auth/authentication/BearerTokenAuthFilter.java new file mode 100644 index 000000000..d3e54927d --- /dev/null +++ b/src/main/java/nextstep/auth/authentication/BearerTokenAuthFilter.java @@ -0,0 +1,37 @@ +package nextstep.auth.authentication; + +import lombok.RequiredArgsConstructor; +import nextstep.auth.authentication.AuthenticationException; +import nextstep.auth.authentication.AuthenticationToken; +import nextstep.auth.authentication.AuthorizationExtractor; +import nextstep.auth.authentication.AuthorizationType; +import nextstep.auth.context.Authentication; +import nextstep.auth.context.SecurityContextHolder; +import nextstep.auth.token.JwtTokenProvider; +import nextstep.common.interceptor.ProgressInterceptor; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.List; + +@RequiredArgsConstructor +public class BearerTokenAuthFilter extends ProgressInterceptor { + private final JwtTokenProvider jwtTokenProvider; + + @Override + protected void execute(final HttpServletRequest request, final HttpServletResponse response) { + String authCredentials = AuthorizationExtractor.extract(request, AuthorizationType.BEARER); + AuthenticationToken token = new AuthenticationToken(authCredentials, authCredentials); + + if (!jwtTokenProvider.validateToken(token.getPrincipal())) { + throw new AuthenticationException(); + } + + String principal = jwtTokenProvider.getPrincipal(token.getPrincipal()); + List roles = jwtTokenProvider.getRoles(token.getPrincipal()); + + Authentication authentication = new Authentication(principal, roles); + + SecurityContextHolder.getContext().setAuthentication(authentication); + } +} diff --git a/src/main/java/nextstep/auth/authentication/BearerTokenAuthenticationFilter.java b/src/main/java/nextstep/auth/authentication/BearerTokenAuthenticationFilter.java deleted file mode 100644 index 609dbb070..000000000 --- a/src/main/java/nextstep/auth/authentication/BearerTokenAuthenticationFilter.java +++ /dev/null @@ -1,41 +0,0 @@ -package nextstep.auth.authentication; - -import nextstep.auth.context.Authentication; -import nextstep.auth.context.SecurityContextHolder; -import nextstep.auth.token.JwtTokenProvider; -import org.springframework.web.servlet.HandlerInterceptor; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.util.List; - -public class BearerTokenAuthenticationFilter implements HandlerInterceptor { - private JwtTokenProvider jwtTokenProvider; - - public BearerTokenAuthenticationFilter(JwtTokenProvider jwtTokenProvider) { - this.jwtTokenProvider = jwtTokenProvider; - } - - @Override - public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { - try { - String authCredentials = AuthorizationExtractor.extract(request, AuthorizationType.BEARER); - AuthenticationToken token = new AuthenticationToken(authCredentials, authCredentials); - - if (!jwtTokenProvider.validateToken(token.getPrincipal())) { - throw new AuthenticationException(); - } - - String principal = jwtTokenProvider.getPrincipal(token.getPrincipal()); - List roles = jwtTokenProvider.getRoles(token.getPrincipal()); - - Authentication authentication = new Authentication(principal, roles); - - SecurityContextHolder.getContext().setAuthentication(authentication); - - return true; - } catch (Exception e) { - return true; - } - } -} diff --git a/src/main/java/nextstep/auth/authentication/UsernamePasswordAuthFilter.java b/src/main/java/nextstep/auth/authentication/UsernamePasswordAuthFilter.java new file mode 100644 index 000000000..104a8f5da --- /dev/null +++ b/src/main/java/nextstep/auth/authentication/UsernamePasswordAuthFilter.java @@ -0,0 +1,44 @@ +package nextstep.auth.authentication; + +import lombok.RequiredArgsConstructor; +import nextstep.auth.context.Authentication; +import nextstep.auth.context.SecurityContextHolder; +import nextstep.common.interceptor.NotProgressInterceptor; +import nextstep.member.application.LoginMemberService; +import nextstep.member.domain.LoginMember; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.Map; + +@RequiredArgsConstructor +public class UsernamePasswordAuthFilter extends NotProgressInterceptor { + public static final String USERNAME_FIELD = "username"; + public static final String PASSWORD_FIELD = "password"; + + private final LoginMemberService loginMemberService; + + @Override + protected void execute(HttpServletRequest request, HttpServletResponse response) { + Map paramMap = request.getParameterMap(); + String username = paramMap.get(USERNAME_FIELD)[0]; + String password = paramMap.get(PASSWORD_FIELD)[0]; + + AuthenticationToken token = new AuthenticationToken(username, password); + + String principal = token.getPrincipal(); + LoginMember loginMember = loginMemberService.loadUserByUsername(principal); + + if (loginMember == null) { + throw new AuthenticationException(); + } + + if (!loginMember.checkPassword(token.getCredentials())) { + throw new AuthenticationException(); + } + + Authentication authentication = new Authentication(loginMember.getEmail(), loginMember.getAuthorities()); + + SecurityContextHolder.getContext().setAuthentication(authentication); + } +} diff --git a/src/main/java/nextstep/auth/authentication/UsernamePasswordAuthenticationFilter.java b/src/main/java/nextstep/auth/authentication/UsernamePasswordAuthenticationFilter.java deleted file mode 100644 index 4f4faf9dc..000000000 --- a/src/main/java/nextstep/auth/authentication/UsernamePasswordAuthenticationFilter.java +++ /dev/null @@ -1,52 +0,0 @@ -package nextstep.auth.authentication; - -import nextstep.auth.context.Authentication; -import nextstep.auth.context.SecurityContextHolder; -import nextstep.member.application.LoginMemberService; -import nextstep.member.domain.LoginMember; -import org.springframework.web.servlet.HandlerInterceptor; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.util.Map; - -public class UsernamePasswordAuthenticationFilter implements HandlerInterceptor { - public static final String USERNAME_FIELD = "username"; - public static final String PASSWORD_FIELD = "password"; - - private LoginMemberService loginMemberService; - - public UsernamePasswordAuthenticationFilter(LoginMemberService loginMemberService) { - this.loginMemberService = loginMemberService; - } - - @Override - public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { - try { - Map paramMap = request.getParameterMap(); - String username = paramMap.get(USERNAME_FIELD)[0]; - String password = paramMap.get(PASSWORD_FIELD)[0]; - - AuthenticationToken token = new AuthenticationToken(username, password); - - String principal = token.getPrincipal(); - LoginMember loginMember = loginMemberService.loadUserByUsername(principal); - - if (loginMember == null) { - throw new AuthenticationException(); - } - - if (!loginMember.checkPassword(token.getCredentials())) { - throw new AuthenticationException(); - } - - Authentication authentication = new Authentication(loginMember.getEmail(), loginMember.getAuthorities()); - - SecurityContextHolder.getContext().setAuthentication(authentication); - - return false; - } catch (Exception e) { - return true; - } - } -} diff --git a/src/main/java/nextstep/auth/token/TokenAuthenticationInterceptor.java b/src/main/java/nextstep/auth/token/TokenAuthInterceptor.java similarity index 71% rename from src/main/java/nextstep/auth/token/TokenAuthenticationInterceptor.java rename to src/main/java/nextstep/auth/token/TokenAuthInterceptor.java index 268443936..232fb11de 100644 --- a/src/main/java/nextstep/auth/token/TokenAuthenticationInterceptor.java +++ b/src/main/java/nextstep/auth/token/TokenAuthInterceptor.java @@ -1,27 +1,27 @@ package nextstep.auth.token; import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.RequiredArgsConstructor; import nextstep.auth.authentication.AuthenticationException; +import nextstep.auth.token.JwtTokenProvider; +import nextstep.auth.token.TokenRequest; +import nextstep.auth.token.TokenResponse; +import nextstep.common.interceptor.NotProgressInterceptor; import nextstep.member.application.LoginMemberService; import nextstep.member.domain.LoginMember; import org.springframework.http.MediaType; -import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.stream.Collectors; -public class TokenAuthenticationInterceptor implements HandlerInterceptor { - private LoginMemberService loginMemberService; - private JwtTokenProvider jwtTokenProvider; - - public TokenAuthenticationInterceptor(LoginMemberService loginMemberService, JwtTokenProvider jwtTokenProvider) { - this.loginMemberService = loginMemberService; - this.jwtTokenProvider = jwtTokenProvider; - } +@RequiredArgsConstructor +public class TokenAuthInterceptor extends NotProgressInterceptor { + private final LoginMemberService loginMemberService; + private final JwtTokenProvider jwtTokenProvider; @Override - public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + protected void execute(final HttpServletRequest request, final HttpServletResponse response) throws Exception{ String content = request.getReader().lines().collect(Collectors.joining(System.lineSeparator())); TokenRequest tokenRequest = new ObjectMapper().readValue(content, TokenRequest.class); @@ -45,7 +45,5 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons response.setStatus(HttpServletResponse.SC_OK); response.setContentType(MediaType.APPLICATION_JSON_VALUE); response.getOutputStream().print(responseToClient); - - return false; } } diff --git a/src/main/java/nextstep/common/interceptor/NotProgressInterceptor.java b/src/main/java/nextstep/common/interceptor/NotProgressInterceptor.java new file mode 100644 index 000000000..9373c4726 --- /dev/null +++ b/src/main/java/nextstep/common/interceptor/NotProgressInterceptor.java @@ -0,0 +1,21 @@ +package nextstep.common.interceptor; + +import org.springframework.web.servlet.HandlerInterceptor; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public abstract class NotProgressInterceptor implements HandlerInterceptor { + + @Override + public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler){ + try { + execute(request, response); + return false; + } catch (Exception e) { + return true; + } + } + + protected abstract void execute(HttpServletRequest request, HttpServletResponse response) throws Exception; +} diff --git a/src/main/java/nextstep/common/interceptor/ProgressInterceptor.java b/src/main/java/nextstep/common/interceptor/ProgressInterceptor.java new file mode 100644 index 000000000..6ba81929e --- /dev/null +++ b/src/main/java/nextstep/common/interceptor/ProgressInterceptor.java @@ -0,0 +1,21 @@ +package nextstep.common.interceptor; + +import org.springframework.web.servlet.HandlerInterceptor; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public abstract class ProgressInterceptor implements HandlerInterceptor { + + @Override + public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler){ + try { + execute(request, response); + return true; + } catch (Exception e) { + return true; + } + } + + protected abstract void execute(final HttpServletRequest request, final HttpServletResponse response); +} diff --git a/src/test/java/nextstep/subway/acceptance/AuthAcceptanceTest.java b/src/test/java/nextstep/subway/acceptance/AuthAcceptanceTest.java index b3f10c6e6..ac800c0db 100644 --- a/src/test/java/nextstep/subway/acceptance/AuthAcceptanceTest.java +++ b/src/test/java/nextstep/subway/acceptance/AuthAcceptanceTest.java @@ -1,14 +1,10 @@ package nextstep.subway.acceptance; -import io.restassured.RestAssured; -import io.restassured.authentication.FormAuthConfig; import io.restassured.response.ExtractableResponse; import io.restassured.response.Response; import nextstep.subway.acceptance.support.AcceptanceTest; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; import static nextstep.subway.acceptance.support.MemberSteps.*; From 8109ffaaaf7932a140aa82a52081d108a66b91bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=ED=98=84=EC=88=98?= Date: Mon, 5 Sep 2022 23:43:23 +0900 Subject: [PATCH 07/27] =?UTF-8?q?refactor:=20auth=20=ED=8C=A8=ED=82=A4?= =?UTF-8?q?=EC=A7=80=EC=99=80=20member=20=ED=8C=A8=ED=82=A4=EC=A7=80?= =?UTF-8?q?=EC=97=90=20=EB=8C=80=ED=95=9C=20=EC=9D=98=EC=A1=B4=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/nextstep/auth/AuthConfig.java | 13 +++++------ .../auth/authentication/BasicAuthFilter.java | 6 +---- .../UsernamePasswordAuthFilter.java | 2 +- .../auth/service/LoginMemberService.java | 7 ++++++ .../auth/token/TokenAuthInterceptor.java | 5 +---- ...rvice.java => LoginMemberServiceImpl.java} | 5 +++-- ...berService.java => MemberServiceImpl.java} | 4 ++-- .../nextstep/member/ui/MemberController.java | 22 +++++++++---------- 8 files changed, 31 insertions(+), 33 deletions(-) create mode 100644 src/main/java/nextstep/auth/service/LoginMemberService.java rename src/main/java/nextstep/member/application/{LoginMemberService.java => LoginMemberServiceImpl.java} (73%) rename src/main/java/nextstep/member/application/{MemberService.java => MemberServiceImpl.java} (94%) diff --git a/src/main/java/nextstep/auth/AuthConfig.java b/src/main/java/nextstep/auth/AuthConfig.java index 60a75c625..2d67716b3 100644 --- a/src/main/java/nextstep/auth/AuthConfig.java +++ b/src/main/java/nextstep/auth/AuthConfig.java @@ -1,28 +1,25 @@ package nextstep.auth; +import lombok.RequiredArgsConstructor; import nextstep.auth.authentication.UsernamePasswordAuthFilter; import nextstep.auth.authorization.AuthenticationPrincipalArgumentResolver; import nextstep.auth.context.SecurityContextPersistenceFilter; import nextstep.auth.authentication.BasicAuthFilter; import nextstep.auth.authentication.BearerTokenAuthFilter; +import nextstep.auth.service.LoginMemberService; import nextstep.auth.token.TokenAuthInterceptor; import nextstep.auth.token.JwtTokenProvider; -import nextstep.member.application.LoginMemberService; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import java.util.List; +@RequiredArgsConstructor @Configuration public class AuthConfig implements WebMvcConfigurer { - private LoginMemberService loginMemberService; - private JwtTokenProvider jwtTokenProvider; - - public AuthConfig(LoginMemberService loginMemberService, JwtTokenProvider jwtTokenProvider) { - this.loginMemberService = loginMemberService; - this.jwtTokenProvider = jwtTokenProvider; - } + private final LoginMemberService loginMemberService; + private final JwtTokenProvider jwtTokenProvider; @Override public void addInterceptors(InterceptorRegistry registry) { diff --git a/src/main/java/nextstep/auth/authentication/BasicAuthFilter.java b/src/main/java/nextstep/auth/authentication/BasicAuthFilter.java index 6ee0abbe1..971299ed7 100644 --- a/src/main/java/nextstep/auth/authentication/BasicAuthFilter.java +++ b/src/main/java/nextstep/auth/authentication/BasicAuthFilter.java @@ -1,14 +1,10 @@ package nextstep.auth.authentication; import lombok.RequiredArgsConstructor; -import nextstep.auth.authentication.AuthenticationException; -import nextstep.auth.authentication.AuthenticationToken; -import nextstep.auth.authentication.AuthorizationExtractor; -import nextstep.auth.authentication.AuthorizationType; import nextstep.auth.context.Authentication; import nextstep.auth.context.SecurityContextHolder; +import nextstep.auth.service.LoginMemberService; import nextstep.common.interceptor.ProgressInterceptor; -import nextstep.member.application.LoginMemberService; import nextstep.member.domain.LoginMember; import org.apache.tomcat.util.codec.binary.Base64; diff --git a/src/main/java/nextstep/auth/authentication/UsernamePasswordAuthFilter.java b/src/main/java/nextstep/auth/authentication/UsernamePasswordAuthFilter.java index 104a8f5da..7612e8725 100644 --- a/src/main/java/nextstep/auth/authentication/UsernamePasswordAuthFilter.java +++ b/src/main/java/nextstep/auth/authentication/UsernamePasswordAuthFilter.java @@ -3,8 +3,8 @@ import lombok.RequiredArgsConstructor; import nextstep.auth.context.Authentication; import nextstep.auth.context.SecurityContextHolder; +import nextstep.auth.service.LoginMemberService; import nextstep.common.interceptor.NotProgressInterceptor; -import nextstep.member.application.LoginMemberService; import nextstep.member.domain.LoginMember; import javax.servlet.http.HttpServletRequest; diff --git a/src/main/java/nextstep/auth/service/LoginMemberService.java b/src/main/java/nextstep/auth/service/LoginMemberService.java new file mode 100644 index 000000000..193c23eb2 --- /dev/null +++ b/src/main/java/nextstep/auth/service/LoginMemberService.java @@ -0,0 +1,7 @@ +package nextstep.auth.service; + +import nextstep.member.domain.LoginMember; + +public interface LoginMemberService { + LoginMember loadUserByUsername(String email); +} diff --git a/src/main/java/nextstep/auth/token/TokenAuthInterceptor.java b/src/main/java/nextstep/auth/token/TokenAuthInterceptor.java index 232fb11de..8a53a73d4 100644 --- a/src/main/java/nextstep/auth/token/TokenAuthInterceptor.java +++ b/src/main/java/nextstep/auth/token/TokenAuthInterceptor.java @@ -3,11 +3,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import lombok.RequiredArgsConstructor; import nextstep.auth.authentication.AuthenticationException; -import nextstep.auth.token.JwtTokenProvider; -import nextstep.auth.token.TokenRequest; -import nextstep.auth.token.TokenResponse; +import nextstep.auth.service.LoginMemberService; import nextstep.common.interceptor.NotProgressInterceptor; -import nextstep.member.application.LoginMemberService; import nextstep.member.domain.LoginMember; import org.springframework.http.MediaType; diff --git a/src/main/java/nextstep/member/application/LoginMemberService.java b/src/main/java/nextstep/member/application/LoginMemberServiceImpl.java similarity index 73% rename from src/main/java/nextstep/member/application/LoginMemberService.java rename to src/main/java/nextstep/member/application/LoginMemberServiceImpl.java index a38234f73..8c76ca72f 100644 --- a/src/main/java/nextstep/member/application/LoginMemberService.java +++ b/src/main/java/nextstep/member/application/LoginMemberServiceImpl.java @@ -1,15 +1,16 @@ package nextstep.member.application; +import nextstep.auth.service.LoginMemberService; import nextstep.member.domain.LoginMember; import nextstep.member.domain.Member; import nextstep.member.domain.MemberRepository; import org.springframework.stereotype.Service; @Service -public class LoginMemberService { +public class LoginMemberServiceImpl implements LoginMemberService { private MemberRepository memberRepository; - public LoginMemberService(MemberRepository memberRepository) { + public LoginMemberServiceImpl(MemberRepository memberRepository) { this.memberRepository = memberRepository; } diff --git a/src/main/java/nextstep/member/application/MemberService.java b/src/main/java/nextstep/member/application/MemberServiceImpl.java similarity index 94% rename from src/main/java/nextstep/member/application/MemberService.java rename to src/main/java/nextstep/member/application/MemberServiceImpl.java index b5985dd44..a5d4d0f22 100644 --- a/src/main/java/nextstep/member/application/MemberService.java +++ b/src/main/java/nextstep/member/application/MemberServiceImpl.java @@ -7,10 +7,10 @@ import org.springframework.stereotype.Service; @Service -public class MemberService { +public class MemberServiceImpl { private MemberRepository memberRepository; - public MemberService(MemberRepository memberRepository) { + public MemberServiceImpl(MemberRepository memberRepository) { this.memberRepository = memberRepository; } diff --git a/src/main/java/nextstep/member/ui/MemberController.java b/src/main/java/nextstep/member/ui/MemberController.java index 742acdb17..1eca1f3dc 100644 --- a/src/main/java/nextstep/member/ui/MemberController.java +++ b/src/main/java/nextstep/member/ui/MemberController.java @@ -1,7 +1,7 @@ package nextstep.member.ui; import nextstep.auth.authorization.AuthenticationPrincipal; -import nextstep.member.application.MemberService; +import nextstep.member.application.MemberServiceImpl; import nextstep.member.application.dto.MemberRequest; import nextstep.member.application.dto.MemberResponse; import nextstep.member.domain.LoginMember; @@ -12,51 +12,51 @@ @RestController public class MemberController { - private MemberService memberService; + private MemberServiceImpl memberServiceImpl; - public MemberController(MemberService memberService) { - this.memberService = memberService; + public MemberController(MemberServiceImpl memberServiceImpl) { + this.memberServiceImpl = memberServiceImpl; } @PostMapping("/members") public ResponseEntity createMember(@RequestBody MemberRequest request) { - MemberResponse member = memberService.createMember(request); + MemberResponse member = memberServiceImpl.createMember(request); return ResponseEntity.created(URI.create("/members/" + member.getId())).build(); } @GetMapping("/members/{id}") public ResponseEntity findMember(@PathVariable Long id) { - MemberResponse member = memberService.findMember(id); + MemberResponse member = memberServiceImpl.findMember(id); return ResponseEntity.ok().body(member); } @PutMapping("/members/{id}") public ResponseEntity updateMember(@PathVariable Long id, @RequestBody MemberRequest param) { - memberService.updateMember(id, param); + memberServiceImpl.updateMember(id, param); return ResponseEntity.ok().build(); } @DeleteMapping("/members/{id}") public ResponseEntity deleteMember(@PathVariable Long id) { - memberService.deleteMember(id); + memberServiceImpl.deleteMember(id); return ResponseEntity.noContent().build(); } @GetMapping("/members/me") public ResponseEntity findMemberOfMine(@AuthenticationPrincipal LoginMember loginMember) { - MemberResponse member = memberService.findMember(loginMember.getEmail()); + MemberResponse member = memberServiceImpl.findMember(loginMember.getEmail()); return ResponseEntity.ok().body(member); } @PutMapping("/members/me") public ResponseEntity updateMemberOfMine(@AuthenticationPrincipal LoginMember loginMember, @RequestBody MemberRequest param) { - memberService.updateMember(loginMember.getEmail(), param); + memberServiceImpl.updateMember(loginMember.getEmail(), param); return ResponseEntity.ok().build(); } @DeleteMapping("/members/me") public ResponseEntity deleteMemberOfMine(@AuthenticationPrincipal LoginMember loginMember) { - memberService.deleteMember(loginMember.getEmail()); + memberServiceImpl.deleteMember(loginMember.getEmail()); return ResponseEntity.noContent().build(); } } From 73ba170b648ce481b1f9b01eb569158601195cce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=ED=98=84=EC=88=98?= Date: Tue, 6 Sep 2022 01:29:08 +0900 Subject: [PATCH 08/27] =?UTF-8?q?refactor:=20UserDetail=20=EC=9D=98?= =?UTF-8?q?=EC=A1=B4=EC=84=B1=20=EC=97=AD=EC=A0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/nextstep/auth/AuthConfig.java | 4 +-- .../auth/authentication/BasicAuthFilter.java | 7 +++-- .../UsernamePasswordAuthFilter.java | 7 +++-- .../auth/service/LoginMemberService.java | 7 ----- .../auth/token/TokenAuthInterceptor.java | 7 +++-- .../java/nextstep/auth/user/UserDetail.java | 13 +++++++++ .../nextstep/auth/user/UserDetailService.java | 5 ++++ ...ceImpl.java => UserDetailServiceImpl.java} | 6 ++-- .../nextstep/member/domain/LoginMember.java | 28 +++++++------------ .../java/nextstep/member/domain/Member.java | 23 ++------------- 10 files changed, 48 insertions(+), 59 deletions(-) delete mode 100644 src/main/java/nextstep/auth/service/LoginMemberService.java create mode 100644 src/main/java/nextstep/auth/user/UserDetail.java create mode 100644 src/main/java/nextstep/auth/user/UserDetailService.java rename src/main/java/nextstep/member/application/{LoginMemberServiceImpl.java => UserDetailServiceImpl.java} (73%) diff --git a/src/main/java/nextstep/auth/AuthConfig.java b/src/main/java/nextstep/auth/AuthConfig.java index 2d67716b3..e17edc191 100644 --- a/src/main/java/nextstep/auth/AuthConfig.java +++ b/src/main/java/nextstep/auth/AuthConfig.java @@ -6,7 +6,7 @@ import nextstep.auth.context.SecurityContextPersistenceFilter; import nextstep.auth.authentication.BasicAuthFilter; import nextstep.auth.authentication.BearerTokenAuthFilter; -import nextstep.auth.service.LoginMemberService; +import nextstep.auth.user.UserDetailService; import nextstep.auth.token.TokenAuthInterceptor; import nextstep.auth.token.JwtTokenProvider; import org.springframework.context.annotation.Configuration; @@ -18,7 +18,7 @@ @RequiredArgsConstructor @Configuration public class AuthConfig implements WebMvcConfigurer { - private final LoginMemberService loginMemberService; + private final UserDetailService loginMemberService; private final JwtTokenProvider jwtTokenProvider; @Override diff --git a/src/main/java/nextstep/auth/authentication/BasicAuthFilter.java b/src/main/java/nextstep/auth/authentication/BasicAuthFilter.java index 971299ed7..5f57af61f 100644 --- a/src/main/java/nextstep/auth/authentication/BasicAuthFilter.java +++ b/src/main/java/nextstep/auth/authentication/BasicAuthFilter.java @@ -3,7 +3,8 @@ import lombok.RequiredArgsConstructor; import nextstep.auth.context.Authentication; import nextstep.auth.context.SecurityContextHolder; -import nextstep.auth.service.LoginMemberService; +import nextstep.auth.user.UserDetail; +import nextstep.auth.user.UserDetailService; import nextstep.common.interceptor.ProgressInterceptor; import nextstep.member.domain.LoginMember; import org.apache.tomcat.util.codec.binary.Base64; @@ -13,7 +14,7 @@ @RequiredArgsConstructor public class BasicAuthFilter extends ProgressInterceptor { - private final LoginMemberService loginMemberService; + private final UserDetailService userDetailService; @Override protected void execute(final HttpServletRequest request, final HttpServletResponse response) { @@ -26,7 +27,7 @@ protected void execute(final HttpServletRequest request, final HttpServletRespon AuthenticationToken token = new AuthenticationToken(principal, credentials); - LoginMember loginMember = loginMemberService.loadUserByUsername(token.getPrincipal()); + UserDetail loginMember = userDetailService.loadUserByUsername(token.getPrincipal()); if (loginMember == null) { throw new AuthenticationException(); } diff --git a/src/main/java/nextstep/auth/authentication/UsernamePasswordAuthFilter.java b/src/main/java/nextstep/auth/authentication/UsernamePasswordAuthFilter.java index 7612e8725..ba0e46753 100644 --- a/src/main/java/nextstep/auth/authentication/UsernamePasswordAuthFilter.java +++ b/src/main/java/nextstep/auth/authentication/UsernamePasswordAuthFilter.java @@ -3,7 +3,8 @@ import lombok.RequiredArgsConstructor; import nextstep.auth.context.Authentication; import nextstep.auth.context.SecurityContextHolder; -import nextstep.auth.service.LoginMemberService; +import nextstep.auth.user.UserDetail; +import nextstep.auth.user.UserDetailService; import nextstep.common.interceptor.NotProgressInterceptor; import nextstep.member.domain.LoginMember; @@ -16,7 +17,7 @@ public class UsernamePasswordAuthFilter extends NotProgressInterceptor { public static final String USERNAME_FIELD = "username"; public static final String PASSWORD_FIELD = "password"; - private final LoginMemberService loginMemberService; + private final UserDetailService loginMemberService; @Override protected void execute(HttpServletRequest request, HttpServletResponse response) { @@ -27,7 +28,7 @@ protected void execute(HttpServletRequest request, HttpServletResponse response) AuthenticationToken token = new AuthenticationToken(username, password); String principal = token.getPrincipal(); - LoginMember loginMember = loginMemberService.loadUserByUsername(principal); + UserDetail loginMember = loginMemberService.loadUserByUsername(principal); if (loginMember == null) { throw new AuthenticationException(); diff --git a/src/main/java/nextstep/auth/service/LoginMemberService.java b/src/main/java/nextstep/auth/service/LoginMemberService.java deleted file mode 100644 index 193c23eb2..000000000 --- a/src/main/java/nextstep/auth/service/LoginMemberService.java +++ /dev/null @@ -1,7 +0,0 @@ -package nextstep.auth.service; - -import nextstep.member.domain.LoginMember; - -public interface LoginMemberService { - LoginMember loadUserByUsername(String email); -} diff --git a/src/main/java/nextstep/auth/token/TokenAuthInterceptor.java b/src/main/java/nextstep/auth/token/TokenAuthInterceptor.java index 8a53a73d4..77f9d336a 100644 --- a/src/main/java/nextstep/auth/token/TokenAuthInterceptor.java +++ b/src/main/java/nextstep/auth/token/TokenAuthInterceptor.java @@ -3,7 +3,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import lombok.RequiredArgsConstructor; import nextstep.auth.authentication.AuthenticationException; -import nextstep.auth.service.LoginMemberService; +import nextstep.auth.user.UserDetail; +import nextstep.auth.user.UserDetailService; import nextstep.common.interceptor.NotProgressInterceptor; import nextstep.member.domain.LoginMember; import org.springframework.http.MediaType; @@ -14,7 +15,7 @@ @RequiredArgsConstructor public class TokenAuthInterceptor extends NotProgressInterceptor { - private final LoginMemberService loginMemberService; + private final UserDetailService loginMemberService; private final JwtTokenProvider jwtTokenProvider; @Override @@ -25,7 +26,7 @@ protected void execute(final HttpServletRequest request, final HttpServletRespon String principal = tokenRequest.getEmail(); String credentials = tokenRequest.getPassword(); - LoginMember loginMember = loginMemberService.loadUserByUsername(principal); + UserDetail loginMember = loginMemberService.loadUserByUsername(principal); if (loginMember == null) { throw new AuthenticationException(); diff --git a/src/main/java/nextstep/auth/user/UserDetail.java b/src/main/java/nextstep/auth/user/UserDetail.java new file mode 100644 index 000000000..410ac0753 --- /dev/null +++ b/src/main/java/nextstep/auth/user/UserDetail.java @@ -0,0 +1,13 @@ +package nextstep.auth.user; + +import java.util.List; + +public interface UserDetail { + String getEmail(); + + String getPassword(); + + List getAuthorities(); + + boolean checkPassword(String password); +} diff --git a/src/main/java/nextstep/auth/user/UserDetailService.java b/src/main/java/nextstep/auth/user/UserDetailService.java new file mode 100644 index 000000000..276bfe677 --- /dev/null +++ b/src/main/java/nextstep/auth/user/UserDetailService.java @@ -0,0 +1,5 @@ +package nextstep.auth.user; + +public interface UserDetailService { + UserDetail loadUserByUsername(String email); +} diff --git a/src/main/java/nextstep/member/application/LoginMemberServiceImpl.java b/src/main/java/nextstep/member/application/UserDetailServiceImpl.java similarity index 73% rename from src/main/java/nextstep/member/application/LoginMemberServiceImpl.java rename to src/main/java/nextstep/member/application/UserDetailServiceImpl.java index 8c76ca72f..0689627ba 100644 --- a/src/main/java/nextstep/member/application/LoginMemberServiceImpl.java +++ b/src/main/java/nextstep/member/application/UserDetailServiceImpl.java @@ -1,16 +1,16 @@ package nextstep.member.application; -import nextstep.auth.service.LoginMemberService; +import nextstep.auth.user.UserDetailService; import nextstep.member.domain.LoginMember; import nextstep.member.domain.Member; import nextstep.member.domain.MemberRepository; import org.springframework.stereotype.Service; @Service -public class LoginMemberServiceImpl implements LoginMemberService { +public class UserDetailServiceImpl implements UserDetailService { private MemberRepository memberRepository; - public LoginMemberServiceImpl(MemberRepository memberRepository) { + public UserDetailServiceImpl(MemberRepository memberRepository) { this.memberRepository = memberRepository; } diff --git a/src/main/java/nextstep/member/domain/LoginMember.java b/src/main/java/nextstep/member/domain/LoginMember.java index 1f90fadc1..b847fe96d 100644 --- a/src/main/java/nextstep/member/domain/LoginMember.java +++ b/src/main/java/nextstep/member/domain/LoginMember.java @@ -1,9 +1,18 @@ package nextstep.member.domain; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import nextstep.auth.user.UserDetail; + import java.util.List; -public class LoginMember { +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@Getter +public class LoginMember implements UserDetail { private String email; private String password; private List authorities; @@ -20,23 +29,6 @@ public static LoginMember guest() { return new LoginMember(); } - public LoginMember() { - } - - public LoginMember(String email, String password, List authorities) { - this.email = email; - this.password = password; - this.authorities = authorities; - } - - public String getEmail() { - return email; - } - - public List getAuthorities() { - return authorities; - } - public boolean checkPassword(String password) { return this.password.equals(password); } diff --git a/src/main/java/nextstep/member/domain/Member.java b/src/main/java/nextstep/member/domain/Member.java index c065bc8f0..d8ebe81aa 100644 --- a/src/main/java/nextstep/member/domain/Member.java +++ b/src/main/java/nextstep/member/domain/Member.java @@ -1,8 +1,11 @@ package nextstep.member.domain; +import lombok.Getter; + import javax.persistence.*; import java.util.List; +@Getter @Entity public class Member { @Id @@ -36,26 +39,6 @@ public Member(String email, String password, Integer age, List roles) { this.roles = roles; } - public Long getId() { - return id; - } - - public String getEmail() { - return email; - } - - public String getPassword() { - return password; - } - - public Integer getAge() { - return age; - } - - public List getRoles() { - return roles; - } - public void update(Member member) { this.email = member.email; this.password = member.password; From 3d4596629693e952f58ea805fdc97a53cea8c592 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=ED=98=84=EC=88=98?= Date: Tue, 6 Sep 2022 02:25:08 +0900 Subject: [PATCH 09/27] =?UTF-8?q?refactor:=20AuthNotChainInterceptor=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EA=B5=AC=EC=A1=B0=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/authentication/BasicAuthFilter.java | 5 +- .../authentication/BearerTokenAuthFilter.java | 8 +-- .../UsernamePasswordAuthFilter.java | 33 ++++-------- .../interceptor/AuthChainInterceptor.java} | 4 +- .../interceptor/AuthNotChainInterceptor.java | 51 +++++++++++++++++++ .../auth/token/TokenAuthInterceptor.java | 39 +++++++------- .../interceptor/NotProgressInterceptor.java | 21 -------- ...berServiceImpl.java => MemberService.java} | 4 +- ...erviceImpl.java => UserDetailService.java} | 5 +- .../nextstep/member/ui/MemberController.java | 22 ++++---- 10 files changed, 100 insertions(+), 92 deletions(-) rename src/main/java/nextstep/{common/interceptor/ProgressInterceptor.java => auth/interceptor/AuthChainInterceptor.java} (83%) create mode 100644 src/main/java/nextstep/auth/interceptor/AuthNotChainInterceptor.java delete mode 100644 src/main/java/nextstep/common/interceptor/NotProgressInterceptor.java rename src/main/java/nextstep/member/application/{MemberServiceImpl.java => MemberService.java} (94%) rename src/main/java/nextstep/member/application/{UserDetailServiceImpl.java => UserDetailService.java} (74%) diff --git a/src/main/java/nextstep/auth/authentication/BasicAuthFilter.java b/src/main/java/nextstep/auth/authentication/BasicAuthFilter.java index 5f57af61f..921912424 100644 --- a/src/main/java/nextstep/auth/authentication/BasicAuthFilter.java +++ b/src/main/java/nextstep/auth/authentication/BasicAuthFilter.java @@ -5,15 +5,14 @@ import nextstep.auth.context.SecurityContextHolder; import nextstep.auth.user.UserDetail; import nextstep.auth.user.UserDetailService; -import nextstep.common.interceptor.ProgressInterceptor; -import nextstep.member.domain.LoginMember; +import nextstep.auth.interceptor.AuthChainInterceptor; import org.apache.tomcat.util.codec.binary.Base64; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @RequiredArgsConstructor -public class BasicAuthFilter extends ProgressInterceptor { +public class BasicAuthFilter extends AuthChainInterceptor { private final UserDetailService userDetailService; @Override diff --git a/src/main/java/nextstep/auth/authentication/BearerTokenAuthFilter.java b/src/main/java/nextstep/auth/authentication/BearerTokenAuthFilter.java index d3e54927d..cb85eee19 100644 --- a/src/main/java/nextstep/auth/authentication/BearerTokenAuthFilter.java +++ b/src/main/java/nextstep/auth/authentication/BearerTokenAuthFilter.java @@ -1,21 +1,17 @@ package nextstep.auth.authentication; import lombok.RequiredArgsConstructor; -import nextstep.auth.authentication.AuthenticationException; -import nextstep.auth.authentication.AuthenticationToken; -import nextstep.auth.authentication.AuthorizationExtractor; -import nextstep.auth.authentication.AuthorizationType; import nextstep.auth.context.Authentication; import nextstep.auth.context.SecurityContextHolder; import nextstep.auth.token.JwtTokenProvider; -import nextstep.common.interceptor.ProgressInterceptor; +import nextstep.auth.interceptor.AuthChainInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.List; @RequiredArgsConstructor -public class BearerTokenAuthFilter extends ProgressInterceptor { +public class BearerTokenAuthFilter extends AuthChainInterceptor { private final JwtTokenProvider jwtTokenProvider; @Override diff --git a/src/main/java/nextstep/auth/authentication/UsernamePasswordAuthFilter.java b/src/main/java/nextstep/auth/authentication/UsernamePasswordAuthFilter.java index ba0e46753..1c8e7bfab 100644 --- a/src/main/java/nextstep/auth/authentication/UsernamePasswordAuthFilter.java +++ b/src/main/java/nextstep/auth/authentication/UsernamePasswordAuthFilter.java @@ -1,45 +1,34 @@ package nextstep.auth.authentication; -import lombok.RequiredArgsConstructor; import nextstep.auth.context.Authentication; import nextstep.auth.context.SecurityContextHolder; import nextstep.auth.user.UserDetail; +import nextstep.auth.interceptor.AuthNotChainInterceptor; import nextstep.auth.user.UserDetailService; -import nextstep.common.interceptor.NotProgressInterceptor; -import nextstep.member.domain.LoginMember; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.Map; -@RequiredArgsConstructor -public class UsernamePasswordAuthFilter extends NotProgressInterceptor { +public class UsernamePasswordAuthFilter extends AuthNotChainInterceptor { public static final String USERNAME_FIELD = "username"; public static final String PASSWORD_FIELD = "password"; - private final UserDetailService loginMemberService; + public UsernamePasswordAuthFilter(final UserDetailService userDetailService) { + super(userDetailService); + } @Override - protected void execute(HttpServletRequest request, HttpServletResponse response) { + protected AuthenticationToken createAuthToken(final HttpServletRequest request) { Map paramMap = request.getParameterMap(); String username = paramMap.get(USERNAME_FIELD)[0]; String password = paramMap.get(PASSWORD_FIELD)[0]; + return new AuthenticationToken(username, password); + } - AuthenticationToken token = new AuthenticationToken(username, password); - - String principal = token.getPrincipal(); - UserDetail loginMember = loginMemberService.loadUserByUsername(principal); - - if (loginMember == null) { - throw new AuthenticationException(); - } - - if (!loginMember.checkPassword(token.getCredentials())) { - throw new AuthenticationException(); - } - - Authentication authentication = new Authentication(loginMember.getEmail(), loginMember.getAuthorities()); - + @Override + protected void afterSuccessUserCheck(final HttpServletRequest request, final HttpServletResponse response, UserDetail userDetail) { + Authentication authentication = new Authentication(userDetail.getEmail(), userDetail.getAuthorities()); SecurityContextHolder.getContext().setAuthentication(authentication); } } diff --git a/src/main/java/nextstep/common/interceptor/ProgressInterceptor.java b/src/main/java/nextstep/auth/interceptor/AuthChainInterceptor.java similarity index 83% rename from src/main/java/nextstep/common/interceptor/ProgressInterceptor.java rename to src/main/java/nextstep/auth/interceptor/AuthChainInterceptor.java index 6ba81929e..06f4cf7d4 100644 --- a/src/main/java/nextstep/common/interceptor/ProgressInterceptor.java +++ b/src/main/java/nextstep/auth/interceptor/AuthChainInterceptor.java @@ -1,11 +1,11 @@ -package nextstep.common.interceptor; +package nextstep.auth.interceptor; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -public abstract class ProgressInterceptor implements HandlerInterceptor { +public abstract class AuthChainInterceptor implements HandlerInterceptor { @Override public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler){ diff --git a/src/main/java/nextstep/auth/interceptor/AuthNotChainInterceptor.java b/src/main/java/nextstep/auth/interceptor/AuthNotChainInterceptor.java new file mode 100644 index 000000000..43f1a1b90 --- /dev/null +++ b/src/main/java/nextstep/auth/interceptor/AuthNotChainInterceptor.java @@ -0,0 +1,51 @@ +package nextstep.auth.interceptor; + +import lombok.RequiredArgsConstructor; +import nextstep.auth.authentication.AuthenticationException; +import nextstep.auth.authentication.AuthenticationToken; +import nextstep.auth.user.UserDetail; +import nextstep.auth.user.UserDetailService; +import org.springframework.web.servlet.HandlerInterceptor; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +@RequiredArgsConstructor +public abstract class AuthNotChainInterceptor implements HandlerInterceptor { + protected final UserDetailService userDetailService; + + @Override + public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler){ + try { + AuthenticationToken authToken = createAuthToken(request); + checkValidUser(authToken); + UserDetail userDetail = getUserDetail(authToken); + afterSuccessUserCheck(request, response, userDetail); + return false; + } catch (Exception e) { + return true; + } + } + + protected abstract AuthenticationToken createAuthToken(final HttpServletRequest request) throws IOException; + + protected abstract void afterSuccessUserCheck(final HttpServletRequest request, final HttpServletResponse response, UserDetail userDetail) + throws IOException; + + protected void checkValidUser(AuthenticationToken authToken){ + UserDetail loginMember = getUserDetail(authToken); + + if (loginMember == null) { + throw new AuthenticationException(); + } + + if (!loginMember.checkPassword(authToken.getCredentials())) { + throw new AuthenticationException(); + } + } + + private UserDetail getUserDetail(final AuthenticationToken authToken) { + return userDetailService.loadUserByUsername(authToken.getPrincipal()); + } +} diff --git a/src/main/java/nextstep/auth/token/TokenAuthInterceptor.java b/src/main/java/nextstep/auth/token/TokenAuthInterceptor.java index 77f9d336a..480a2e499 100644 --- a/src/main/java/nextstep/auth/token/TokenAuthInterceptor.java +++ b/src/main/java/nextstep/auth/token/TokenAuthInterceptor.java @@ -1,42 +1,37 @@ package nextstep.auth.token; import com.fasterxml.jackson.databind.ObjectMapper; -import lombok.RequiredArgsConstructor; -import nextstep.auth.authentication.AuthenticationException; +import nextstep.auth.authentication.AuthenticationToken; import nextstep.auth.user.UserDetail; +import nextstep.auth.interceptor.AuthNotChainInterceptor; import nextstep.auth.user.UserDetailService; -import nextstep.common.interceptor.NotProgressInterceptor; -import nextstep.member.domain.LoginMember; import org.springframework.http.MediaType; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import java.io.IOException; import java.util.stream.Collectors; -@RequiredArgsConstructor -public class TokenAuthInterceptor extends NotProgressInterceptor { - private final UserDetailService loginMemberService; +public class TokenAuthInterceptor extends AuthNotChainInterceptor { private final JwtTokenProvider jwtTokenProvider; + public TokenAuthInterceptor(final UserDetailService userDetailService, final JwtTokenProvider jwtTokenProvider) { + super(userDetailService); + this.jwtTokenProvider = jwtTokenProvider; + } + @Override - protected void execute(final HttpServletRequest request, final HttpServletResponse response) throws Exception{ + protected AuthenticationToken createAuthToken(final HttpServletRequest request) throws IOException { String content = request.getReader().lines().collect(Collectors.joining(System.lineSeparator())); TokenRequest tokenRequest = new ObjectMapper().readValue(content, TokenRequest.class); + return new AuthenticationToken(tokenRequest.getEmail(), tokenRequest.getPassword()); + } - String principal = tokenRequest.getEmail(); - String credentials = tokenRequest.getPassword(); - - UserDetail loginMember = loginMemberService.loadUserByUsername(principal); - - if (loginMember == null) { - throw new AuthenticationException(); - } - - if (!loginMember.checkPassword(credentials)) { - throw new AuthenticationException(); - } - - String token = jwtTokenProvider.createToken(loginMember.getEmail(), loginMember.getAuthorities()); + @Override + protected void afterSuccessUserCheck(final HttpServletRequest request, + final HttpServletResponse response, + final UserDetail userDetail) throws IOException { + String token = jwtTokenProvider.createToken(userDetail.getEmail(), userDetail.getAuthorities()); TokenResponse tokenResponse = new TokenResponse(token); String responseToClient = new ObjectMapper().writeValueAsString(tokenResponse); diff --git a/src/main/java/nextstep/common/interceptor/NotProgressInterceptor.java b/src/main/java/nextstep/common/interceptor/NotProgressInterceptor.java deleted file mode 100644 index 9373c4726..000000000 --- a/src/main/java/nextstep/common/interceptor/NotProgressInterceptor.java +++ /dev/null @@ -1,21 +0,0 @@ -package nextstep.common.interceptor; - -import org.springframework.web.servlet.HandlerInterceptor; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -public abstract class NotProgressInterceptor implements HandlerInterceptor { - - @Override - public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler){ - try { - execute(request, response); - return false; - } catch (Exception e) { - return true; - } - } - - protected abstract void execute(HttpServletRequest request, HttpServletResponse response) throws Exception; -} diff --git a/src/main/java/nextstep/member/application/MemberServiceImpl.java b/src/main/java/nextstep/member/application/MemberService.java similarity index 94% rename from src/main/java/nextstep/member/application/MemberServiceImpl.java rename to src/main/java/nextstep/member/application/MemberService.java index a5d4d0f22..b5985dd44 100644 --- a/src/main/java/nextstep/member/application/MemberServiceImpl.java +++ b/src/main/java/nextstep/member/application/MemberService.java @@ -7,10 +7,10 @@ import org.springframework.stereotype.Service; @Service -public class MemberServiceImpl { +public class MemberService { private MemberRepository memberRepository; - public MemberServiceImpl(MemberRepository memberRepository) { + public MemberService(MemberRepository memberRepository) { this.memberRepository = memberRepository; } diff --git a/src/main/java/nextstep/member/application/UserDetailServiceImpl.java b/src/main/java/nextstep/member/application/UserDetailService.java similarity index 74% rename from src/main/java/nextstep/member/application/UserDetailServiceImpl.java rename to src/main/java/nextstep/member/application/UserDetailService.java index 0689627ba..4113b2b0c 100644 --- a/src/main/java/nextstep/member/application/UserDetailServiceImpl.java +++ b/src/main/java/nextstep/member/application/UserDetailService.java @@ -1,16 +1,15 @@ package nextstep.member.application; -import nextstep.auth.user.UserDetailService; import nextstep.member.domain.LoginMember; import nextstep.member.domain.Member; import nextstep.member.domain.MemberRepository; import org.springframework.stereotype.Service; @Service -public class UserDetailServiceImpl implements UserDetailService { +public class UserDetailService implements nextstep.auth.user.UserDetailService { private MemberRepository memberRepository; - public UserDetailServiceImpl(MemberRepository memberRepository) { + public UserDetailService(MemberRepository memberRepository) { this.memberRepository = memberRepository; } diff --git a/src/main/java/nextstep/member/ui/MemberController.java b/src/main/java/nextstep/member/ui/MemberController.java index 1eca1f3dc..742acdb17 100644 --- a/src/main/java/nextstep/member/ui/MemberController.java +++ b/src/main/java/nextstep/member/ui/MemberController.java @@ -1,7 +1,7 @@ package nextstep.member.ui; import nextstep.auth.authorization.AuthenticationPrincipal; -import nextstep.member.application.MemberServiceImpl; +import nextstep.member.application.MemberService; import nextstep.member.application.dto.MemberRequest; import nextstep.member.application.dto.MemberResponse; import nextstep.member.domain.LoginMember; @@ -12,51 +12,51 @@ @RestController public class MemberController { - private MemberServiceImpl memberServiceImpl; + private MemberService memberService; - public MemberController(MemberServiceImpl memberServiceImpl) { - this.memberServiceImpl = memberServiceImpl; + public MemberController(MemberService memberService) { + this.memberService = memberService; } @PostMapping("/members") public ResponseEntity createMember(@RequestBody MemberRequest request) { - MemberResponse member = memberServiceImpl.createMember(request); + MemberResponse member = memberService.createMember(request); return ResponseEntity.created(URI.create("/members/" + member.getId())).build(); } @GetMapping("/members/{id}") public ResponseEntity findMember(@PathVariable Long id) { - MemberResponse member = memberServiceImpl.findMember(id); + MemberResponse member = memberService.findMember(id); return ResponseEntity.ok().body(member); } @PutMapping("/members/{id}") public ResponseEntity updateMember(@PathVariable Long id, @RequestBody MemberRequest param) { - memberServiceImpl.updateMember(id, param); + memberService.updateMember(id, param); return ResponseEntity.ok().build(); } @DeleteMapping("/members/{id}") public ResponseEntity deleteMember(@PathVariable Long id) { - memberServiceImpl.deleteMember(id); + memberService.deleteMember(id); return ResponseEntity.noContent().build(); } @GetMapping("/members/me") public ResponseEntity findMemberOfMine(@AuthenticationPrincipal LoginMember loginMember) { - MemberResponse member = memberServiceImpl.findMember(loginMember.getEmail()); + MemberResponse member = memberService.findMember(loginMember.getEmail()); return ResponseEntity.ok().body(member); } @PutMapping("/members/me") public ResponseEntity updateMemberOfMine(@AuthenticationPrincipal LoginMember loginMember, @RequestBody MemberRequest param) { - memberServiceImpl.updateMember(loginMember.getEmail(), param); + memberService.updateMember(loginMember.getEmail(), param); return ResponseEntity.ok().build(); } @DeleteMapping("/members/me") public ResponseEntity deleteMemberOfMine(@AuthenticationPrincipal LoginMember loginMember) { - memberServiceImpl.deleteMember(loginMember.getEmail()); + memberService.deleteMember(loginMember.getEmail()); return ResponseEntity.noContent().build(); } } From 9570e2bec580d4008dfd6fb436aa1575b7de7eca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=ED=98=84=EC=88=98?= Date: Tue, 6 Sep 2022 02:39:21 +0900 Subject: [PATCH 10/27] =?UTF-8?q?refactor:=20AuthChainInterceptor=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EA=B5=AC=EC=A1=B0=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/authentication/BasicAuthFilter.java | 29 ++++++++++--------- .../authentication/BearerTokenAuthFilter.java | 16 +++++----- .../interceptor/AuthChainInterceptor.java | 18 ++++++++++-- 3 files changed, 39 insertions(+), 24 deletions(-) diff --git a/src/main/java/nextstep/auth/authentication/BasicAuthFilter.java b/src/main/java/nextstep/auth/authentication/BasicAuthFilter.java index 921912424..52400bc11 100644 --- a/src/main/java/nextstep/auth/authentication/BasicAuthFilter.java +++ b/src/main/java/nextstep/auth/authentication/BasicAuthFilter.java @@ -15,28 +15,29 @@ public class BasicAuthFilter extends AuthChainInterceptor { private final UserDetailService userDetailService; - @Override - protected void execute(final HttpServletRequest request, final HttpServletResponse response) { - String authCredentials = AuthorizationExtractor.extract(request, AuthorizationType.BASIC); - String authHeader = new String(Base64.decodeBase64(authCredentials)); - - String[] splits = authHeader.split(":"); - String principal = splits[0]; - String credentials = splits[1]; - - AuthenticationToken token = new AuthenticationToken(principal, credentials); - + protected void checkValidAuth(final AuthenticationToken token) { UserDetail loginMember = userDetailService.loadUserByUsername(token.getPrincipal()); if (loginMember == null) { throw new AuthenticationException(); } - if (!loginMember.checkPassword(token.getCredentials())) { throw new AuthenticationException(); } + } - Authentication authentication = new Authentication(loginMember.getEmail(), loginMember.getAuthorities()); + protected Authentication getAuthentication(final AuthenticationToken token) { + UserDetail loginMember = userDetailService.loadUserByUsername(token.getPrincipal()); + return new Authentication(loginMember.getEmail(), loginMember.getAuthorities()); + } + + protected AuthenticationToken getAuthenticationToken(final HttpServletRequest request) { + String authCredentials = AuthorizationExtractor.extract(request, AuthorizationType.BASIC); + String authHeader = new String(Base64.decodeBase64(authCredentials)); + + String[] splits = authHeader.split(":"); + String principal = splits[0]; + String credentials = splits[1]; - SecurityContextHolder.getContext().setAuthentication(authentication); + return new AuthenticationToken(principal, credentials); } } diff --git a/src/main/java/nextstep/auth/authentication/BearerTokenAuthFilter.java b/src/main/java/nextstep/auth/authentication/BearerTokenAuthFilter.java index cb85eee19..66b7ea163 100644 --- a/src/main/java/nextstep/auth/authentication/BearerTokenAuthFilter.java +++ b/src/main/java/nextstep/auth/authentication/BearerTokenAuthFilter.java @@ -2,32 +2,32 @@ import lombok.RequiredArgsConstructor; import nextstep.auth.context.Authentication; -import nextstep.auth.context.SecurityContextHolder; import nextstep.auth.token.JwtTokenProvider; import nextstep.auth.interceptor.AuthChainInterceptor; import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import java.util.List; @RequiredArgsConstructor public class BearerTokenAuthFilter extends AuthChainInterceptor { private final JwtTokenProvider jwtTokenProvider; - @Override - protected void execute(final HttpServletRequest request, final HttpServletResponse response) { - String authCredentials = AuthorizationExtractor.extract(request, AuthorizationType.BEARER); - AuthenticationToken token = new AuthenticationToken(authCredentials, authCredentials); - + protected void checkValidAuth(final AuthenticationToken token) { if (!jwtTokenProvider.validateToken(token.getPrincipal())) { throw new AuthenticationException(); } + } + protected Authentication getAuthentication(final AuthenticationToken token) { String principal = jwtTokenProvider.getPrincipal(token.getPrincipal()); List roles = jwtTokenProvider.getRoles(token.getPrincipal()); Authentication authentication = new Authentication(principal, roles); + return authentication; + } - SecurityContextHolder.getContext().setAuthentication(authentication); + protected AuthenticationToken getAuthenticationToken(final HttpServletRequest request) { + String authCredentials = AuthorizationExtractor.extract(request, AuthorizationType.BEARER); + return new AuthenticationToken(authCredentials, authCredentials); } } diff --git a/src/main/java/nextstep/auth/interceptor/AuthChainInterceptor.java b/src/main/java/nextstep/auth/interceptor/AuthChainInterceptor.java index 06f4cf7d4..639ce4e37 100644 --- a/src/main/java/nextstep/auth/interceptor/AuthChainInterceptor.java +++ b/src/main/java/nextstep/auth/interceptor/AuthChainInterceptor.java @@ -1,5 +1,8 @@ package nextstep.auth.interceptor; +import nextstep.auth.authentication.AuthenticationToken; +import nextstep.auth.context.Authentication; +import nextstep.auth.context.SecurityContextHolder; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; @@ -10,12 +13,23 @@ public abstract class AuthChainInterceptor implements HandlerInterceptor { @Override public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler){ try { - execute(request, response); + AuthenticationToken token = getAuthenticationToken(request); + checkValidAuth(token); + saveAuthContext(token); return true; } catch (Exception e) { return true; } } - protected abstract void execute(final HttpServletRequest request, final HttpServletResponse response); + protected abstract void checkValidAuth(final AuthenticationToken token); + + protected abstract Authentication getAuthentication(final AuthenticationToken token); + + protected abstract AuthenticationToken getAuthenticationToken(final HttpServletRequest request); + + private void saveAuthContext(final AuthenticationToken token) { + Authentication authentication = getAuthentication(token); + SecurityContextHolder.getContext().setAuthentication(authentication); + } } From 4aa8fb11643b58dcd7a4bbdae732718d7f7ac442 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=ED=98=84=EC=88=98?= Date: Wed, 7 Sep 2022 02:54:59 +0900 Subject: [PATCH 11/27] =?UTF-8?q?style:=20=ED=81=B4=EB=9E=98=EC=8A=A4?= =?UTF-8?q?=EB=AA=85=20UserDetailService=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{UserDetailService.java => LoginMemberService.java} | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) rename src/main/java/nextstep/member/application/{UserDetailService.java => LoginMemberService.java} (75%) diff --git a/src/main/java/nextstep/member/application/UserDetailService.java b/src/main/java/nextstep/member/application/LoginMemberService.java similarity index 75% rename from src/main/java/nextstep/member/application/UserDetailService.java rename to src/main/java/nextstep/member/application/LoginMemberService.java index 4113b2b0c..90442b38a 100644 --- a/src/main/java/nextstep/member/application/UserDetailService.java +++ b/src/main/java/nextstep/member/application/LoginMemberService.java @@ -1,15 +1,16 @@ package nextstep.member.application; +import nextstep.auth.user.UserDetailService; import nextstep.member.domain.LoginMember; import nextstep.member.domain.Member; import nextstep.member.domain.MemberRepository; import org.springframework.stereotype.Service; @Service -public class UserDetailService implements nextstep.auth.user.UserDetailService { +public class LoginMemberService implements UserDetailService { private MemberRepository memberRepository; - public UserDetailService(MemberRepository memberRepository) { + public LoginMemberService(MemberRepository memberRepository) { this.memberRepository = memberRepository; } From 18e19c07554014a0a3fda0bf38becf97a1176cea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=ED=98=84=EC=88=98?= Date: Thu, 8 Sep 2022 03:23:36 +0900 Subject: [PATCH 12/27] =?UTF-8?q?refactor:=20given()=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EA=B0=84=EC=86=8C=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../subway/acceptance/support/MemberSteps.java | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/test/java/nextstep/subway/acceptance/support/MemberSteps.java b/src/test/java/nextstep/subway/acceptance/support/MemberSteps.java index 6fb3c6bd6..6811bd166 100644 --- a/src/test/java/nextstep/subway/acceptance/support/MemberSteps.java +++ b/src/test/java/nextstep/subway/acceptance/support/MemberSteps.java @@ -1,6 +1,5 @@ package nextstep.subway.acceptance.support; -import io.restassured.RestAssured; import io.restassured.authentication.FormAuthConfig; import io.restassured.response.ExtractableResponse; import io.restassured.response.Response; @@ -83,18 +82,17 @@ public class MemberSteps extends AcceptanceTestSteps { } public static ExtractableResponse 베이직_인증으로_내_회원_정보_조회_요청(String username, String password) { - return RestAssured.given().log().all() - .auth().preemptive().basic(username, password) - .accept(MediaType.APPLICATION_JSON_VALUE) - .when().get("/members/me") - .then().log().all() - .statusCode(HttpStatus.OK.value()) - .extract(); + return given() + .auth().preemptive().basic(username, password) + .accept(MediaType.APPLICATION_JSON_VALUE) + .when().get("/members/me") + .then().log().all() + .statusCode(HttpStatus.OK.value()) + .extract(); } public static ExtractableResponse 폼_로그인_후_내_회원_정보_조회_요청(String email, String password) { - return RestAssured - .given().log().all() + return given() .auth().form(email, password, new FormAuthConfig("/login/form", USERNAME_FIELD, PASSWORD_FIELD)) .accept(MediaType.APPLICATION_JSON_VALUE) .when().get("/members/me") From 797aff77655f6b8c759c1a076abdc098ff1c74aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=ED=98=84=EC=88=98?= Date: Tue, 13 Sep 2022 01:53:34 +0900 Subject: [PATCH 13/27] =?UTF-8?q?feat:=20@Overide=20=ED=83=9C=EA=B7=B8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/nextstep/auth/authentication/BasicAuthFilter.java | 5 +++-- .../nextstep/auth/authentication/BearerTokenAuthFilter.java | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/nextstep/auth/authentication/BasicAuthFilter.java b/src/main/java/nextstep/auth/authentication/BasicAuthFilter.java index 52400bc11..4b82b5432 100644 --- a/src/main/java/nextstep/auth/authentication/BasicAuthFilter.java +++ b/src/main/java/nextstep/auth/authentication/BasicAuthFilter.java @@ -2,19 +2,18 @@ import lombok.RequiredArgsConstructor; import nextstep.auth.context.Authentication; -import nextstep.auth.context.SecurityContextHolder; import nextstep.auth.user.UserDetail; import nextstep.auth.user.UserDetailService; import nextstep.auth.interceptor.AuthChainInterceptor; import org.apache.tomcat.util.codec.binary.Base64; import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; @RequiredArgsConstructor public class BasicAuthFilter extends AuthChainInterceptor { private final UserDetailService userDetailService; + @Override protected void checkValidAuth(final AuthenticationToken token) { UserDetail loginMember = userDetailService.loadUserByUsername(token.getPrincipal()); if (loginMember == null) { @@ -25,11 +24,13 @@ protected void checkValidAuth(final AuthenticationToken token) { } } + @Override protected Authentication getAuthentication(final AuthenticationToken token) { UserDetail loginMember = userDetailService.loadUserByUsername(token.getPrincipal()); return new Authentication(loginMember.getEmail(), loginMember.getAuthorities()); } + @Override protected AuthenticationToken getAuthenticationToken(final HttpServletRequest request) { String authCredentials = AuthorizationExtractor.extract(request, AuthorizationType.BASIC); String authHeader = new String(Base64.decodeBase64(authCredentials)); diff --git a/src/main/java/nextstep/auth/authentication/BearerTokenAuthFilter.java b/src/main/java/nextstep/auth/authentication/BearerTokenAuthFilter.java index 66b7ea163..b843d77fe 100644 --- a/src/main/java/nextstep/auth/authentication/BearerTokenAuthFilter.java +++ b/src/main/java/nextstep/auth/authentication/BearerTokenAuthFilter.java @@ -12,12 +12,14 @@ public class BearerTokenAuthFilter extends AuthChainInterceptor { private final JwtTokenProvider jwtTokenProvider; + @Override protected void checkValidAuth(final AuthenticationToken token) { if (!jwtTokenProvider.validateToken(token.getPrincipal())) { throw new AuthenticationException(); } } + @Override protected Authentication getAuthentication(final AuthenticationToken token) { String principal = jwtTokenProvider.getPrincipal(token.getPrincipal()); List roles = jwtTokenProvider.getRoles(token.getPrincipal()); @@ -26,6 +28,7 @@ protected Authentication getAuthentication(final AuthenticationToken token) { return authentication; } + @Override protected AuthenticationToken getAuthenticationToken(final HttpServletRequest request) { String authCredentials = AuthorizationExtractor.extract(request, AuthorizationType.BEARER); return new AuthenticationToken(authCredentials, authCredentials); From 1859e9ba2d429f96c0b2921b3444c7bbac0cdbb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=ED=98=84=EC=88=98?= Date: Tue, 13 Sep 2022 01:55:15 +0900 Subject: [PATCH 14/27] =?UTF-8?q?style:=20userdetailservice=20=EB=B3=80?= =?UTF-8?q?=EC=88=98=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/nextstep/auth/AuthConfig.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/nextstep/auth/AuthConfig.java b/src/main/java/nextstep/auth/AuthConfig.java index e17edc191..90d9ad340 100644 --- a/src/main/java/nextstep/auth/AuthConfig.java +++ b/src/main/java/nextstep/auth/AuthConfig.java @@ -18,15 +18,15 @@ @RequiredArgsConstructor @Configuration public class AuthConfig implements WebMvcConfigurer { - private final UserDetailService loginMemberService; + private final UserDetailService userDetailService; private final JwtTokenProvider jwtTokenProvider; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new SecurityContextPersistenceFilter()); - registry.addInterceptor(new UsernamePasswordAuthFilter(loginMemberService)).addPathPatterns("/login/form"); - registry.addInterceptor(new TokenAuthInterceptor(loginMemberService, jwtTokenProvider)).addPathPatterns("/login/token"); - registry.addInterceptor(new BasicAuthFilter(loginMemberService)); + registry.addInterceptor(new UsernamePasswordAuthFilter(userDetailService)).addPathPatterns("/login/form"); + registry.addInterceptor(new TokenAuthInterceptor(userDetailService, jwtTokenProvider)).addPathPatterns("/login/token"); + registry.addInterceptor(new BasicAuthFilter(userDetailService)); registry.addInterceptor(new BearerTokenAuthFilter(jwtTokenProvider)); } From 6b735384e88a3bc23858b63d9579bea1bbdc7f98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=ED=98=84=EC=88=98?= Date: Tue, 13 Sep 2022 02:29:03 +0900 Subject: [PATCH 15/27] =?UTF-8?q?style:=20=ED=81=B4=EB=9E=98=EC=8A=A4?= =?UTF-8?q?=EB=AA=85=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=EB=AA=85=20=ED=86=B5=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/nextstep/auth/authentication/BasicAuthFilter.java | 6 +++--- .../nextstep/auth/authentication/BearerTokenAuthFilter.java | 6 +++--- ...ainInterceptor.java => AuthContextChainInterceptor.java} | 6 +++--- .../nextstep/auth/interceptor/AuthNotChainInterceptor.java | 4 ++-- src/main/java/nextstep/common/CommonResponse.java | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) rename src/main/java/nextstep/auth/interceptor/{AuthChainInterceptor.java => AuthContextChainInterceptor.java} (80%) diff --git a/src/main/java/nextstep/auth/authentication/BasicAuthFilter.java b/src/main/java/nextstep/auth/authentication/BasicAuthFilter.java index 4b82b5432..4f7024777 100644 --- a/src/main/java/nextstep/auth/authentication/BasicAuthFilter.java +++ b/src/main/java/nextstep/auth/authentication/BasicAuthFilter.java @@ -4,13 +4,13 @@ import nextstep.auth.context.Authentication; import nextstep.auth.user.UserDetail; import nextstep.auth.user.UserDetailService; -import nextstep.auth.interceptor.AuthChainInterceptor; +import nextstep.auth.interceptor.AuthContextChainInterceptor; import org.apache.tomcat.util.codec.binary.Base64; import javax.servlet.http.HttpServletRequest; @RequiredArgsConstructor -public class BasicAuthFilter extends AuthChainInterceptor { +public class BasicAuthFilter extends AuthContextChainInterceptor { private final UserDetailService userDetailService; @Override @@ -31,7 +31,7 @@ protected Authentication getAuthentication(final AuthenticationToken token) { } @Override - protected AuthenticationToken getAuthenticationToken(final HttpServletRequest request) { + protected AuthenticationToken createAuthToken(final HttpServletRequest request) { String authCredentials = AuthorizationExtractor.extract(request, AuthorizationType.BASIC); String authHeader = new String(Base64.decodeBase64(authCredentials)); diff --git a/src/main/java/nextstep/auth/authentication/BearerTokenAuthFilter.java b/src/main/java/nextstep/auth/authentication/BearerTokenAuthFilter.java index b843d77fe..03f842432 100644 --- a/src/main/java/nextstep/auth/authentication/BearerTokenAuthFilter.java +++ b/src/main/java/nextstep/auth/authentication/BearerTokenAuthFilter.java @@ -3,13 +3,13 @@ import lombok.RequiredArgsConstructor; import nextstep.auth.context.Authentication; import nextstep.auth.token.JwtTokenProvider; -import nextstep.auth.interceptor.AuthChainInterceptor; +import nextstep.auth.interceptor.AuthContextChainInterceptor; import javax.servlet.http.HttpServletRequest; import java.util.List; @RequiredArgsConstructor -public class BearerTokenAuthFilter extends AuthChainInterceptor { +public class BearerTokenAuthFilter extends AuthContextChainInterceptor { private final JwtTokenProvider jwtTokenProvider; @Override @@ -29,7 +29,7 @@ protected Authentication getAuthentication(final AuthenticationToken token) { } @Override - protected AuthenticationToken getAuthenticationToken(final HttpServletRequest request) { + protected AuthenticationToken createAuthToken(final HttpServletRequest request) { String authCredentials = AuthorizationExtractor.extract(request, AuthorizationType.BEARER); return new AuthenticationToken(authCredentials, authCredentials); } diff --git a/src/main/java/nextstep/auth/interceptor/AuthChainInterceptor.java b/src/main/java/nextstep/auth/interceptor/AuthContextChainInterceptor.java similarity index 80% rename from src/main/java/nextstep/auth/interceptor/AuthChainInterceptor.java rename to src/main/java/nextstep/auth/interceptor/AuthContextChainInterceptor.java index 639ce4e37..1745be99e 100644 --- a/src/main/java/nextstep/auth/interceptor/AuthChainInterceptor.java +++ b/src/main/java/nextstep/auth/interceptor/AuthContextChainInterceptor.java @@ -8,12 +8,12 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -public abstract class AuthChainInterceptor implements HandlerInterceptor { +public abstract class AuthContextChainInterceptor implements HandlerInterceptor { @Override public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler){ try { - AuthenticationToken token = getAuthenticationToken(request); + AuthenticationToken token = createAuthToken(request); checkValidAuth(token); saveAuthContext(token); return true; @@ -26,7 +26,7 @@ public boolean preHandle(final HttpServletRequest request, final HttpServletResp protected abstract Authentication getAuthentication(final AuthenticationToken token); - protected abstract AuthenticationToken getAuthenticationToken(final HttpServletRequest request); + protected abstract AuthenticationToken createAuthToken(final HttpServletRequest request); private void saveAuthContext(final AuthenticationToken token) { Authentication authentication = getAuthentication(token); diff --git a/src/main/java/nextstep/auth/interceptor/AuthNotChainInterceptor.java b/src/main/java/nextstep/auth/interceptor/AuthNotChainInterceptor.java index 43f1a1b90..64a2c024e 100644 --- a/src/main/java/nextstep/auth/interceptor/AuthNotChainInterceptor.java +++ b/src/main/java/nextstep/auth/interceptor/AuthNotChainInterceptor.java @@ -19,7 +19,7 @@ public abstract class AuthNotChainInterceptor implements HandlerInterceptor { public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler){ try { AuthenticationToken authToken = createAuthToken(request); - checkValidUser(authToken); + checkValidAuth(authToken); UserDetail userDetail = getUserDetail(authToken); afterSuccessUserCheck(request, response, userDetail); return false; @@ -33,7 +33,7 @@ public boolean preHandle(final HttpServletRequest request, final HttpServletResp protected abstract void afterSuccessUserCheck(final HttpServletRequest request, final HttpServletResponse response, UserDetail userDetail) throws IOException; - protected void checkValidUser(AuthenticationToken authToken){ + protected void checkValidAuth(AuthenticationToken authToken){ UserDetail loginMember = getUserDetail(authToken); if (loginMember == null) { diff --git a/src/main/java/nextstep/common/CommonResponse.java b/src/main/java/nextstep/common/CommonResponse.java index 4c0131bdb..2349fbbeb 100644 --- a/src/main/java/nextstep/common/CommonResponse.java +++ b/src/main/java/nextstep/common/CommonResponse.java @@ -13,7 +13,7 @@ public class CommonResponse { private int code; private String message; - T data; + private T data; public CommonResponse(ResponseCode responseCode) { this.code = responseCode.getCode(); From b7948055672de0d8181bfd7a624113eb12509eec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=ED=98=84=EC=88=98?= Date: Thu, 15 Sep 2022 02:28:09 +0900 Subject: [PATCH 16/27] =?UTF-8?q?refactor:=20AuthenticationPrincipalArgume?= =?UTF-8?q?ntResolver=20=ED=81=B4=EB=9E=98=EC=8A=A4=20member=20=ED=8C=A8?= =?UTF-8?q?=ED=82=A4=EC=A7=80=20=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0=20-=20LoginMember=20=ED=81=B4=EB=9E=98=EC=8A=A4=20aut?= =?UTF-8?q?h=20=ED=8C=A8=ED=82=A4=EC=A7=80=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= =?UTF-8?q?=20=EB=B0=8F=20User=EB=A1=9C=20=EB=AA=85=EC=B9=AD=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20-=20LoginMember=20=ED=81=B4=EB=9E=98=EC=8A=A4=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99=EC=9C=BC=EB=A1=9C=20=EC=88=9C=ED=99=98?= =?UTF-8?q?=EC=9D=B4=20=EC=A0=9C=EA=B1=B0=EB=90=98=EC=96=B4=20=EA=B8=B0?= =?UTF-8?q?=EC=A1=B4=20UserDetail=EC=9D=B4=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=98=EC=97=AC=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/authentication/BasicAuthFilter.java | 6 ++-- .../UsernamePasswordAuthFilter.java | 6 ++-- ...thenticationPrincipalArgumentResolver.java | 8 ++--- .../interceptor/AuthNotChainInterceptor.java | 12 +++---- .../auth/token/TokenAuthInterceptor.java | 6 ++-- src/main/java/nextstep/auth/user/User.java | 33 +++++++++++++++++ .../java/nextstep/auth/user/UserDetail.java | 13 ------- .../nextstep/auth/user/UserDetailService.java | 2 +- .../application/LoginMemberService.java | 6 ++-- .../nextstep/member/domain/LoginMember.java | 35 ------------------- .../nextstep/member/ui/MemberController.java | 8 ++--- 11 files changed, 60 insertions(+), 75 deletions(-) create mode 100644 src/main/java/nextstep/auth/user/User.java delete mode 100644 src/main/java/nextstep/auth/user/UserDetail.java delete mode 100644 src/main/java/nextstep/member/domain/LoginMember.java diff --git a/src/main/java/nextstep/auth/authentication/BasicAuthFilter.java b/src/main/java/nextstep/auth/authentication/BasicAuthFilter.java index 4f7024777..101af28ce 100644 --- a/src/main/java/nextstep/auth/authentication/BasicAuthFilter.java +++ b/src/main/java/nextstep/auth/authentication/BasicAuthFilter.java @@ -2,7 +2,7 @@ import lombok.RequiredArgsConstructor; import nextstep.auth.context.Authentication; -import nextstep.auth.user.UserDetail; +import nextstep.auth.user.User; import nextstep.auth.user.UserDetailService; import nextstep.auth.interceptor.AuthContextChainInterceptor; import org.apache.tomcat.util.codec.binary.Base64; @@ -15,7 +15,7 @@ public class BasicAuthFilter extends AuthContextChainInterceptor { @Override protected void checkValidAuth(final AuthenticationToken token) { - UserDetail loginMember = userDetailService.loadUserByUsername(token.getPrincipal()); + User loginMember = userDetailService.loadUserByUsername(token.getPrincipal()); if (loginMember == null) { throw new AuthenticationException(); } @@ -26,7 +26,7 @@ protected void checkValidAuth(final AuthenticationToken token) { @Override protected Authentication getAuthentication(final AuthenticationToken token) { - UserDetail loginMember = userDetailService.loadUserByUsername(token.getPrincipal()); + User loginMember = userDetailService.loadUserByUsername(token.getPrincipal()); return new Authentication(loginMember.getEmail(), loginMember.getAuthorities()); } diff --git a/src/main/java/nextstep/auth/authentication/UsernamePasswordAuthFilter.java b/src/main/java/nextstep/auth/authentication/UsernamePasswordAuthFilter.java index 1c8e7bfab..dcc5327bf 100644 --- a/src/main/java/nextstep/auth/authentication/UsernamePasswordAuthFilter.java +++ b/src/main/java/nextstep/auth/authentication/UsernamePasswordAuthFilter.java @@ -2,8 +2,8 @@ import nextstep.auth.context.Authentication; import nextstep.auth.context.SecurityContextHolder; -import nextstep.auth.user.UserDetail; import nextstep.auth.interceptor.AuthNotChainInterceptor; +import nextstep.auth.user.User; import nextstep.auth.user.UserDetailService; import javax.servlet.http.HttpServletRequest; @@ -27,8 +27,8 @@ protected AuthenticationToken createAuthToken(final HttpServletRequest request) } @Override - protected void afterSuccessUserCheck(final HttpServletRequest request, final HttpServletResponse response, UserDetail userDetail) { - Authentication authentication = new Authentication(userDetail.getEmail(), userDetail.getAuthorities()); + protected void afterSuccessUserCheck(final HttpServletRequest request, final HttpServletResponse response, User user) { + Authentication authentication = new Authentication(user.getEmail(), user.getAuthorities()); SecurityContextHolder.getContext().setAuthentication(authentication); } } diff --git a/src/main/java/nextstep/auth/authorization/AuthenticationPrincipalArgumentResolver.java b/src/main/java/nextstep/auth/authorization/AuthenticationPrincipalArgumentResolver.java index 10317cab0..a96603745 100644 --- a/src/main/java/nextstep/auth/authorization/AuthenticationPrincipalArgumentResolver.java +++ b/src/main/java/nextstep/auth/authorization/AuthenticationPrincipalArgumentResolver.java @@ -2,7 +2,7 @@ import nextstep.auth.context.Authentication; import nextstep.auth.context.SecurityContextHolder; -import nextstep.member.domain.LoginMember; +import nextstep.auth.user.User; import org.springframework.core.MethodParameter; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; @@ -16,12 +16,12 @@ public boolean supportsParameter(MethodParameter parameter) { } @Override - public LoginMember resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) { + public User resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication == null) { - return LoginMember.guest(); + return User.guest(); } - return LoginMember.of(authentication.getPrincipal().toString(), authentication.getAuthorities()); + return User.of(authentication.getPrincipal().toString(), authentication.getAuthorities()); } } diff --git a/src/main/java/nextstep/auth/interceptor/AuthNotChainInterceptor.java b/src/main/java/nextstep/auth/interceptor/AuthNotChainInterceptor.java index 64a2c024e..c31ca2840 100644 --- a/src/main/java/nextstep/auth/interceptor/AuthNotChainInterceptor.java +++ b/src/main/java/nextstep/auth/interceptor/AuthNotChainInterceptor.java @@ -3,7 +3,7 @@ import lombok.RequiredArgsConstructor; import nextstep.auth.authentication.AuthenticationException; import nextstep.auth.authentication.AuthenticationToken; -import nextstep.auth.user.UserDetail; +import nextstep.auth.user.User; import nextstep.auth.user.UserDetailService; import org.springframework.web.servlet.HandlerInterceptor; @@ -20,8 +20,8 @@ public boolean preHandle(final HttpServletRequest request, final HttpServletResp try { AuthenticationToken authToken = createAuthToken(request); checkValidAuth(authToken); - UserDetail userDetail = getUserDetail(authToken); - afterSuccessUserCheck(request, response, userDetail); + User user = getUserDetail(authToken); + afterSuccessUserCheck(request, response, user); return false; } catch (Exception e) { return true; @@ -30,11 +30,11 @@ public boolean preHandle(final HttpServletRequest request, final HttpServletResp protected abstract AuthenticationToken createAuthToken(final HttpServletRequest request) throws IOException; - protected abstract void afterSuccessUserCheck(final HttpServletRequest request, final HttpServletResponse response, UserDetail userDetail) + protected abstract void afterSuccessUserCheck(final HttpServletRequest request, final HttpServletResponse response, User user) throws IOException; protected void checkValidAuth(AuthenticationToken authToken){ - UserDetail loginMember = getUserDetail(authToken); + User loginMember = getUserDetail(authToken); if (loginMember == null) { throw new AuthenticationException(); @@ -45,7 +45,7 @@ protected void checkValidAuth(AuthenticationToken authToken){ } } - private UserDetail getUserDetail(final AuthenticationToken authToken) { + private User getUserDetail(final AuthenticationToken authToken) { return userDetailService.loadUserByUsername(authToken.getPrincipal()); } } diff --git a/src/main/java/nextstep/auth/token/TokenAuthInterceptor.java b/src/main/java/nextstep/auth/token/TokenAuthInterceptor.java index 480a2e499..1982d576e 100644 --- a/src/main/java/nextstep/auth/token/TokenAuthInterceptor.java +++ b/src/main/java/nextstep/auth/token/TokenAuthInterceptor.java @@ -2,7 +2,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import nextstep.auth.authentication.AuthenticationToken; -import nextstep.auth.user.UserDetail; +import nextstep.auth.user.User; import nextstep.auth.interceptor.AuthNotChainInterceptor; import nextstep.auth.user.UserDetailService; import org.springframework.http.MediaType; @@ -30,8 +30,8 @@ protected AuthenticationToken createAuthToken(final HttpServletRequest request) @Override protected void afterSuccessUserCheck(final HttpServletRequest request, final HttpServletResponse response, - final UserDetail userDetail) throws IOException { - String token = jwtTokenProvider.createToken(userDetail.getEmail(), userDetail.getAuthorities()); + final User user) throws IOException { + String token = jwtTokenProvider.createToken(user.getEmail(), user.getAuthorities()); TokenResponse tokenResponse = new TokenResponse(token); String responseToClient = new ObjectMapper().writeValueAsString(tokenResponse); diff --git a/src/main/java/nextstep/auth/user/User.java b/src/main/java/nextstep/auth/user/User.java new file mode 100644 index 000000000..3c4927440 --- /dev/null +++ b/src/main/java/nextstep/auth/user/User.java @@ -0,0 +1,33 @@ +package nextstep.auth.user; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.List; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@Getter +public class User { + private String email; + private String password; + private List authorities; + + public static User of(String email, List authorities) { + return new User(email, null, authorities); + } + + public static User of(String email, String password, List authorities) { + return new User(email, password, authorities); + } + + public static User guest() { + return new User(); + } + + public boolean checkPassword(final String password) { + return this.password.equals(password); + } +} diff --git a/src/main/java/nextstep/auth/user/UserDetail.java b/src/main/java/nextstep/auth/user/UserDetail.java deleted file mode 100644 index 410ac0753..000000000 --- a/src/main/java/nextstep/auth/user/UserDetail.java +++ /dev/null @@ -1,13 +0,0 @@ -package nextstep.auth.user; - -import java.util.List; - -public interface UserDetail { - String getEmail(); - - String getPassword(); - - List getAuthorities(); - - boolean checkPassword(String password); -} diff --git a/src/main/java/nextstep/auth/user/UserDetailService.java b/src/main/java/nextstep/auth/user/UserDetailService.java index 276bfe677..779c11028 100644 --- a/src/main/java/nextstep/auth/user/UserDetailService.java +++ b/src/main/java/nextstep/auth/user/UserDetailService.java @@ -1,5 +1,5 @@ package nextstep.auth.user; public interface UserDetailService { - UserDetail loadUserByUsername(String email); + User loadUserByUsername(String email); } diff --git a/src/main/java/nextstep/member/application/LoginMemberService.java b/src/main/java/nextstep/member/application/LoginMemberService.java index 90442b38a..c8d46ce52 100644 --- a/src/main/java/nextstep/member/application/LoginMemberService.java +++ b/src/main/java/nextstep/member/application/LoginMemberService.java @@ -1,7 +1,7 @@ package nextstep.member.application; +import nextstep.auth.user.User; import nextstep.auth.user.UserDetailService; -import nextstep.member.domain.LoginMember; import nextstep.member.domain.Member; import nextstep.member.domain.MemberRepository; import org.springframework.stereotype.Service; @@ -14,8 +14,8 @@ public LoginMemberService(MemberRepository memberRepository) { this.memberRepository = memberRepository; } - public LoginMember loadUserByUsername(String email) { + public User loadUserByUsername(String email) { Member member = memberRepository.findByEmail(email).orElseThrow(RuntimeException::new); - return LoginMember.of(member); + return User.of(member.getEmail(), member.getPassword(), member.getRoles()); } } diff --git a/src/main/java/nextstep/member/domain/LoginMember.java b/src/main/java/nextstep/member/domain/LoginMember.java deleted file mode 100644 index b847fe96d..000000000 --- a/src/main/java/nextstep/member/domain/LoginMember.java +++ /dev/null @@ -1,35 +0,0 @@ -package nextstep.member.domain; - - -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import nextstep.auth.user.UserDetail; - -import java.util.List; - -@NoArgsConstructor(access = AccessLevel.PRIVATE) -@AllArgsConstructor(access = AccessLevel.PRIVATE) -@Getter -public class LoginMember implements UserDetail { - private String email; - private String password; - private List authorities; - - public static LoginMember of(Member member) { - return new LoginMember(member.getEmail(), member.getPassword(), member.getRoles()); - } - - public static LoginMember of(String email, List authorities) { - return new LoginMember(email, null, authorities); - } - - public static LoginMember guest() { - return new LoginMember(); - } - - public boolean checkPassword(String password) { - return this.password.equals(password); - } -} diff --git a/src/main/java/nextstep/member/ui/MemberController.java b/src/main/java/nextstep/member/ui/MemberController.java index 742acdb17..ac61d6ace 100644 --- a/src/main/java/nextstep/member/ui/MemberController.java +++ b/src/main/java/nextstep/member/ui/MemberController.java @@ -1,10 +1,10 @@ package nextstep.member.ui; import nextstep.auth.authorization.AuthenticationPrincipal; +import nextstep.auth.user.User; import nextstep.member.application.MemberService; import nextstep.member.application.dto.MemberRequest; import nextstep.member.application.dto.MemberResponse; -import nextstep.member.domain.LoginMember; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -43,19 +43,19 @@ public ResponseEntity deleteMember(@PathVariable Long id) { } @GetMapping("/members/me") - public ResponseEntity findMemberOfMine(@AuthenticationPrincipal LoginMember loginMember) { + public ResponseEntity findMemberOfMine(@AuthenticationPrincipal User loginMember) { MemberResponse member = memberService.findMember(loginMember.getEmail()); return ResponseEntity.ok().body(member); } @PutMapping("/members/me") - public ResponseEntity updateMemberOfMine(@AuthenticationPrincipal LoginMember loginMember, @RequestBody MemberRequest param) { + public ResponseEntity updateMemberOfMine(@AuthenticationPrincipal User loginMember, @RequestBody MemberRequest param) { memberService.updateMember(loginMember.getEmail(), param); return ResponseEntity.ok().build(); } @DeleteMapping("/members/me") - public ResponseEntity deleteMemberOfMine(@AuthenticationPrincipal LoginMember loginMember) { + public ResponseEntity deleteMemberOfMine(@AuthenticationPrincipal User loginMember) { memberService.deleteMember(loginMember.getEmail()); return ResponseEntity.noContent().build(); } From 91d8c97399165f184ba5f71a74e7f1a8b043f963 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=ED=98=84=EC=88=98?= Date: Tue, 27 Sep 2022 02:31:14 +0900 Subject: [PATCH 17/27] =?UTF-8?q?feat:=20=EC=9D=B8=EC=88=98=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../subway/ui/FavoritesController.java | 11 ++ .../acceptance/FavoritesAcceptanceTest.java | 138 ++++++++++++++++++ .../support/AcceptanceTestSteps.java | 4 +- .../acceptance/support/FavoritesSteps.java | 36 +++++ 4 files changed, 187 insertions(+), 2 deletions(-) create mode 100644 src/main/java/nextstep/subway/ui/FavoritesController.java create mode 100644 src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java create mode 100644 src/test/java/nextstep/subway/acceptance/support/FavoritesSteps.java diff --git a/src/main/java/nextstep/subway/ui/FavoritesController.java b/src/main/java/nextstep/subway/ui/FavoritesController.java new file mode 100644 index 000000000..014284e36 --- /dev/null +++ b/src/main/java/nextstep/subway/ui/FavoritesController.java @@ -0,0 +1,11 @@ +package nextstep.subway.ui; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + + +@RestController +@RequestMapping("/favorites") +public class FavoritesController { + +} diff --git a/src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java b/src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java new file mode 100644 index 000000000..e52e3be71 --- /dev/null +++ b/src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java @@ -0,0 +1,138 @@ +package nextstep.subway.acceptance; + +import io.restassured.response.ExtractableResponse; +import io.restassured.response.Response; +import nextstep.subway.acceptance.support.AcceptanceTest; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.springframework.http.HttpStatus; + +import static nextstep.subway.acceptance.support.FavoritesSteps.즐겨찾기_삭제_요청; +import static nextstep.subway.acceptance.support.FavoritesSteps.즐겨찾기_생성_요청; +import static nextstep.subway.acceptance.support.FavoritesSteps.즐겨찾기_조회_요청; +import static nextstep.subway.acceptance.support.StationSteps.지하철역_생성_요청; +import static org.assertj.core.api.Assertions.assertThat; + +@DisplayName("즐겨찾기 기능") +public class FavoritesAcceptanceTest extends AcceptanceTest { + + private Long 강남역; + private Long 양재역; + + /** + * Given 지하철역을 생성하고 + */ + @BeforeEach + public void setUp() { + super.setUp(); + + 강남역 = 지하철역_생성_요청(관리자, "강남역").jsonPath().getLong("id"); + 양재역 = 지하철역_생성_요청(관리자, "양재역").jsonPath().getLong("id"); + } + + @DisplayName("즐겨찾기 생성") + @Nested + class CreateFavorite{ + /** + * When 서로 다른 두개의 역에 대해서 즐겨찾기를 등록하면 + * Then 즐겨찾기가 등록된다. + */ + void success(){ + // when + ExtractableResponse response = 즐겨찾기_생성_요청(관리자, 강남역, 양재역); + + // then + assertThat(response.statusCode()).isEqualTo(HttpStatus.CREATED.value()); + } + + /** + * When 유효하지 않은 사용자가 즐겨찾기를 등록하면 + * Then 등록에 실패한다. + */ + void unauthorized(){ + // when + final String notAdmin = "9999"; + ExtractableResponse response = 즐겨찾기_생성_요청(notAdmin, 강남역, 양재역); + + // then + assertThat(response.statusCode()).isEqualTo(HttpStatus.OK.value()); + assertThat(response.jsonPath().getLong("code")).isEqualTo(1002); + } + } + + @DisplayName("즐겨찾기 조회") + @Nested + class GetFavorite{ + /** + * Given 서로 다른 두개의 역에 대해서 즐겨찾기를 등록한다. + * When 즐겨 찾기를 조회하면 + * Then 즐겨찾기 정보를 얻을 수 있다. + */ + void success(){ + // given + 즐겨찾기_생성_요청(관리자, 강남역, 양재역); + + // when + ExtractableResponse response = 즐겨찾기_조회_요청(관리자); + + // then + assertThat(response.statusCode()).isEqualTo(HttpStatus.OK.value()); + assertThat(response.jsonPath().getLong("id")).isNotNull(); + assertThat(response.jsonPath().getString("source.name")).isEqualTo("강남역"); + assertThat(response.jsonPath().getString("target.name")).isEqualTo("양재역"); + } + + /** + * When 유효하지 않은 사용자가 즐겨찾기를 조회하면 + * Then 조회에 실패한다. + */ + void unauthorized(){ + // when + final String notAdmin = "9999"; + ExtractableResponse response = 즐겨찾기_조회_요청(notAdmin); + + // then + assertThat(response.statusCode()).isEqualTo(HttpStatus.OK.value()); + assertThat(response.jsonPath().getLong("code")).isEqualTo(1002); + } + } + + @DisplayName("즐겨찾기 삭제") + @Nested + class DeleteFavorite{ + /** + * Given 서로 다른 두개의 역에 대해서 즐겨찾기를 등록한다. + * When 즐겨찾기를 삭제하면 + * Then 즐겨찾기 삭제가 된다. + */ + void success(){ + // given + Long 등록된_즐겨찾기 = 즐겨찾기_생성_요청(관리자, 강남역, 양재역).jsonPath().getLong("id"); + + // when + ExtractableResponse response = 즐겨찾기_삭제_요청(관리자, 등록된_즐겨찾기); + + // then + assertThat(response.statusCode()).isEqualTo(HttpStatus.NO_CONTENT.value()); + } + + /** + * Given 서로 다른 두개의 역에 대해서 즐겨찾기를 등록한다. + * When 유효하지 않은 사용자가 즐겨찾기를 조회하면 + * Then 조회에 실패한다. + */ + void unauthorized(){ + // given + Long 등록된_즐겨찾기 = 즐겨찾기_생성_요청(관리자, 강남역, 양재역).jsonPath().getLong("id"); + + // when + final String notAdmin = "9999"; + ExtractableResponse response = 즐겨찾기_삭제_요청(notAdmin, 등록된_즐겨찾기); + + // then + assertThat(response.statusCode()).isEqualTo(HttpStatus.OK.value()); + assertThat(response.jsonPath().getLong("code")).isEqualTo(1002); + } + } +} diff --git a/src/test/java/nextstep/subway/acceptance/support/AcceptanceTestSteps.java b/src/test/java/nextstep/subway/acceptance/support/AcceptanceTestSteps.java index f6a483981..c30956dea 100644 --- a/src/test/java/nextstep/subway/acceptance/support/AcceptanceTestSteps.java +++ b/src/test/java/nextstep/subway/acceptance/support/AcceptanceTestSteps.java @@ -4,12 +4,12 @@ import io.restassured.specification.RequestSpecification; public class AcceptanceTestSteps { - static RequestSpecification given() { + public static RequestSpecification given() { return RestAssured .given().log().all(); } - static RequestSpecification given(String token) { + public static RequestSpecification given(String token) { return RestAssured .given().log().all() .auth().oauth2(token); diff --git a/src/test/java/nextstep/subway/acceptance/support/FavoritesSteps.java b/src/test/java/nextstep/subway/acceptance/support/FavoritesSteps.java new file mode 100644 index 000000000..5dd7cb999 --- /dev/null +++ b/src/test/java/nextstep/subway/acceptance/support/FavoritesSteps.java @@ -0,0 +1,36 @@ +package nextstep.subway.acceptance.support; + +import io.restassured.response.ExtractableResponse; +import io.restassured.response.Response; +import org.springframework.http.MediaType; + +import java.util.HashMap; +import java.util.Map; + +import static nextstep.subway.acceptance.support.AcceptanceTestSteps.given; + +public class FavoritesSteps { + public static ExtractableResponse 즐겨찾기_생성_요청(String token, Long sourceId, Long targetId) { + Map params = new HashMap<>(); + params.put("source", sourceId); + params.put("target", targetId); + return given(token) + .body(params) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .when().post("/favorites") + .then().log().all().extract(); + } + + public static ExtractableResponse 즐겨찾기_조회_요청(String token) { + return given(token) + .when().get("/favorites") + .then().log().all().extract(); + } + + public static ExtractableResponse 즐겨찾기_삭제_요청(String token, Long favoritesId) { + return given(token) + .delete("/favorites/{favoritesId}", favoritesId) + .then().log().all() + .extract(); + } +} From 05305bcfd82dfd2ae0d3a43a0afa23b1257ca51c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=ED=98=84=EC=88=98?= Date: Tue, 27 Sep 2022 02:56:08 +0900 Subject: [PATCH 18/27] =?UTF-8?q?feat:=20=EC=A6=90=EA=B2=A8=EC=B0=BE?= =?UTF-8?q?=EA=B8=B0=20=EB=93=B1=EB=A1=9D=20controller=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../subway/applicaion/FavoritesService.java | 14 ++++++++++++++ .../applicaion/dto/FavoriteCreateRequest.java | 9 +++++++++ .../applicaion/dto/FavoriteResponse.java | 18 ++++++++++++++++++ .../subway/ui/FavoritesController.java | 18 ++++++++++++++++++ .../acceptance/FavoritesAcceptanceTest.java | 7 +++++++ 5 files changed, 66 insertions(+) create mode 100644 src/main/java/nextstep/subway/applicaion/FavoritesService.java create mode 100644 src/main/java/nextstep/subway/applicaion/dto/FavoriteCreateRequest.java create mode 100644 src/main/java/nextstep/subway/applicaion/dto/FavoriteResponse.java diff --git a/src/main/java/nextstep/subway/applicaion/FavoritesService.java b/src/main/java/nextstep/subway/applicaion/FavoritesService.java new file mode 100644 index 000000000..cf87728aa --- /dev/null +++ b/src/main/java/nextstep/subway/applicaion/FavoritesService.java @@ -0,0 +1,14 @@ +package nextstep.subway.applicaion; + +import nextstep.subway.applicaion.dto.FavoriteCreateRequest; +import nextstep.subway.applicaion.dto.FavoriteResponse; +import org.springframework.stereotype.Service; + +@Service +public class FavoritesService { + public FavoriteResponse createFavorite(final FavoriteCreateRequest request) { + FavoriteResponse.StationDTO source = new FavoriteResponse.StationDTO(1L, "n1"); + FavoriteResponse.StationDTO target = new FavoriteResponse.StationDTO(2L, "n2"); + return new FavoriteResponse(1L, source, target); + } +} diff --git a/src/main/java/nextstep/subway/applicaion/dto/FavoriteCreateRequest.java b/src/main/java/nextstep/subway/applicaion/dto/FavoriteCreateRequest.java new file mode 100644 index 000000000..8e999e178 --- /dev/null +++ b/src/main/java/nextstep/subway/applicaion/dto/FavoriteCreateRequest.java @@ -0,0 +1,9 @@ +package nextstep.subway.applicaion.dto; + +import lombok.Getter; + +@Getter +public class FavoriteCreateRequest { + private Long source; + private Long target; +} diff --git a/src/main/java/nextstep/subway/applicaion/dto/FavoriteResponse.java b/src/main/java/nextstep/subway/applicaion/dto/FavoriteResponse.java new file mode 100644 index 000000000..406a93281 --- /dev/null +++ b/src/main/java/nextstep/subway/applicaion/dto/FavoriteResponse.java @@ -0,0 +1,18 @@ +package nextstep.subway.applicaion.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public class FavoriteResponse { + Long id; + StationDTO source; + StationDTO target; + + @AllArgsConstructor + public static class StationDTO { + private Long id; + private String name; + } +} diff --git a/src/main/java/nextstep/subway/ui/FavoritesController.java b/src/main/java/nextstep/subway/ui/FavoritesController.java index 014284e36..935dead33 100644 --- a/src/main/java/nextstep/subway/ui/FavoritesController.java +++ b/src/main/java/nextstep/subway/ui/FavoritesController.java @@ -1,11 +1,29 @@ package nextstep.subway.ui; +import lombok.RequiredArgsConstructor; +import nextstep.auth.secured.Secured; +import nextstep.member.domain.RoleType; +import nextstep.subway.applicaion.FavoritesService; +import nextstep.subway.applicaion.dto.FavoriteCreateRequest; +import nextstep.subway.applicaion.dto.FavoriteResponse; +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; +import java.net.URI; +@RequiredArgsConstructor @RestController @RequestMapping("/favorites") public class FavoritesController { + private final FavoritesService favoritesService; + @Secured(RoleType.ROLE_ADMIN) + @PostMapping + public ResponseEntity createFavorite(@RequestBody FavoriteCreateRequest request){ + FavoriteResponse favorite = favoritesService.createFavorite(request); + return ResponseEntity.created(URI.create("/favorites/" + favorite.getId())).build(); + } } diff --git a/src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java b/src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java index e52e3be71..bc31d4c7b 100644 --- a/src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java +++ b/src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java @@ -6,6 +6,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; import org.springframework.http.HttpStatus; import static nextstep.subway.acceptance.support.FavoritesSteps.즐겨찾기_삭제_요청; @@ -38,6 +39,7 @@ class CreateFavorite{ * When 서로 다른 두개의 역에 대해서 즐겨찾기를 등록하면 * Then 즐겨찾기가 등록된다. */ + @Test void success(){ // when ExtractableResponse response = 즐겨찾기_생성_요청(관리자, 강남역, 양재역); @@ -50,6 +52,7 @@ void success(){ * When 유효하지 않은 사용자가 즐겨찾기를 등록하면 * Then 등록에 실패한다. */ + @Test void unauthorized(){ // when final String notAdmin = "9999"; @@ -69,6 +72,7 @@ class GetFavorite{ * When 즐겨 찾기를 조회하면 * Then 즐겨찾기 정보를 얻을 수 있다. */ + @Test void success(){ // given 즐겨찾기_생성_요청(관리자, 강남역, 양재역); @@ -87,6 +91,7 @@ void success(){ * When 유효하지 않은 사용자가 즐겨찾기를 조회하면 * Then 조회에 실패한다. */ + @Test void unauthorized(){ // when final String notAdmin = "9999"; @@ -106,6 +111,7 @@ class DeleteFavorite{ * When 즐겨찾기를 삭제하면 * Then 즐겨찾기 삭제가 된다. */ + @Test void success(){ // given Long 등록된_즐겨찾기 = 즐겨찾기_생성_요청(관리자, 강남역, 양재역).jsonPath().getLong("id"); @@ -122,6 +128,7 @@ void success(){ * When 유효하지 않은 사용자가 즐겨찾기를 조회하면 * Then 조회에 실패한다. */ + @Test void unauthorized(){ // given Long 등록된_즐겨찾기 = 즐겨찾기_생성_요청(관리자, 강남역, 양재역).jsonPath().getLong("id"); From 2d746ddcd24e03b7c0e7f9783312544d5be07792 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=ED=98=84=EC=88=98?= Date: Wed, 28 Sep 2022 03:13:19 +0900 Subject: [PATCH 19/27] =?UTF-8?q?feat:=20=EC=A6=90=EA=B2=A8=EC=B0=BE?= =?UTF-8?q?=EA=B8=B0=20=EB=8F=84=EB=A9=94=EC=9D=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/nextstep/subway/domain/Favorite.java | 29 +++++++++++++++++++ .../subway/domain/FavoritesRepository.java | 6 ++++ 2 files changed, 35 insertions(+) create mode 100644 src/main/java/nextstep/subway/domain/Favorite.java create mode 100644 src/main/java/nextstep/subway/domain/FavoritesRepository.java diff --git a/src/main/java/nextstep/subway/domain/Favorite.java b/src/main/java/nextstep/subway/domain/Favorite.java new file mode 100644 index 000000000..f680120fa --- /dev/null +++ b/src/main/java/nextstep/subway/domain/Favorite.java @@ -0,0 +1,29 @@ +package nextstep.subway.domain; + +import lombok.Getter; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; + +@Getter +@Entity +public class Favorite { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + @ManyToOne + @JoinColumn(name = "source_id") + private Station source; + @ManyToOne + @JoinColumn(name = "target_id") + private Station target; + + public Favorite(final Station source, final Station target) { + this.source = source; + this.target = target; + } +} diff --git a/src/main/java/nextstep/subway/domain/FavoritesRepository.java b/src/main/java/nextstep/subway/domain/FavoritesRepository.java new file mode 100644 index 000000000..805774160 --- /dev/null +++ b/src/main/java/nextstep/subway/domain/FavoritesRepository.java @@ -0,0 +1,6 @@ +package nextstep.subway.domain; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface FavoritesRepository extends JpaRepository { +} From 0c419ad9d245ba74b283f0578f61f794c63ae700 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=ED=98=84=EC=88=98?= Date: Wed, 28 Sep 2022 03:13:29 +0900 Subject: [PATCH 20/27] =?UTF-8?q?feat:=20=EC=A6=90=EA=B2=A8=EC=B0=BE?= =?UTF-8?q?=EA=B8=B0=20=EB=93=B1=EB=A1=9D=20service=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../subway/applicaion/FavoritesService.java | 18 +++++-- .../applicaion/dto/FavoriteCreateRequest.java | 2 + .../applicaion/dto/FavoriteResponse.java | 13 +++++ .../java/nextstep/subway/domain/Station.java | 8 +++ .../subway/unit/FavoritesServiceTest.java | 54 +++++++++++++++++++ .../unit/support/StationMockFactory.java | 11 ++++ 6 files changed, 103 insertions(+), 3 deletions(-) create mode 100644 src/test/java/nextstep/subway/unit/FavoritesServiceTest.java create mode 100644 src/test/java/nextstep/subway/unit/support/StationMockFactory.java diff --git a/src/main/java/nextstep/subway/applicaion/FavoritesService.java b/src/main/java/nextstep/subway/applicaion/FavoritesService.java index cf87728aa..6cffc982d 100644 --- a/src/main/java/nextstep/subway/applicaion/FavoritesService.java +++ b/src/main/java/nextstep/subway/applicaion/FavoritesService.java @@ -1,14 +1,26 @@ package nextstep.subway.applicaion; +import lombok.RequiredArgsConstructor; import nextstep.subway.applicaion.dto.FavoriteCreateRequest; import nextstep.subway.applicaion.dto.FavoriteResponse; +import nextstep.subway.domain.Favorite; +import nextstep.subway.domain.FavoritesRepository; +import nextstep.subway.domain.Station; import org.springframework.stereotype.Service; +@RequiredArgsConstructor @Service public class FavoritesService { + private final FavoritesRepository favoritesRepository; + private final StationService stationService; + public FavoriteResponse createFavorite(final FavoriteCreateRequest request) { - FavoriteResponse.StationDTO source = new FavoriteResponse.StationDTO(1L, "n1"); - FavoriteResponse.StationDTO target = new FavoriteResponse.StationDTO(2L, "n2"); - return new FavoriteResponse(1L, source, target); + Station source = stationService.findById(request.getSource()); + Station target = stationService.findById(request.getTarget()); + + Favorite favorite = new Favorite(source, target); + favoritesRepository.save(favorite); + + return new FavoriteResponse(favorite); } } diff --git a/src/main/java/nextstep/subway/applicaion/dto/FavoriteCreateRequest.java b/src/main/java/nextstep/subway/applicaion/dto/FavoriteCreateRequest.java index 8e999e178..a4d1e4c9b 100644 --- a/src/main/java/nextstep/subway/applicaion/dto/FavoriteCreateRequest.java +++ b/src/main/java/nextstep/subway/applicaion/dto/FavoriteCreateRequest.java @@ -1,7 +1,9 @@ package nextstep.subway.applicaion.dto; +import lombok.AllArgsConstructor; import lombok.Getter; +@AllArgsConstructor @Getter public class FavoriteCreateRequest { private Long source; diff --git a/src/main/java/nextstep/subway/applicaion/dto/FavoriteResponse.java b/src/main/java/nextstep/subway/applicaion/dto/FavoriteResponse.java index 406a93281..67bb07f37 100644 --- a/src/main/java/nextstep/subway/applicaion/dto/FavoriteResponse.java +++ b/src/main/java/nextstep/subway/applicaion/dto/FavoriteResponse.java @@ -2,6 +2,8 @@ import lombok.AllArgsConstructor; import lombok.Getter; +import nextstep.subway.domain.Favorite; +import nextstep.subway.domain.Station; @AllArgsConstructor @Getter @@ -10,9 +12,20 @@ public class FavoriteResponse { StationDTO source; StationDTO target; + public FavoriteResponse(Favorite favorite) { + this.source = new StationDTO(favorite.getSource()); + this.target = new StationDTO(favorite.getTarget()); + } + + @Getter @AllArgsConstructor public static class StationDTO { private Long id; private String name; + + public StationDTO(Station station){ + this.id = station.getId(); + this.name = station.getName(); + } } } diff --git a/src/main/java/nextstep/subway/domain/Station.java b/src/main/java/nextstep/subway/domain/Station.java index 79e394179..b54745b36 100644 --- a/src/main/java/nextstep/subway/domain/Station.java +++ b/src/main/java/nextstep/subway/domain/Station.java @@ -1,5 +1,7 @@ package nextstep.subway.domain; +import lombok.Builder; + import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; @@ -15,6 +17,12 @@ public class Station { public Station() { } + @Builder + public Station(final Long id, final String name) { + this.id = id; + this.name = name; + } + public Station(String name) { this.name = name; } diff --git a/src/test/java/nextstep/subway/unit/FavoritesServiceTest.java b/src/test/java/nextstep/subway/unit/FavoritesServiceTest.java new file mode 100644 index 000000000..06e961530 --- /dev/null +++ b/src/test/java/nextstep/subway/unit/FavoritesServiceTest.java @@ -0,0 +1,54 @@ +package nextstep.subway.unit; + +import nextstep.subway.applicaion.FavoritesService; +import nextstep.subway.applicaion.StationService; +import nextstep.subway.applicaion.dto.FavoriteCreateRequest; +import nextstep.subway.applicaion.dto.FavoriteResponse; +import nextstep.subway.domain.Favorite; +import nextstep.subway.domain.FavoritesRepository; +import nextstep.subway.domain.Station; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import static nextstep.subway.unit.support.StationMockFactory.station; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.verify; + + +@ExtendWith(MockitoExtension.class) +class FavoritesServiceTest { + @Mock + private StationService stationService; + @Mock + private FavoritesRepository favoritesRepository; + @InjectMocks + private FavoritesService favoritesService; + + @Test + void createFavorite() { + // given + Station source = station(1L); + Station target = station(2L); + FavoriteCreateRequest request = new FavoriteCreateRequest(source.getId(), target.getId()); + given(stationService.findById(source.getId())).willReturn(source); + given(stationService.findById(target.getId())).willReturn(target); + + // when + FavoriteResponse response = favoritesService.createFavorite(request); + + // then + assertAll( + () -> assertThat(response.getSource().getId()).isEqualTo(source.getId()), + () -> assertThat(response.getSource().getName()).isEqualTo(source.getName()), + () -> assertThat(response.getTarget().getId()).isEqualTo(target.getId()), + () -> assertThat(response.getTarget().getName()).isEqualTo(target.getName()) + ); + verify(favoritesRepository).save(any(Favorite.class)); + } +} \ No newline at end of file diff --git a/src/test/java/nextstep/subway/unit/support/StationMockFactory.java b/src/test/java/nextstep/subway/unit/support/StationMockFactory.java new file mode 100644 index 000000000..a06043c8c --- /dev/null +++ b/src/test/java/nextstep/subway/unit/support/StationMockFactory.java @@ -0,0 +1,11 @@ +package nextstep.subway.unit.support; + +import nextstep.subway.domain.Station; + +public class StationMockFactory { + public static Station station(long id) { + return Station.builder() + .id(id) + .name("station1").build(); + } +} From a8d4b2a15d9796a9dfebbe032a8555a7010ffcb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=ED=98=84=EC=88=98?= Date: Fri, 30 Sep 2022 03:46:45 +0900 Subject: [PATCH 21/27] =?UTF-8?q?feat:=20=EC=A6=90=EA=B2=A8=EC=B0=BE?= =?UTF-8?q?=EA=B8=B0=20=EC=A1=B0=ED=9A=8C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nextstep/subway/applicaion/FavoritesService.java | 11 +++++++++++ .../subway/applicaion/dto/FavoriteResponse.java | 1 + src/main/java/nextstep/subway/domain/Favorite.java | 3 +++ .../java/nextstep/subway/ui/FavoritesController.java | 9 +++++++++ .../subway/acceptance/FavoritesAcceptanceTest.java | 6 +++--- .../nextstep/subway/unit/FavoritesServiceTest.java | 2 ++ 6 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/main/java/nextstep/subway/applicaion/FavoritesService.java b/src/main/java/nextstep/subway/applicaion/FavoritesService.java index 6cffc982d..300a25c7c 100644 --- a/src/main/java/nextstep/subway/applicaion/FavoritesService.java +++ b/src/main/java/nextstep/subway/applicaion/FavoritesService.java @@ -8,6 +8,9 @@ import nextstep.subway.domain.Station; import org.springframework.stereotype.Service; +import java.util.List; +import java.util.stream.Collectors; + @RequiredArgsConstructor @Service public class FavoritesService { @@ -23,4 +26,12 @@ public FavoriteResponse createFavorite(final FavoriteCreateRequest request) { return new FavoriteResponse(favorite); } + + public List getFavorites() { + List favorites = favoritesRepository.findAll(); + + return favorites.stream() + .map(FavoriteResponse::new) + .collect(Collectors.toList()); + } } diff --git a/src/main/java/nextstep/subway/applicaion/dto/FavoriteResponse.java b/src/main/java/nextstep/subway/applicaion/dto/FavoriteResponse.java index 67bb07f37..37b7e5147 100644 --- a/src/main/java/nextstep/subway/applicaion/dto/FavoriteResponse.java +++ b/src/main/java/nextstep/subway/applicaion/dto/FavoriteResponse.java @@ -13,6 +13,7 @@ public class FavoriteResponse { StationDTO target; public FavoriteResponse(Favorite favorite) { + this.id = favorite.getId(); this.source = new StationDTO(favorite.getSource()); this.target = new StationDTO(favorite.getTarget()); } diff --git a/src/main/java/nextstep/subway/domain/Favorite.java b/src/main/java/nextstep/subway/domain/Favorite.java index f680120fa..849320f29 100644 --- a/src/main/java/nextstep/subway/domain/Favorite.java +++ b/src/main/java/nextstep/subway/domain/Favorite.java @@ -1,6 +1,8 @@ package nextstep.subway.domain; +import lombok.AccessLevel; import lombok.Getter; +import lombok.NoArgsConstructor; import javax.persistence.Entity; import javax.persistence.GeneratedValue; @@ -9,6 +11,7 @@ import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; +@NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter @Entity public class Favorite { diff --git a/src/main/java/nextstep/subway/ui/FavoritesController.java b/src/main/java/nextstep/subway/ui/FavoritesController.java index 935dead33..477727fc7 100644 --- a/src/main/java/nextstep/subway/ui/FavoritesController.java +++ b/src/main/java/nextstep/subway/ui/FavoritesController.java @@ -7,12 +7,14 @@ import nextstep.subway.applicaion.dto.FavoriteCreateRequest; import nextstep.subway.applicaion.dto.FavoriteResponse; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; 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; import java.net.URI; +import java.util.List; @RequiredArgsConstructor @RestController @@ -26,4 +28,11 @@ public ResponseEntity createFavorite(@RequestBody FavoriteCreateRequest re FavoriteResponse favorite = favoritesService.createFavorite(request); return ResponseEntity.created(URI.create("/favorites/" + favorite.getId())).build(); } + + @Secured(RoleType.ROLE_ADMIN) + @GetMapping + public ResponseEntity> getFavorites(){ + List response = favoritesService.getFavorites(); + return ResponseEntity.ok(response); + } } diff --git a/src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java b/src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java index bc31d4c7b..2bb2140ff 100644 --- a/src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java +++ b/src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java @@ -82,9 +82,9 @@ void success(){ // then assertThat(response.statusCode()).isEqualTo(HttpStatus.OK.value()); - assertThat(response.jsonPath().getLong("id")).isNotNull(); - assertThat(response.jsonPath().getString("source.name")).isEqualTo("강남역"); - assertThat(response.jsonPath().getString("target.name")).isEqualTo("양재역"); + assertThat(response.jsonPath().getList("id")).isNotNull(); + assertThat(response.jsonPath().getList("source.name")).contains("강남역"); + assertThat(response.jsonPath().getList("target.name")).contains("양재역"); } /** diff --git a/src/test/java/nextstep/subway/unit/FavoritesServiceTest.java b/src/test/java/nextstep/subway/unit/FavoritesServiceTest.java index 06e961530..38510c5a9 100644 --- a/src/test/java/nextstep/subway/unit/FavoritesServiceTest.java +++ b/src/test/java/nextstep/subway/unit/FavoritesServiceTest.java @@ -51,4 +51,6 @@ void createFavorite() { ); verify(favoritesRepository).save(any(Favorite.class)); } + + } \ No newline at end of file From fefa7ed0541c3062d07e728014a7cf38f6481974 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=ED=98=84=EC=88=98?= Date: Fri, 30 Sep 2022 04:21:11 +0900 Subject: [PATCH 22/27] =?UTF-8?q?feat:=20=EC=A6=90=EA=B2=A8=EC=B0=BE?= =?UTF-8?q?=EA=B8=B0=20=EC=82=AD=EC=A0=9C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../subway/applicaion/FavoritesService.java | 4 ++++ .../nextstep/subway/ui/FavoritesController.java | 9 +++++++++ .../acceptance/FavoritesAcceptanceTest.java | 16 +++++++++------- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/main/java/nextstep/subway/applicaion/FavoritesService.java b/src/main/java/nextstep/subway/applicaion/FavoritesService.java index 300a25c7c..2b5112711 100644 --- a/src/main/java/nextstep/subway/applicaion/FavoritesService.java +++ b/src/main/java/nextstep/subway/applicaion/FavoritesService.java @@ -34,4 +34,8 @@ public List getFavorites() { .map(FavoriteResponse::new) .collect(Collectors.toList()); } + + public void deleteFavorite(final Long favoriteId) { + favoritesRepository.deleteById(favoriteId); + } } diff --git a/src/main/java/nextstep/subway/ui/FavoritesController.java b/src/main/java/nextstep/subway/ui/FavoritesController.java index 477727fc7..05ad6a7c9 100644 --- a/src/main/java/nextstep/subway/ui/FavoritesController.java +++ b/src/main/java/nextstep/subway/ui/FavoritesController.java @@ -7,7 +7,9 @@ import nextstep.subway.applicaion.dto.FavoriteCreateRequest; import nextstep.subway.applicaion.dto.FavoriteResponse; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -35,4 +37,11 @@ public ResponseEntity> getFavorites(){ List response = favoritesService.getFavorites(); return ResponseEntity.ok(response); } + + @Secured(RoleType.ROLE_ADMIN) + @DeleteMapping("/{favoriteId}") + public ResponseEntity deleteFavorite(@PathVariable Long favoriteId){ + favoritesService.deleteFavorite(favoriteId); + return ResponseEntity.noContent().build(); + } } diff --git a/src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java b/src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java index 2bb2140ff..48bbf2e6f 100644 --- a/src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java +++ b/src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java @@ -82,9 +82,9 @@ void success(){ // then assertThat(response.statusCode()).isEqualTo(HttpStatus.OK.value()); - assertThat(response.jsonPath().getList("id")).isNotNull(); - assertThat(response.jsonPath().getList("source.name")).contains("강남역"); - assertThat(response.jsonPath().getList("target.name")).contains("양재역"); + assertThat(response.jsonPath().getLong("[0].id")).isNotNull(); + assertThat(response.jsonPath().getString("[0].source.name")).contains("강남역"); + assertThat(response.jsonPath().getString("[0].target.name")).contains("양재역"); } /** @@ -114,7 +114,8 @@ class DeleteFavorite{ @Test void success(){ // given - Long 등록된_즐겨찾기 = 즐겨찾기_생성_요청(관리자, 강남역, 양재역).jsonPath().getLong("id"); + 즐겨찾기_생성_요청(관리자, 강남역, 양재역); + Long 등록된_즐겨찾기 = 즐겨찾기_조회_요청(관리자).jsonPath().getLong("[0].id"); // when ExtractableResponse response = 즐겨찾기_삭제_요청(관리자, 등록된_즐겨찾기); @@ -131,11 +132,12 @@ void success(){ @Test void unauthorized(){ // given - Long 등록된_즐겨찾기 = 즐겨찾기_생성_요청(관리자, 강남역, 양재역).jsonPath().getLong("id"); + 즐겨찾기_생성_요청(관리자, 강남역, 양재역); + Long 등록된_즐겨찾기 = 즐겨찾기_조회_요청(관리자).jsonPath().getLong("[0].id"); // when - final String notAdmin = "9999"; - ExtractableResponse response = 즐겨찾기_삭제_요청(notAdmin, 등록된_즐겨찾기); + final String 유효하지않은_사용자 = "9999"; + ExtractableResponse response = 즐겨찾기_삭제_요청(유효하지않은_사용자, 등록된_즐겨찾기); // then assertThat(response.statusCode()).isEqualTo(HttpStatus.OK.value()); From 49171feb4f20ef8ac522683b2a776d2aa3fc4464 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=ED=98=84=EC=88=98?= Date: Fri, 30 Sep 2022 04:34:42 +0900 Subject: [PATCH 23/27] merge commit --- .../interceptor/AuthChainInterceptor.java | 35 ------------------- .../java/nextstep/auth/user/UserDetail.java | 13 ------- 2 files changed, 48 deletions(-) delete mode 100644 src/main/java/nextstep/auth/interceptor/AuthChainInterceptor.java delete mode 100644 src/main/java/nextstep/auth/user/UserDetail.java diff --git a/src/main/java/nextstep/auth/interceptor/AuthChainInterceptor.java b/src/main/java/nextstep/auth/interceptor/AuthChainInterceptor.java deleted file mode 100644 index 639ce4e37..000000000 --- a/src/main/java/nextstep/auth/interceptor/AuthChainInterceptor.java +++ /dev/null @@ -1,35 +0,0 @@ -package nextstep.auth.interceptor; - -import nextstep.auth.authentication.AuthenticationToken; -import nextstep.auth.context.Authentication; -import nextstep.auth.context.SecurityContextHolder; -import org.springframework.web.servlet.HandlerInterceptor; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -public abstract class AuthChainInterceptor implements HandlerInterceptor { - - @Override - public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler){ - try { - AuthenticationToken token = getAuthenticationToken(request); - checkValidAuth(token); - saveAuthContext(token); - return true; - } catch (Exception e) { - return true; - } - } - - protected abstract void checkValidAuth(final AuthenticationToken token); - - protected abstract Authentication getAuthentication(final AuthenticationToken token); - - protected abstract AuthenticationToken getAuthenticationToken(final HttpServletRequest request); - - private void saveAuthContext(final AuthenticationToken token) { - Authentication authentication = getAuthentication(token); - SecurityContextHolder.getContext().setAuthentication(authentication); - } -} diff --git a/src/main/java/nextstep/auth/user/UserDetail.java b/src/main/java/nextstep/auth/user/UserDetail.java deleted file mode 100644 index 410ac0753..000000000 --- a/src/main/java/nextstep/auth/user/UserDetail.java +++ /dev/null @@ -1,13 +0,0 @@ -package nextstep.auth.user; - -import java.util.List; - -public interface UserDetail { - String getEmail(); - - String getPassword(); - - List getAuthorities(); - - boolean checkPassword(String password); -} From d1b97b5773346276e1040bbbd5f27383a7086161 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=ED=98=84=EC=88=98?= Date: Sun, 2 Oct 2022 03:03:08 +0900 Subject: [PATCH 24/27] =?UTF-8?q?feat:=20=ED=8A=B8=EB=9E=9C=EC=9E=AD?= =?UTF-8?q?=EC=85=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/nextstep/subway/applicaion/FavoritesService.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/nextstep/subway/applicaion/FavoritesService.java b/src/main/java/nextstep/subway/applicaion/FavoritesService.java index 2b5112711..8fed8ab4c 100644 --- a/src/main/java/nextstep/subway/applicaion/FavoritesService.java +++ b/src/main/java/nextstep/subway/applicaion/FavoritesService.java @@ -7,11 +7,13 @@ import nextstep.subway.domain.FavoritesRepository; import nextstep.subway.domain.Station; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.util.List; import java.util.stream.Collectors; @RequiredArgsConstructor +@Transactional @Service public class FavoritesService { private final FavoritesRepository favoritesRepository; @@ -27,6 +29,7 @@ public FavoriteResponse createFavorite(final FavoriteCreateRequest request) { return new FavoriteResponse(favorite); } + @Transactional(readOnly = true) public List getFavorites() { List favorites = favoritesRepository.findAll(); From 33bc8339dfed12baa4453ffb03f64ee9185eb4b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=ED=98=84=EC=88=98?= Date: Sun, 2 Oct 2022 03:19:58 +0900 Subject: [PATCH 25/27] =?UTF-8?q?feat:=20=EC=98=88=EC=99=B8=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=EA=B5=AC=EC=B2=B4=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/secured/SecuredAnnotationChecker.java | 8 ++++---- .../common/exception/AuthException.java | 9 +++++++++ .../common/exception/code/AuthCode.java | 17 +++++++++++++++++ .../common/exception/code/CommonCode.java | 3 +-- 4 files changed, 31 insertions(+), 6 deletions(-) create mode 100644 src/main/java/nextstep/common/exception/AuthException.java create mode 100644 src/main/java/nextstep/common/exception/code/AuthCode.java diff --git a/src/main/java/nextstep/auth/secured/SecuredAnnotationChecker.java b/src/main/java/nextstep/auth/secured/SecuredAnnotationChecker.java index 755955a04..fe191f042 100644 --- a/src/main/java/nextstep/auth/secured/SecuredAnnotationChecker.java +++ b/src/main/java/nextstep/auth/secured/SecuredAnnotationChecker.java @@ -2,8 +2,8 @@ import nextstep.auth.context.Authentication; import nextstep.auth.context.SecurityContextHolder; -import nextstep.common.exception.CustomException; -import nextstep.common.exception.code.CommonCode; +import nextstep.common.exception.AuthException; +import nextstep.common.exception.code.AuthCode; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; @@ -27,11 +27,11 @@ public void checkAuthorities(JoinPoint joinPoint) { List values = Arrays.stream(secured.value()).map(Enum::name).collect(Collectors.toList()); Authentication authentication = Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication()) - .orElseThrow(() -> new CustomException(CommonCode.AUTH_INVALID)); + .orElseThrow(() -> new AuthException(AuthCode.AUTH_INVALID)); authentication.getAuthorities().stream() .filter(values::contains) .findFirst() - .orElseThrow(() -> new CustomException(CommonCode.AUTH_INVALID)); + .orElseThrow(() -> new AuthException(AuthCode.AUTH_INVALID)); } } diff --git a/src/main/java/nextstep/common/exception/AuthException.java b/src/main/java/nextstep/common/exception/AuthException.java new file mode 100644 index 000000000..d176cf8a9 --- /dev/null +++ b/src/main/java/nextstep/common/exception/AuthException.java @@ -0,0 +1,9 @@ +package nextstep.common.exception; + +import nextstep.common.exception.code.AuthCode; + +public class AuthException extends CustomException{ + public AuthException(final AuthCode authCode) { + super(authCode); + } +} diff --git a/src/main/java/nextstep/common/exception/code/AuthCode.java b/src/main/java/nextstep/common/exception/code/AuthCode.java new file mode 100644 index 000000000..b7cb80db1 --- /dev/null +++ b/src/main/java/nextstep/common/exception/code/AuthCode.java @@ -0,0 +1,17 @@ +package nextstep.common.exception.code; + +import lombok.Getter; + +@Getter +public enum AuthCode implements ResponseCode { + AUTH_INVALID(1002, "올바르지 않는 사용자입니다."); + + private final int code; + + private final String message; + + AuthCode(int code, String message) { + this.code = code; + this.message = message; + } +} diff --git a/src/main/java/nextstep/common/exception/code/CommonCode.java b/src/main/java/nextstep/common/exception/code/CommonCode.java index c296fa80a..3b0a1458d 100644 --- a/src/main/java/nextstep/common/exception/code/CommonCode.java +++ b/src/main/java/nextstep/common/exception/code/CommonCode.java @@ -5,8 +5,7 @@ @Getter public enum CommonCode implements ResponseCode { ETC(1000, "알 수 없는 오류입니다."), - PARAM_INVALID(1001, "올바르지 않은 파라미터입니다."), - AUTH_INVALID(1002, "올바르지 않는 사용자입니다."); + PARAM_INVALID(1001, "올바르지 않은 파라미터입니다."); private final int code; From e194e54a9fa2d9866ecb8be0b027e160cdbf2a01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=ED=98=84=EC=88=98?= Date: Fri, 14 Oct 2022 02:13:18 +0900 Subject: [PATCH 26/27] =?UTF-8?q?test:=20=EC=9C=A0=ED=9A=A8=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EC=9D=80=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=20=EC=BC=80=EC=9D=B4=EC=8A=A4=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/GlobalExceptionHandler.java | 11 ++++++ .../acceptance/FavoritesAcceptanceTest.java | 35 +++++++++++++++++-- .../subway/acceptance/LineAcceptanceTest.java | 2 +- .../acceptance/support/AcceptanceTest.java | 11 ++++-- .../acceptance/support/FavoritesSteps.java | 11 ++++++ 5 files changed, 63 insertions(+), 7 deletions(-) diff --git a/src/main/java/nextstep/common/exception/GlobalExceptionHandler.java b/src/main/java/nextstep/common/exception/GlobalExceptionHandler.java index 1cfb56cf7..5e0e70545 100644 --- a/src/main/java/nextstep/common/exception/GlobalExceptionHandler.java +++ b/src/main/java/nextstep/common/exception/GlobalExceptionHandler.java @@ -2,6 +2,7 @@ import lombok.extern.slf4j.Slf4j; import nextstep.common.CommonResponse; +import nextstep.common.exception.code.AuthCode; import nextstep.common.exception.code.CommonCode; import nextstep.common.exception.code.ResponseCode; import org.springframework.dao.DataIntegrityViolationException; @@ -28,6 +29,16 @@ public ResponseEntity handleCustomException(CustomException ex) { return ResponseEntity.status(HttpStatus.OK).body(response); } + /** + * 인증 exception 처리 + */ + @ExceptionHandler(value = {AuthException.class}) + public ResponseEntity handleAuthException(AuthException ex) { + ResponseCode responseCode = ex.getResponseCode(); + CommonResponse response = new CommonResponse<>(responseCode); + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response); + } + /** * 파라미터 유효성관련 exception 처리 */ diff --git a/src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java b/src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java index 48bbf2e6f..6d9f0f26b 100644 --- a/src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java +++ b/src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java @@ -9,6 +9,7 @@ import org.junit.jupiter.api.Test; import org.springframework.http.HttpStatus; +import static nextstep.subway.acceptance.support.FavoritesSteps.미로그인_즐겨찾기_생성_요청; import static nextstep.subway.acceptance.support.FavoritesSteps.즐겨찾기_삭제_요청; import static nextstep.subway.acceptance.support.FavoritesSteps.즐겨찾기_생성_요청; import static nextstep.subway.acceptance.support.FavoritesSteps.즐겨찾기_조회_요청; @@ -59,7 +60,35 @@ void unauthorized(){ ExtractableResponse response = 즐겨찾기_생성_요청(notAdmin, 강남역, 양재역); // then - assertThat(response.statusCode()).isEqualTo(HttpStatus.OK.value()); + assertThat(response.statusCode()).isEqualTo(HttpStatus.UNAUTHORIZED.value()); + assertThat(response.jsonPath().getLong("code")).isEqualTo(1002); + } + + /** + * When 로그인 하지 않은 사용자가 즐겨찾기 등록하면 + * Then 등록에 실패한다. + */ + @Test + void unauthorized_notlogin(){ + // when + ExtractableResponse response = 미로그인_즐겨찾기_생성_요청(강남역, 양재역); + + // then + assertThat(response.statusCode()).isEqualTo(HttpStatus.UNAUTHORIZED.value()); + assertThat(response.jsonPath().getLong("code")).isEqualTo(1002); + } + + /** + * When 일반 회원이 즐겨찾기를 등록하면 + * Then 등록에 실패한다. + */ + @Test + void unauthorized_when_member(){ + // when + ExtractableResponse response = 즐겨찾기_생성_요청(일반회원, 강남역, 양재역); + + // then + assertThat(response.statusCode()).isEqualTo(HttpStatus.UNAUTHORIZED.value()); assertThat(response.jsonPath().getLong("code")).isEqualTo(1002); } } @@ -98,7 +127,7 @@ void unauthorized(){ ExtractableResponse response = 즐겨찾기_조회_요청(notAdmin); // then - assertThat(response.statusCode()).isEqualTo(HttpStatus.OK.value()); + assertThat(response.statusCode()).isEqualTo(HttpStatus.UNAUTHORIZED.value()); assertThat(response.jsonPath().getLong("code")).isEqualTo(1002); } } @@ -140,7 +169,7 @@ void unauthorized(){ ExtractableResponse response = 즐겨찾기_삭제_요청(유효하지않은_사용자, 등록된_즐겨찾기); // then - assertThat(response.statusCode()).isEqualTo(HttpStatus.OK.value()); + assertThat(response.statusCode()).isEqualTo(HttpStatus.UNAUTHORIZED.value()); assertThat(response.jsonPath().getLong("code")).isEqualTo(1002); } } diff --git a/src/test/java/nextstep/subway/acceptance/LineAcceptanceTest.java b/src/test/java/nextstep/subway/acceptance/LineAcceptanceTest.java index 0cf5258eb..214454a5c 100644 --- a/src/test/java/nextstep/subway/acceptance/LineAcceptanceTest.java +++ b/src/test/java/nextstep/subway/acceptance/LineAcceptanceTest.java @@ -49,7 +49,7 @@ void throwExceptionWhenNotAdmin() { ExtractableResponse response = 지하철_노선_생성_요청(notAdmin, "2호선", "green"); // then - assertThat(response.statusCode()).isEqualTo(HttpStatus.OK.value()); + assertThat(response.statusCode()).isEqualTo(HttpStatus.UNAUTHORIZED.value()); assertThat(response.jsonPath().getLong("code")).isEqualTo(1002); } } diff --git a/src/test/java/nextstep/subway/acceptance/support/AcceptanceTest.java b/src/test/java/nextstep/subway/acceptance/support/AcceptanceTest.java index e4e6f1be5..5bcd69139 100644 --- a/src/test/java/nextstep/subway/acceptance/support/AcceptanceTest.java +++ b/src/test/java/nextstep/subway/acceptance/support/AcceptanceTest.java @@ -14,8 +14,11 @@ @ActiveProfiles("test") @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class AcceptanceTest { - private static final String EMAIL = "admin@email.com"; - private static final String PASSWORD = "password"; + private static final String ADMIN_EMAIL = "admin@email.com"; + private static final String ADMIN_PASSWORD = "password"; + + private static final String MEMBER_EMAIL = "member@email.com"; + private static final String MEMBER_PASSWORD = "password"; @LocalServerPort int port; @@ -27,6 +30,7 @@ public class AcceptanceTest { private DataLoader dataLoader; protected String 관리자; + protected String 일반회원; @BeforeEach public void setUp() { @@ -34,6 +38,7 @@ public void setUp() { databaseCleanup.execute(); dataLoader.loadData(); - 관리자 = 로그인_되어_있음(EMAIL, PASSWORD); + 관리자 = 로그인_되어_있음(ADMIN_EMAIL, ADMIN_PASSWORD); + 일반회원 = 로그인_되어_있음(MEMBER_EMAIL, MEMBER_PASSWORD); } } diff --git a/src/test/java/nextstep/subway/acceptance/support/FavoritesSteps.java b/src/test/java/nextstep/subway/acceptance/support/FavoritesSteps.java index 5dd7cb999..0e1596a39 100644 --- a/src/test/java/nextstep/subway/acceptance/support/FavoritesSteps.java +++ b/src/test/java/nextstep/subway/acceptance/support/FavoritesSteps.java @@ -21,6 +21,17 @@ public class FavoritesSteps { .then().log().all().extract(); } + public static ExtractableResponse 미로그인_즐겨찾기_생성_요청(Long sourceId, Long targetId) { + Map params = new HashMap<>(); + params.put("source", sourceId); + params.put("target", targetId); + return given() + .body(params) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .when().post("/favorites") + .then().log().all().extract(); + } + public static ExtractableResponse 즐겨찾기_조회_요청(String token) { return given(token) .when().get("/favorites") From 0e880a27951771970745938164ba497ad579ce10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=ED=98=84=EC=88=98?= Date: Fri, 14 Oct 2022 02:37:03 +0900 Subject: [PATCH 27/27] =?UTF-8?q?feat:=20UserDetail=20=EC=9D=B8=ED=84=B0?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=8A=A4=20=EC=83=9D=EC=84=B1=20=EB=B0=8F=20?= =?UTF-8?q?User=20=EA=B0=9D=EC=B2=B4=20=EC=B6=94=EC=83=81=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/authentication/BasicAuthFilter.java | 5 +++-- .../authentication/UsernamePasswordAuthFilter.java | 3 ++- .../AuthenticationPrincipalArgumentResolver.java | 3 ++- .../auth/interceptor/AuthNotChainInterceptor.java | 9 +++++---- .../nextstep/auth/token/TokenAuthInterceptor.java | 3 ++- src/main/java/nextstep/auth/user/User.java | 5 +++-- src/main/java/nextstep/auth/user/UserDetail.java | 13 +++++++++++++ .../java/nextstep/auth/user/UserDetailService.java | 2 +- .../member/application/LoginMemberService.java | 3 ++- 9 files changed, 33 insertions(+), 13 deletions(-) create mode 100644 src/main/java/nextstep/auth/user/UserDetail.java diff --git a/src/main/java/nextstep/auth/authentication/BasicAuthFilter.java b/src/main/java/nextstep/auth/authentication/BasicAuthFilter.java index 101af28ce..f4ef7d39d 100644 --- a/src/main/java/nextstep/auth/authentication/BasicAuthFilter.java +++ b/src/main/java/nextstep/auth/authentication/BasicAuthFilter.java @@ -3,6 +3,7 @@ import lombok.RequiredArgsConstructor; import nextstep.auth.context.Authentication; import nextstep.auth.user.User; +import nextstep.auth.user.UserDetail; import nextstep.auth.user.UserDetailService; import nextstep.auth.interceptor.AuthContextChainInterceptor; import org.apache.tomcat.util.codec.binary.Base64; @@ -15,7 +16,7 @@ public class BasicAuthFilter extends AuthContextChainInterceptor { @Override protected void checkValidAuth(final AuthenticationToken token) { - User loginMember = userDetailService.loadUserByUsername(token.getPrincipal()); + UserDetail loginMember = userDetailService.loadUserByUsername(token.getPrincipal()); if (loginMember == null) { throw new AuthenticationException(); } @@ -26,7 +27,7 @@ protected void checkValidAuth(final AuthenticationToken token) { @Override protected Authentication getAuthentication(final AuthenticationToken token) { - User loginMember = userDetailService.loadUserByUsername(token.getPrincipal()); + UserDetail loginMember = userDetailService.loadUserByUsername(token.getPrincipal()); return new Authentication(loginMember.getEmail(), loginMember.getAuthorities()); } diff --git a/src/main/java/nextstep/auth/authentication/UsernamePasswordAuthFilter.java b/src/main/java/nextstep/auth/authentication/UsernamePasswordAuthFilter.java index dcc5327bf..1d276be95 100644 --- a/src/main/java/nextstep/auth/authentication/UsernamePasswordAuthFilter.java +++ b/src/main/java/nextstep/auth/authentication/UsernamePasswordAuthFilter.java @@ -4,6 +4,7 @@ import nextstep.auth.context.SecurityContextHolder; import nextstep.auth.interceptor.AuthNotChainInterceptor; import nextstep.auth.user.User; +import nextstep.auth.user.UserDetail; import nextstep.auth.user.UserDetailService; import javax.servlet.http.HttpServletRequest; @@ -27,7 +28,7 @@ protected AuthenticationToken createAuthToken(final HttpServletRequest request) } @Override - protected void afterSuccessUserCheck(final HttpServletRequest request, final HttpServletResponse response, User user) { + protected void afterSuccessUserCheck(final HttpServletRequest request, final HttpServletResponse response, UserDetail user) { Authentication authentication = new Authentication(user.getEmail(), user.getAuthorities()); SecurityContextHolder.getContext().setAuthentication(authentication); } diff --git a/src/main/java/nextstep/auth/authorization/AuthenticationPrincipalArgumentResolver.java b/src/main/java/nextstep/auth/authorization/AuthenticationPrincipalArgumentResolver.java index a96603745..741767dea 100644 --- a/src/main/java/nextstep/auth/authorization/AuthenticationPrincipalArgumentResolver.java +++ b/src/main/java/nextstep/auth/authorization/AuthenticationPrincipalArgumentResolver.java @@ -3,6 +3,7 @@ import nextstep.auth.context.Authentication; import nextstep.auth.context.SecurityContextHolder; import nextstep.auth.user.User; +import nextstep.auth.user.UserDetail; import org.springframework.core.MethodParameter; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; @@ -16,7 +17,7 @@ public boolean supportsParameter(MethodParameter parameter) { } @Override - public User resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) { + public UserDetail resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication == null) { return User.guest(); diff --git a/src/main/java/nextstep/auth/interceptor/AuthNotChainInterceptor.java b/src/main/java/nextstep/auth/interceptor/AuthNotChainInterceptor.java index c31ca2840..39107123d 100644 --- a/src/main/java/nextstep/auth/interceptor/AuthNotChainInterceptor.java +++ b/src/main/java/nextstep/auth/interceptor/AuthNotChainInterceptor.java @@ -4,6 +4,7 @@ import nextstep.auth.authentication.AuthenticationException; import nextstep.auth.authentication.AuthenticationToken; import nextstep.auth.user.User; +import nextstep.auth.user.UserDetail; import nextstep.auth.user.UserDetailService; import org.springframework.web.servlet.HandlerInterceptor; @@ -20,7 +21,7 @@ public boolean preHandle(final HttpServletRequest request, final HttpServletResp try { AuthenticationToken authToken = createAuthToken(request); checkValidAuth(authToken); - User user = getUserDetail(authToken); + UserDetail user = getUserDetail(authToken); afterSuccessUserCheck(request, response, user); return false; } catch (Exception e) { @@ -30,11 +31,11 @@ public boolean preHandle(final HttpServletRequest request, final HttpServletResp protected abstract AuthenticationToken createAuthToken(final HttpServletRequest request) throws IOException; - protected abstract void afterSuccessUserCheck(final HttpServletRequest request, final HttpServletResponse response, User user) + protected abstract void afterSuccessUserCheck(final HttpServletRequest request, final HttpServletResponse response, UserDetail user) throws IOException; protected void checkValidAuth(AuthenticationToken authToken){ - User loginMember = getUserDetail(authToken); + UserDetail loginMember = getUserDetail(authToken); if (loginMember == null) { throw new AuthenticationException(); @@ -45,7 +46,7 @@ protected void checkValidAuth(AuthenticationToken authToken){ } } - private User getUserDetail(final AuthenticationToken authToken) { + private UserDetail getUserDetail(final AuthenticationToken authToken) { return userDetailService.loadUserByUsername(authToken.getPrincipal()); } } diff --git a/src/main/java/nextstep/auth/token/TokenAuthInterceptor.java b/src/main/java/nextstep/auth/token/TokenAuthInterceptor.java index 1982d576e..5b568a864 100644 --- a/src/main/java/nextstep/auth/token/TokenAuthInterceptor.java +++ b/src/main/java/nextstep/auth/token/TokenAuthInterceptor.java @@ -4,6 +4,7 @@ import nextstep.auth.authentication.AuthenticationToken; import nextstep.auth.user.User; import nextstep.auth.interceptor.AuthNotChainInterceptor; +import nextstep.auth.user.UserDetail; import nextstep.auth.user.UserDetailService; import org.springframework.http.MediaType; @@ -30,7 +31,7 @@ protected AuthenticationToken createAuthToken(final HttpServletRequest request) @Override protected void afterSuccessUserCheck(final HttpServletRequest request, final HttpServletResponse response, - final User user) throws IOException { + final UserDetail user) throws IOException { String token = jwtTokenProvider.createToken(user.getEmail(), user.getAuthorities()); TokenResponse tokenResponse = new TokenResponse(token); diff --git a/src/main/java/nextstep/auth/user/User.java b/src/main/java/nextstep/auth/user/User.java index 3c4927440..0f39cb09a 100644 --- a/src/main/java/nextstep/auth/user/User.java +++ b/src/main/java/nextstep/auth/user/User.java @@ -10,7 +10,7 @@ @NoArgsConstructor(access = AccessLevel.PRIVATE) @AllArgsConstructor(access = AccessLevel.PRIVATE) @Getter -public class User { +public class User implements UserDetail{ private String email; private String password; private List authorities; @@ -23,10 +23,11 @@ public static User of(String email, String password, List authorities) { return new User(email, password, authorities); } - public static User guest() { + public static UserDetail guest() { return new User(); } + @Override public boolean checkPassword(final String password) { return this.password.equals(password); } diff --git a/src/main/java/nextstep/auth/user/UserDetail.java b/src/main/java/nextstep/auth/user/UserDetail.java new file mode 100644 index 000000000..5459ef036 --- /dev/null +++ b/src/main/java/nextstep/auth/user/UserDetail.java @@ -0,0 +1,13 @@ +package nextstep.auth.user; + +import java.util.List; + +public interface UserDetail { + boolean checkPassword(final String password); + + String getEmail(); + + String getPassword(); + + List getAuthorities(); +} diff --git a/src/main/java/nextstep/auth/user/UserDetailService.java b/src/main/java/nextstep/auth/user/UserDetailService.java index 779c11028..276bfe677 100644 --- a/src/main/java/nextstep/auth/user/UserDetailService.java +++ b/src/main/java/nextstep/auth/user/UserDetailService.java @@ -1,5 +1,5 @@ package nextstep.auth.user; public interface UserDetailService { - User loadUserByUsername(String email); + UserDetail loadUserByUsername(String email); } diff --git a/src/main/java/nextstep/member/application/LoginMemberService.java b/src/main/java/nextstep/member/application/LoginMemberService.java index c8d46ce52..515d09ec4 100644 --- a/src/main/java/nextstep/member/application/LoginMemberService.java +++ b/src/main/java/nextstep/member/application/LoginMemberService.java @@ -1,6 +1,7 @@ package nextstep.member.application; import nextstep.auth.user.User; +import nextstep.auth.user.UserDetail; import nextstep.auth.user.UserDetailService; import nextstep.member.domain.Member; import nextstep.member.domain.MemberRepository; @@ -14,7 +15,7 @@ public LoginMemberService(MemberRepository memberRepository) { this.memberRepository = memberRepository; } - public User loadUserByUsername(String email) { + public UserDetail loadUserByUsername(String email) { Member member = memberRepository.findByEmail(email).orElseThrow(RuntimeException::new); return User.of(member.getEmail(), member.getPassword(), member.getRoles()); }