From 23deacdb0ab2b45e10f5053460726d6be01b1fd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=B5=9C=EC=A3=BC=ED=98=95/=EC=9B=B9=EC=84=9C=EB=B9=84?= =?UTF-8?q?=EC=8A=A4=EC=84=9C=EB=B2=84=EA=B0=9C=EB=B0=9CCell/Kia?= Date: Fri, 1 Nov 2024 22:31:07 +0900 Subject: [PATCH 1/6] feat: update README.md --- README.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/README.md b/README.md index 1e7ba652..ebbc8fdc 100644 --- a/README.md +++ b/README.md @@ -1 +1,35 @@ # spring-security-authentication + +## 기능 요구 사항 +- 아이디와 비밀번호를 기반으로 로그인 기능 구현 + - Basic 인증 사용 + - 스프링 프레임워크를 사용해 웹 앱으로 구현 + +### 아이디와 비밀번호 기반 로그인 구현 +- POST /login 경로로 로그인 요청 +- 사용자가 입력한 아이디와 비밀번호를 확인하여 인증 +- 로그인 성공 시 Session을 사용하여 인증 정보 저장 +- LoginTest 통과 + + +### Basic 인증 구현 +- GET /member 요청 시 사용자 목록 조회 +- 단, Member로 등록되어 있는 사용자만 가능하도록 한다. +- 이를 위해 Basic 인증을 사용하여 사용자 식별 +- 요청의 Authorization 헤더에서 Basic 인증 정보를 추출하여 인증 처리 +- 인증 성공 시 Session을 사용하여 인증 정보 저장 +- MemberTest 통과 + +### 인터셉터 분리 +- HandlerInterceptor를 사용하여 인증 관련 로직을 Controller 클래스에서 분리 + - 앞서 구현한 두 인증 방식 모두 인터셉터에서 처리되도록 구현 + - 가급적이면 하나의 인터셉터는 하나의 작업만 수행하도록 한다. + +### 인증 로직과 서비스 로직 간의 패키지 분리 +- 서비스 코드와 인증 코드를 명확히 분리하여 관리 + - 서비스 관련 코드는 app 패키지에 위치시키고, 인증 관련 코드는 security 패키지에 위치 +- 리팩터링 과정에서 패키지 간의 양방향 참조가 발생한다면 단방향 참조로 리팩터링 + - app 패키지는 security 패키지에 의존할 수 있지만, + - security 패키지는 app 패키지에 의존하지 않도록 한다. +- 인증 관련 작업은 security 패키지에서 전담하도록 설계하여, 서비스 로직이 인증 세부 사항에 의존하지 않게 만든다. +- LoginTest, MemberTest 의 모든 테스트는 지속해서 통과하여야 한다. From 550772d106d3bbbbc00af74f57ec130b74ef43ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=B5=9C=EC=A3=BC=ED=98=95/=EC=9B=B9=EC=84=9C=EB=B9=84?= =?UTF-8?q?=EC=8A=A4=EC=84=9C=EB=B2=84=EA=B0=9C=EB=B0=9CCell/Kia?= Date: Sat, 2 Nov 2024 20:51:25 +0900 Subject: [PATCH 2/6] =?UTF-8?q?feat:=20BasicAuthenticationInterceptor=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 12 ++++++ settings.gradle | 5 +++ .../nextstep/security/config/WebConfig.java | 14 +++++++ .../BasicAuthenticationInterceptor.java | 38 +++++++++++++++++++ 4 files changed, 69 insertions(+) create mode 100644 src/main/java/nextstep/security/config/WebConfig.java create mode 100644 src/main/java/nextstep/security/interceptor/BasicAuthenticationInterceptor.java diff --git a/build.gradle b/build.gradle index 99766160..95741fee 100644 --- a/build.gradle +++ b/build.gradle @@ -2,6 +2,7 @@ plugins { id 'org.springframework.boot' version '2.7.1' id 'io.spring.dependency-management' version '1.0.11.RELEASE' id 'java' + id 'org.jetbrains.kotlin.jvm' } group = 'nextstep' @@ -15,8 +16,19 @@ repositories { dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' testImplementation 'org.springframework.boot:spring-boot-starter-test' + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" } tasks.named('test') { useJUnitPlatform() } +compileKotlin { + kotlinOptions { + jvmTarget = "11" + } +} +compileTestKotlin { + kotlinOptions { + jvmTarget = "11" + } +} diff --git a/settings.gradle b/settings.gradle index b187a12b..20162923 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1,6 @@ +pluginManagement { + plugins { + id 'org.jetbrains.kotlin.jvm' version '1.9.21' + } +} rootProject.name = 'spring-security-authentication' diff --git a/src/main/java/nextstep/security/config/WebConfig.java b/src/main/java/nextstep/security/config/WebConfig.java new file mode 100644 index 00000000..a5463f5a --- /dev/null +++ b/src/main/java/nextstep/security/config/WebConfig.java @@ -0,0 +1,14 @@ +package nextstep.security.config; + +import nextstep.security.interceptor.BasicAuthenticationInterceptor; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(new BasicAuthenticationInterceptor()).addPathPatterns("/members"); + } +} diff --git a/src/main/java/nextstep/security/interceptor/BasicAuthenticationInterceptor.java b/src/main/java/nextstep/security/interceptor/BasicAuthenticationInterceptor.java new file mode 100644 index 00000000..adc658d2 --- /dev/null +++ b/src/main/java/nextstep/security/interceptor/BasicAuthenticationInterceptor.java @@ -0,0 +1,38 @@ +package nextstep.security.interceptor; + +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.web.servlet.HandlerInterceptor; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.nio.charset.StandardCharsets; +import java.util.Base64; + +public class BasicAuthenticationInterceptor implements HandlerInterceptor { + private static final String BASIC_AUTH = "Basic"; + private static final String USERNAME = "username"; + private static final String PASSWORD = "password"; + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { + String authorizationHeader = request.getHeader(HttpHeaders.AUTHORIZATION); + + if (authorizationHeader != null && authorizationHeader.startsWith(BASIC_AUTH)) { + String[] values = getBasicValues(authorizationHeader); + if (values.length == 2 && values[0].equals(USERNAME) && values[1].equals(PASSWORD)) { + return true; // 인증 성공 시 요청을 진행 + } + } + + // 인증 실패 시 401 상태 코드를 반환 + response.setStatus(HttpStatus.UNAUTHORIZED.value()); + return false; + } + + private String[] getBasicValues(String authorizationHeader) { + String base64Credentials = authorizationHeader.substring(BASIC_AUTH.length()).trim(); + String credentials = new String(Base64.getDecoder().decode(base64Credentials), StandardCharsets.UTF_8); + return credentials.split(":", 2); + } +} From 30a857f1b1b9ab1cf2cf40351466c844745e5781 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=B5=9C=EC=A3=BC=ED=98=95/=EC=9B=B9=EC=84=9C=EB=B9=84?= =?UTF-8?q?=EC=8A=A4=EC=84=9C=EB=B2=84=EA=B0=9C=EB=B0=9CCell/Kia?= Date: Sat, 2 Nov 2024 22:08:44 +0900 Subject: [PATCH 3/6] =?UTF-8?q?feat:=20FormLoginAuthorizationInterceptor?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{security => app}/config/WebConfig.java | 11 +++++- .../app/domain/UserDetailService.java | 19 ++++++++++ .../FormLoginAuthorizationInterceptor.java | 36 +++++++++++++++++++ 3 files changed, 65 insertions(+), 1 deletion(-) rename src/main/java/nextstep/{security => app}/config/WebConfig.java (55%) create mode 100644 src/main/java/nextstep/app/domain/UserDetailService.java create mode 100644 src/main/java/nextstep/security/interceptor/FormLoginAuthorizationInterceptor.java diff --git a/src/main/java/nextstep/security/config/WebConfig.java b/src/main/java/nextstep/app/config/WebConfig.java similarity index 55% rename from src/main/java/nextstep/security/config/WebConfig.java rename to src/main/java/nextstep/app/config/WebConfig.java index a5463f5a..6c7a1c5a 100644 --- a/src/main/java/nextstep/security/config/WebConfig.java +++ b/src/main/java/nextstep/app/config/WebConfig.java @@ -1,14 +1,23 @@ -package nextstep.security.config; +package nextstep.app.config; +import nextstep.app.domain.UserDetailService; import nextstep.security.interceptor.BasicAuthenticationInterceptor; +import nextstep.security.interceptor.FormLoginAuthorizationInterceptor; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class WebConfig implements WebMvcConfigurer { + private final UserDetailService userDetailService; + + public WebConfig(UserDetailService userDetailService) { + this.userDetailService = userDetailService; + } + @Override public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(new FormLoginAuthorizationInterceptor(userDetailService)).addPathPatterns("/login"); registry.addInterceptor(new BasicAuthenticationInterceptor()).addPathPatterns("/members"); } } diff --git a/src/main/java/nextstep/app/domain/UserDetailService.java b/src/main/java/nextstep/app/domain/UserDetailService.java new file mode 100644 index 00000000..671438cd --- /dev/null +++ b/src/main/java/nextstep/app/domain/UserDetailService.java @@ -0,0 +1,19 @@ +package nextstep.app.domain; + +import org.springframework.stereotype.Service; + +@Service +public class UserDetailService { + + private final MemberRepository memberRepository; + + public UserDetailService(MemberRepository memberRepository) { + this.memberRepository = memberRepository; + } + + public Member retrieveMemberByEmailAndPassword(String email, String password) { + return memberRepository.findByEmail(email) + .filter(member -> member.getPassword().equals(password)) + .orElse(null); + } +} diff --git a/src/main/java/nextstep/security/interceptor/FormLoginAuthorizationInterceptor.java b/src/main/java/nextstep/security/interceptor/FormLoginAuthorizationInterceptor.java new file mode 100644 index 00000000..78106040 --- /dev/null +++ b/src/main/java/nextstep/security/interceptor/FormLoginAuthorizationInterceptor.java @@ -0,0 +1,36 @@ +package nextstep.security.interceptor; + +import nextstep.app.domain.Member; +import nextstep.app.domain.UserDetailService; +import org.springframework.web.servlet.HandlerInterceptor; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public class FormLoginAuthorizationInterceptor implements HandlerInterceptor { + private final UserDetailService userDetailService; + + public FormLoginAuthorizationInterceptor(UserDetailService userDetailService) { + this.userDetailService = userDetailService; + } + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + String username = request.getParameter("username"); + String password = request.getParameter("password"); + + if (username == null || password == null) { + response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "username, password가 필요합니다."); + return false; + } + + Member member = userDetailService.retrieveMemberByEmailAndPassword(username, password); + if (member == null) { + response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "username, password가 일치하지 않슴니다."); + return false; + } + + request.getSession().setAttribute("SPRING_SECURITY_CONTEXT", member); + return true; + } +} From 196eb9959f5c260faf8c67f9316e8c65d791268d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=B5=9C=EC=A3=BC=ED=98=95/=EC=9B=B9=EC=84=9C=EB=B9=84?= =?UTF-8?q?=EC=8A=A4=EC=84=9C=EB=B2=84=EA=B0=9C=EB=B0=9CCell/Kia?= Date: Sat, 2 Nov 2024 22:27:04 +0900 Subject: [PATCH 4/6] =?UTF-8?q?fix:=20Basic=20Interceptor=20=EC=9D=B8?= =?UTF-8?q?=EC=A6=9D=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84=20=EB=B0=8F?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/nextstep/app/config/WebConfig.java | 2 +- .../security/constants/SecurityConstants.java | 9 ++++ .../BasicAuthenticationInterceptor.java | 42 +++++++++++++------ .../FormLoginAuthorizationInterceptor.java | 8 ++-- 4 files changed, 44 insertions(+), 17 deletions(-) create mode 100644 src/main/java/nextstep/security/constants/SecurityConstants.java diff --git a/src/main/java/nextstep/app/config/WebConfig.java b/src/main/java/nextstep/app/config/WebConfig.java index 6c7a1c5a..55c5ada5 100644 --- a/src/main/java/nextstep/app/config/WebConfig.java +++ b/src/main/java/nextstep/app/config/WebConfig.java @@ -18,6 +18,6 @@ public WebConfig(UserDetailService userDetailService) { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new FormLoginAuthorizationInterceptor(userDetailService)).addPathPatterns("/login"); - registry.addInterceptor(new BasicAuthenticationInterceptor()).addPathPatterns("/members"); + registry.addInterceptor(new BasicAuthenticationInterceptor(userDetailService)).addPathPatterns("/members"); } } diff --git a/src/main/java/nextstep/security/constants/SecurityConstants.java b/src/main/java/nextstep/security/constants/SecurityConstants.java new file mode 100644 index 00000000..58d7c342 --- /dev/null +++ b/src/main/java/nextstep/security/constants/SecurityConstants.java @@ -0,0 +1,9 @@ +package nextstep.security.constants; + +public class SecurityConstants { + public static final String BASIC_AUTH_HEADER = "Basic"; + public static final String USERNAME = "username"; + public static final String PASSWORD = "password"; + public static final String SPRING_SECURITY_CONTEXT_KEY = "SPRING_SECURITY_CONTEXT"; + +} diff --git a/src/main/java/nextstep/security/interceptor/BasicAuthenticationInterceptor.java b/src/main/java/nextstep/security/interceptor/BasicAuthenticationInterceptor.java index adc658d2..3de29493 100644 --- a/src/main/java/nextstep/security/interceptor/BasicAuthenticationInterceptor.java +++ b/src/main/java/nextstep/security/interceptor/BasicAuthenticationInterceptor.java @@ -1,5 +1,7 @@ package nextstep.security.interceptor; +import nextstep.app.domain.Member; +import nextstep.app.domain.UserDetailService; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.web.servlet.HandlerInterceptor; @@ -9,29 +11,43 @@ import java.nio.charset.StandardCharsets; import java.util.Base64; +import static nextstep.security.constants.SecurityConstants.BASIC_AUTH_HEADER; + public class BasicAuthenticationInterceptor implements HandlerInterceptor { - private static final String BASIC_AUTH = "Basic"; - private static final String USERNAME = "username"; - private static final String PASSWORD = "password"; + private final UserDetailService userDetailService; + + public BasicAuthenticationInterceptor(UserDetailService userDetailService) { + this.userDetailService = userDetailService; + } @Override - public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String authorizationHeader = request.getHeader(HttpHeaders.AUTHORIZATION); + if (authorizationHeader == null || !authorizationHeader.startsWith(BASIC_AUTH_HEADER)) { + response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "인증되지 않은 사용자입니다."); + return false; + } + + String[] values = getBasicValues(authorizationHeader); + if (values.length != 2) { + response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "인증되지 않은 사용자입니다."); + return false; + } + + String username = values[0]; + String password = values[1]; - if (authorizationHeader != null && authorizationHeader.startsWith(BASIC_AUTH)) { - String[] values = getBasicValues(authorizationHeader); - if (values.length == 2 && values[0].equals(USERNAME) && values[1].equals(PASSWORD)) { - return true; // 인증 성공 시 요청을 진행 - } + Member member = userDetailService.retrieveMemberByEmailAndPassword(username, password); + if (member == null) { + response.setStatus(HttpStatus.UNAUTHORIZED.value()); + return false; } - // 인증 실패 시 401 상태 코드를 반환 - response.setStatus(HttpStatus.UNAUTHORIZED.value()); - return false; + return true; } private String[] getBasicValues(String authorizationHeader) { - String base64Credentials = authorizationHeader.substring(BASIC_AUTH.length()).trim(); + String base64Credentials = authorizationHeader.substring(BASIC_AUTH_HEADER.length()).trim(); String credentials = new String(Base64.getDecoder().decode(base64Credentials), StandardCharsets.UTF_8); return credentials.split(":", 2); } diff --git a/src/main/java/nextstep/security/interceptor/FormLoginAuthorizationInterceptor.java b/src/main/java/nextstep/security/interceptor/FormLoginAuthorizationInterceptor.java index 78106040..c811c4e5 100644 --- a/src/main/java/nextstep/security/interceptor/FormLoginAuthorizationInterceptor.java +++ b/src/main/java/nextstep/security/interceptor/FormLoginAuthorizationInterceptor.java @@ -7,6 +7,8 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import static nextstep.security.constants.SecurityConstants.*; + public class FormLoginAuthorizationInterceptor implements HandlerInterceptor { private final UserDetailService userDetailService; @@ -16,8 +18,8 @@ public FormLoginAuthorizationInterceptor(UserDetailService userDetailService) { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { - String username = request.getParameter("username"); - String password = request.getParameter("password"); + String username = request.getParameter(USERNAME); + String password = request.getParameter(PASSWORD); if (username == null || password == null) { response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "username, password가 필요합니다."); @@ -30,7 +32,7 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons return false; } - request.getSession().setAttribute("SPRING_SECURITY_CONTEXT", member); + request.getSession().setAttribute(SPRING_SECURITY_CONTEXT_KEY, member); return true; } } From fb08302cb0c318fa7bc0580acb31485e56b094fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=B5=9C=EC=A3=BC=ED=98=95/=EC=9B=B9=EC=84=9C=EB=B9=84?= =?UTF-8?q?=EC=8A=A4=EC=84=9C=EB=B2=84=EA=B0=9C=EB=B0=9CCell/Kia?= Date: Sat, 2 Nov 2024 22:39:12 +0900 Subject: [PATCH 5/6] =?UTF-8?q?fix:=20=EC=9D=98=EC=A1=B4=EC=84=B1=20?= =?UTF-8?q?=EC=82=AC=EC=9D=B4=ED=81=B4=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/nextstep/app/config/WebConfig.java | 2 +- .../nextstep/app/domain/{ => member/param}/Member.java | 8 ++++++-- .../{ => member/repository}/MemberRepository.java | 4 +++- .../service/MemberService.java} | 10 +++++++--- .../app/infrastructure/InmemoryMemberRepository.java | 4 ++-- src/main/java/nextstep/app/ui/LoginController.java | 2 +- src/main/java/nextstep/app/ui/MemberController.java | 4 ++-- .../interceptor/BasicAuthenticationInterceptor.java | 4 ++-- .../interceptor/FormLoginAuthorizationInterceptor.java | 3 +-- src/main/java/nextstep/security/param/UserDetails.java | 7 +++++++ .../nextstep/security/service/UserDetailsService.java | 7 +++++++ src/test/java/nextstep/app/LoginTest.java | 3 +-- src/test/java/nextstep/app/MemberTest.java | 4 ++-- 13 files changed, 42 insertions(+), 20 deletions(-) rename src/main/java/nextstep/app/domain/{ => member/param}/Member.java (78%) rename src/main/java/nextstep/app/domain/{ => member/repository}/MemberRepository.java (67%) rename src/main/java/nextstep/app/domain/{UserDetailService.java => member/service/MemberService.java} (54%) create mode 100644 src/main/java/nextstep/security/param/UserDetails.java create mode 100644 src/main/java/nextstep/security/service/UserDetailsService.java diff --git a/src/main/java/nextstep/app/config/WebConfig.java b/src/main/java/nextstep/app/config/WebConfig.java index 55c5ada5..554b067d 100644 --- a/src/main/java/nextstep/app/config/WebConfig.java +++ b/src/main/java/nextstep/app/config/WebConfig.java @@ -1,8 +1,8 @@ package nextstep.app.config; -import nextstep.app.domain.UserDetailService; import nextstep.security.interceptor.BasicAuthenticationInterceptor; import nextstep.security.interceptor.FormLoginAuthorizationInterceptor; +import nextstep.security.service.UserDetailService; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; diff --git a/src/main/java/nextstep/app/domain/Member.java b/src/main/java/nextstep/app/domain/member/param/Member.java similarity index 78% rename from src/main/java/nextstep/app/domain/Member.java rename to src/main/java/nextstep/app/domain/member/param/Member.java index 6cafa9c7..bafa7031 100644 --- a/src/main/java/nextstep/app/domain/Member.java +++ b/src/main/java/nextstep/app/domain/member/param/Member.java @@ -1,6 +1,8 @@ -package nextstep.app.domain; +package nextstep.app.domain.member.param; -public class Member { +import nextstep.security.param.UserDetails; + +public class Member implements UserDetails { private final String email; private final String password; private final String name; @@ -13,10 +15,12 @@ public Member(String email, String password, String name, String imageUrl) { this.imageUrl = imageUrl; } + @Override public String getEmail() { return email; } + @Override public String getPassword() { return password; } diff --git a/src/main/java/nextstep/app/domain/MemberRepository.java b/src/main/java/nextstep/app/domain/member/repository/MemberRepository.java similarity index 67% rename from src/main/java/nextstep/app/domain/MemberRepository.java rename to src/main/java/nextstep/app/domain/member/repository/MemberRepository.java index 2eb5cdbb..feee5e96 100644 --- a/src/main/java/nextstep/app/domain/MemberRepository.java +++ b/src/main/java/nextstep/app/domain/member/repository/MemberRepository.java @@ -1,4 +1,6 @@ -package nextstep.app.domain; +package nextstep.app.domain.member.repository; + +import nextstep.app.domain.member.param.Member; import java.util.List; import java.util.Optional; diff --git a/src/main/java/nextstep/app/domain/UserDetailService.java b/src/main/java/nextstep/app/domain/member/service/MemberService.java similarity index 54% rename from src/main/java/nextstep/app/domain/UserDetailService.java rename to src/main/java/nextstep/app/domain/member/service/MemberService.java index 671438cd..2d4e3f61 100644 --- a/src/main/java/nextstep/app/domain/UserDetailService.java +++ b/src/main/java/nextstep/app/domain/member/service/MemberService.java @@ -1,16 +1,20 @@ -package nextstep.app.domain; +package nextstep.app.domain.member.service; +import nextstep.app.domain.member.param.Member; +import nextstep.app.domain.member.repository.MemberRepository; +import nextstep.security.service.UserDetailsService; import org.springframework.stereotype.Service; @Service -public class UserDetailService { +public class MemberService implements UserDetailsService { private final MemberRepository memberRepository; - public UserDetailService(MemberRepository memberRepository) { + public MemberService(MemberRepository memberRepository) { this.memberRepository = memberRepository; } + @Override public Member retrieveMemberByEmailAndPassword(String email, String password) { return memberRepository.findByEmail(email) .filter(member -> member.getPassword().equals(password)) diff --git a/src/main/java/nextstep/app/infrastructure/InmemoryMemberRepository.java b/src/main/java/nextstep/app/infrastructure/InmemoryMemberRepository.java index 5a6062cf..fba17bc6 100644 --- a/src/main/java/nextstep/app/infrastructure/InmemoryMemberRepository.java +++ b/src/main/java/nextstep/app/infrastructure/InmemoryMemberRepository.java @@ -1,7 +1,7 @@ package nextstep.app.infrastructure; -import nextstep.app.domain.Member; -import nextstep.app.domain.MemberRepository; +import nextstep.app.domain.member.param.Member; +import nextstep.app.domain.member.repository.MemberRepository; import org.springframework.stereotype.Repository; import java.util.HashMap; diff --git a/src/main/java/nextstep/app/ui/LoginController.java b/src/main/java/nextstep/app/ui/LoginController.java index 0ea94f1b..f630ac78 100644 --- a/src/main/java/nextstep/app/ui/LoginController.java +++ b/src/main/java/nextstep/app/ui/LoginController.java @@ -1,6 +1,6 @@ package nextstep.app.ui; -import nextstep.app.domain.MemberRepository; +import nextstep.app.domain.member.repository.MemberRepository; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ExceptionHandler; diff --git a/src/main/java/nextstep/app/ui/MemberController.java b/src/main/java/nextstep/app/ui/MemberController.java index c8cc74d6..fe45335f 100644 --- a/src/main/java/nextstep/app/ui/MemberController.java +++ b/src/main/java/nextstep/app/ui/MemberController.java @@ -1,7 +1,7 @@ package nextstep.app.ui; -import nextstep.app.domain.Member; -import nextstep.app.domain.MemberRepository; +import nextstep.app.domain.member.param.Member; +import nextstep.app.domain.member.repository.MemberRepository; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; diff --git a/src/main/java/nextstep/security/interceptor/BasicAuthenticationInterceptor.java b/src/main/java/nextstep/security/interceptor/BasicAuthenticationInterceptor.java index 3de29493..16db579c 100644 --- a/src/main/java/nextstep/security/interceptor/BasicAuthenticationInterceptor.java +++ b/src/main/java/nextstep/security/interceptor/BasicAuthenticationInterceptor.java @@ -1,7 +1,7 @@ package nextstep.security.interceptor; -import nextstep.app.domain.Member; -import nextstep.app.domain.UserDetailService; +import nextstep.app.domain.member.param.Member; +import nextstep.security.service.UserDetailService; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.web.servlet.HandlerInterceptor; diff --git a/src/main/java/nextstep/security/interceptor/FormLoginAuthorizationInterceptor.java b/src/main/java/nextstep/security/interceptor/FormLoginAuthorizationInterceptor.java index c811c4e5..510edcfe 100644 --- a/src/main/java/nextstep/security/interceptor/FormLoginAuthorizationInterceptor.java +++ b/src/main/java/nextstep/security/interceptor/FormLoginAuthorizationInterceptor.java @@ -1,7 +1,6 @@ package nextstep.security.interceptor; -import nextstep.app.domain.Member; -import nextstep.app.domain.UserDetailService; +import nextstep.app.domain.member.param.Member; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; diff --git a/src/main/java/nextstep/security/param/UserDetails.java b/src/main/java/nextstep/security/param/UserDetails.java new file mode 100644 index 00000000..75068914 --- /dev/null +++ b/src/main/java/nextstep/security/param/UserDetails.java @@ -0,0 +1,7 @@ +package nextstep.security.param; + +public interface UserDetails { + String getEmail(); + + String getPassword(); +} diff --git a/src/main/java/nextstep/security/service/UserDetailsService.java b/src/main/java/nextstep/security/service/UserDetailsService.java new file mode 100644 index 00000000..0c7dd4e1 --- /dev/null +++ b/src/main/java/nextstep/security/service/UserDetailsService.java @@ -0,0 +1,7 @@ +package nextstep.security.service; + +import nextstep.security.param.UserDetails; + +public interface UserDetailsService { + UserDetails retrieveMemberByEmailAndPassword(String email, String password); +} diff --git a/src/test/java/nextstep/app/LoginTest.java b/src/test/java/nextstep/app/LoginTest.java index 717bcc8a..149cb555 100644 --- a/src/test/java/nextstep/app/LoginTest.java +++ b/src/test/java/nextstep/app/LoginTest.java @@ -1,6 +1,6 @@ package nextstep.app; -import nextstep.app.domain.Member; +import nextstep.app.domain.member.param.Member; import nextstep.app.infrastructure.InmemoryMemberRepository; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -9,7 +9,6 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.ResultActions; import javax.servlet.http.HttpSession; diff --git a/src/test/java/nextstep/app/MemberTest.java b/src/test/java/nextstep/app/MemberTest.java index 58aba17b..254f31b9 100644 --- a/src/test/java/nextstep/app/MemberTest.java +++ b/src/test/java/nextstep/app/MemberTest.java @@ -1,7 +1,7 @@ package nextstep.app; -import nextstep.app.domain.Member; -import nextstep.app.domain.MemberRepository; +import nextstep.app.domain.member.param.Member; +import nextstep.app.domain.member.repository.MemberRepository; import nextstep.app.infrastructure.InmemoryMemberRepository; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; From 8aedd7cf2d4c3e7f17f506b1e39ff6ff4d45cae7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=B5=9C=EC=A3=BC=ED=98=95/=EC=9B=B9=EC=84=9C=EB=B9=84?= =?UTF-8?q?=EC=8A=A4=EC=84=9C=EB=B2=84=EA=B0=9C=EB=B0=9CCell/Kia?= Date: Sat, 2 Nov 2024 22:42:07 +0900 Subject: [PATCH 6/6] =?UTF-8?q?fix:=20=EC=9D=98=EC=A1=B4=EC=84=B1=20?= =?UTF-8?q?=EC=82=AC=EC=9D=B4=ED=81=B4=20=EC=A0=9C=EA=B1=B0=20-=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 --- src/main/java/nextstep/app/config/WebConfig.java | 12 ++++++------ .../interceptor/BasicAuthenticationInterceptor.java | 12 ++++++------ .../FormLoginAuthorizationInterceptor.java | 13 +++++++------ 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/main/java/nextstep/app/config/WebConfig.java b/src/main/java/nextstep/app/config/WebConfig.java index 554b067d..f7e090bf 100644 --- a/src/main/java/nextstep/app/config/WebConfig.java +++ b/src/main/java/nextstep/app/config/WebConfig.java @@ -2,22 +2,22 @@ import nextstep.security.interceptor.BasicAuthenticationInterceptor; import nextstep.security.interceptor.FormLoginAuthorizationInterceptor; -import nextstep.security.service.UserDetailService; +import nextstep.security.service.UserDetailsService; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class WebConfig implements WebMvcConfigurer { - private final UserDetailService userDetailService; + private final UserDetailsService userDetailsService; - public WebConfig(UserDetailService userDetailService) { - this.userDetailService = userDetailService; + public WebConfig(UserDetailsService userDetailsService) { + this.userDetailsService = userDetailsService; } @Override public void addInterceptors(InterceptorRegistry registry) { - registry.addInterceptor(new FormLoginAuthorizationInterceptor(userDetailService)).addPathPatterns("/login"); - registry.addInterceptor(new BasicAuthenticationInterceptor(userDetailService)).addPathPatterns("/members"); + registry.addInterceptor(new FormLoginAuthorizationInterceptor(userDetailsService)).addPathPatterns("/login"); + registry.addInterceptor(new BasicAuthenticationInterceptor(userDetailsService)).addPathPatterns("/members"); } } diff --git a/src/main/java/nextstep/security/interceptor/BasicAuthenticationInterceptor.java b/src/main/java/nextstep/security/interceptor/BasicAuthenticationInterceptor.java index 16db579c..d67a24ba 100644 --- a/src/main/java/nextstep/security/interceptor/BasicAuthenticationInterceptor.java +++ b/src/main/java/nextstep/security/interceptor/BasicAuthenticationInterceptor.java @@ -1,7 +1,7 @@ package nextstep.security.interceptor; -import nextstep.app.domain.member.param.Member; -import nextstep.security.service.UserDetailService; +import nextstep.security.param.UserDetails; +import nextstep.security.service.UserDetailsService; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.web.servlet.HandlerInterceptor; @@ -14,9 +14,9 @@ import static nextstep.security.constants.SecurityConstants.BASIC_AUTH_HEADER; public class BasicAuthenticationInterceptor implements HandlerInterceptor { - private final UserDetailService userDetailService; + private final UserDetailsService userDetailService; - public BasicAuthenticationInterceptor(UserDetailService userDetailService) { + public BasicAuthenticationInterceptor(UserDetailsService userDetailService) { this.userDetailService = userDetailService; } @@ -37,8 +37,8 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons String username = values[0]; String password = values[1]; - Member member = userDetailService.retrieveMemberByEmailAndPassword(username, password); - if (member == null) { + UserDetails userDetails = userDetailService.retrieveMemberByEmailAndPassword(username, password); + if (userDetails == null) { response.setStatus(HttpStatus.UNAUTHORIZED.value()); return false; } diff --git a/src/main/java/nextstep/security/interceptor/FormLoginAuthorizationInterceptor.java b/src/main/java/nextstep/security/interceptor/FormLoginAuthorizationInterceptor.java index 510edcfe..f8e41e11 100644 --- a/src/main/java/nextstep/security/interceptor/FormLoginAuthorizationInterceptor.java +++ b/src/main/java/nextstep/security/interceptor/FormLoginAuthorizationInterceptor.java @@ -1,6 +1,7 @@ package nextstep.security.interceptor; -import nextstep.app.domain.member.param.Member; +import nextstep.security.param.UserDetails; +import nextstep.security.service.UserDetailsService; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; @@ -9,9 +10,9 @@ import static nextstep.security.constants.SecurityConstants.*; public class FormLoginAuthorizationInterceptor implements HandlerInterceptor { - private final UserDetailService userDetailService; + private final UserDetailsService userDetailService; - public FormLoginAuthorizationInterceptor(UserDetailService userDetailService) { + public FormLoginAuthorizationInterceptor(UserDetailsService userDetailService) { this.userDetailService = userDetailService; } @@ -25,13 +26,13 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons return false; } - Member member = userDetailService.retrieveMemberByEmailAndPassword(username, password); - if (member == null) { + UserDetails userDetails = userDetailService.retrieveMemberByEmailAndPassword(username, password); + if (userDetails == null) { response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "username, password가 일치하지 않슴니다."); return false; } - request.getSession().setAttribute(SPRING_SECURITY_CONTEXT_KEY, member); + request.getSession().setAttribute(SPRING_SECURITY_CONTEXT_KEY, userDetails); return true; } }