-
Notifications
You must be signed in to change notification settings - Fork 179
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[2단계 - [장바구니 기능] 허브(방대의) 미션 제출합니다. (#300)
* docs: 2단계 요구사항 정리 * test: 잘못된 테스트명 수정 * feat: data.sql 수정 및 schema.sql 파일 추가 * refactor: auditing sql의 default를 사용하도록 변경 * refactor: 사용자 엔티티 추가 * feat: 사용자 설정 페이지 연동 기능 추가 * feat: 장바구니 페이지 연동 기능 추가 * feat: Basic Auth로 오는 값을 파싱하는 클래스 추가 * feat: 사용자 정보에 대한 ArgumentResolver 추가 * test: 잘못 작성된 테스트 수정 * feat: 장바구니 등록 및 조회 기능 추가 * feat: 장바구니 삭제 기능 추가 * refactor: Cart -> CartProduct로 수정 * refactor: create URI 수정 * feat: 인증을 인터셉터가 담당하도록 설정 * docs: 요구사항 업데이트 * feat: argumentResolver에서 threadLocal clear하는 기능 추가 * feat: Credential에서 비밀번호가 다른지 확인하는 기능 추가 * refactor: 기존에 인증과정에서 Member를 반환하던 Dao를 Credential을 반환하도록 수정 - AuthMemberDao -> CredentialDao - 추가로 Credential을 검증하는 과정에서 에러메시지를 조금 더 자세하게 출력하도록 변경 * feat: ExceptionHandler에 로깅 적용 * refactor: Auditing을 위한 필드 전부 제거 * refactor: BasicAuthorizationParser의 isNotValid를 제거하고 조금 더 응집력 있는 클래스로 변경 - 올바른 Basic 유형의 헤더(파싱 불가능한 경우) InvalidBasicCredentialException 예외 던지도록 변경 * refactor: Credential의 id를 memberId로 변경 * feat: getMemberId 메서드에 Nullable 애너테이션 추가 * refactor: 패키지 구조 변경 * feat: 도미노 페페로니 피자 추가 * remove: todo 주석 제거 * refactor: InvalidBasicCredentialException이 AuthenticationException을 상속 받도록 수정 * test: BaiscAuthorizationParser 테스트 출력 수정
- Loading branch information
1 parent
f282f04
commit cbc8152
Showing
48 changed files
with
1,498 additions
and
100 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package cart.auth; | ||
|
||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.lang.annotation.Target; | ||
|
||
@Target(ElementType.PARAMETER) | ||
@Retention(RetentionPolicy.RUNTIME) | ||
public @interface Auth { | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package cart.auth; | ||
|
||
import org.springframework.core.MethodParameter; | ||
import org.springframework.web.bind.support.WebDataBinderFactory; | ||
import org.springframework.web.context.request.NativeWebRequest; | ||
import org.springframework.web.method.support.HandlerMethodArgumentResolver; | ||
import org.springframework.web.method.support.ModelAndViewContainer; | ||
|
||
public class AuthArgumentResolver implements HandlerMethodArgumentResolver { | ||
|
||
private final CredentialThreadLocal credentialThreadLocal; | ||
|
||
public AuthArgumentResolver(final CredentialThreadLocal credentialThreadLocal) { | ||
this.credentialThreadLocal = credentialThreadLocal; | ||
} | ||
|
||
@Override | ||
public boolean supportsParameter(final MethodParameter parameter) { | ||
final boolean hasParameterAnnotation = parameter.hasParameterAnnotation(Auth.class); | ||
final boolean hasCredentialType = Credential.class.isAssignableFrom(parameter.getParameterType()); | ||
return hasParameterAnnotation && hasCredentialType; | ||
} | ||
|
||
@Override | ||
public Object resolveArgument( | ||
final MethodParameter parameter, | ||
final ModelAndViewContainer mavContainer, | ||
final NativeWebRequest webRequest, | ||
final WebDataBinderFactory binderFactory | ||
) { | ||
final Credential credential = credentialThreadLocal.get(); | ||
credentialThreadLocal.clear(); | ||
return credential; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package cart.auth; | ||
|
||
import cart.dao.CredentialDao; | ||
import cart.exception.AuthenticationException; | ||
import javax.servlet.http.HttpServletRequest; | ||
import javax.servlet.http.HttpServletResponse; | ||
import org.springframework.web.servlet.HandlerInterceptor; | ||
|
||
public class AuthInterceptor implements HandlerInterceptor { | ||
|
||
private static final String AUTHORIZATION_HEADER_NAME = "Authorization"; | ||
|
||
private final CredentialDao credentialDao; | ||
private final BasicAuthorizationParser basicAuthorizationParser; | ||
private final CredentialThreadLocal credentialThreadLocal; | ||
|
||
public AuthInterceptor( | ||
final CredentialDao credentialDao, | ||
final BasicAuthorizationParser basicAuthorizationParser, | ||
final CredentialThreadLocal credentialThreadLocal | ||
) { | ||
this.credentialDao = credentialDao; | ||
this.basicAuthorizationParser = basicAuthorizationParser; | ||
this.credentialThreadLocal = credentialThreadLocal; | ||
} | ||
|
||
@Override | ||
public boolean preHandle( | ||
final HttpServletRequest request, | ||
final HttpServletResponse response, | ||
final Object handler | ||
) { | ||
final String authorizationHeader = request.getHeader(AUTHORIZATION_HEADER_NAME); | ||
|
||
final Credential credential = basicAuthorizationParser.parse(authorizationHeader); | ||
final Credential savedCredential = credentialDao.findByEmail(credential.getEmail()) | ||
.orElseThrow(() -> new AuthenticationException("올바르지 않은 이메일입니다. 입력값: " + credential.getEmail())); | ||
|
||
if (credential.isNotSamePassword(savedCredential)) { | ||
throw new AuthenticationException(); | ||
} | ||
|
||
credentialThreadLocal.set(savedCredential); | ||
return true; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package cart.auth; | ||
|
||
import cart.exception.InvalidBasicCredentialException; | ||
import java.util.Base64; | ||
import org.springframework.stereotype.Component; | ||
|
||
@Component | ||
public class BasicAuthorizationParser { | ||
|
||
private static final String KEYWORD = "Basic "; | ||
private static final String DELIMITER = ":"; | ||
private static final int VALID_CREDENTIAL_SIZE = 2; | ||
private static final int EMAIL_INDEX = 0; | ||
private static final int PASSWORD_INDEX = 1; | ||
private static final String EMPTY = ""; | ||
|
||
public Credential parse(final String authorizationHeader) { | ||
final String[] credential = parseCredential(authorizationHeader); | ||
if (isInvalidBasicCredential(authorizationHeader)) { | ||
throw new InvalidBasicCredentialException(authorizationHeader); | ||
} | ||
return new Credential(credential[EMAIL_INDEX], credential[PASSWORD_INDEX]); | ||
} | ||
|
||
private String[] parseCredential(final String authorizationHeader) { | ||
final String credential = authorizationHeader.replace(KEYWORD, EMPTY); | ||
return decodeBase64(credential).split(DELIMITER); | ||
} | ||
|
||
private String decodeBase64(final String credential) { | ||
try { | ||
return new String(Base64.getDecoder().decode(credential)); | ||
} catch (final IllegalArgumentException e) { | ||
throw new InvalidBasicCredentialException(credential); | ||
} | ||
} | ||
|
||
private boolean isInvalidBasicCredential(final String authorizationHeader) { | ||
return !authorizationHeader.startsWith(KEYWORD) || | ||
parseCredential(authorizationHeader).length != VALID_CREDENTIAL_SIZE; | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package cart.auth; | ||
|
||
import org.springframework.lang.Nullable; | ||
|
||
public class Credential { | ||
|
||
private final Long memberId; | ||
private final String email; | ||
private final String password; | ||
|
||
public Credential(final String email, final String password) { | ||
this(null, email, password); | ||
} | ||
|
||
public Credential(final Long memberId, final String email, final String password) { | ||
this.memberId = memberId; | ||
this.email = email; | ||
this.password = password; | ||
} | ||
|
||
public boolean isNotSamePassword(final Credential credential) { | ||
return !this.password.equals(credential.getPassword()); | ||
} | ||
|
||
@Nullable | ||
public Long getMemberId() { | ||
return memberId; | ||
} | ||
|
||
public String getEmail() { | ||
return email; | ||
} | ||
|
||
public String getPassword() { | ||
return password; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package cart.auth; | ||
|
||
import org.springframework.stereotype.Component; | ||
|
||
@Component | ||
public class CredentialThreadLocal { | ||
|
||
private final ThreadLocal<Credential> local = new ThreadLocal<>(); | ||
|
||
public void set(final Credential credential) { | ||
local.set(credential); | ||
} | ||
|
||
public Credential get() { | ||
return local.get(); | ||
} | ||
|
||
public void clear() { | ||
local.remove(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package cart.config; | ||
|
||
import cart.auth.AuthArgumentResolver; | ||
import cart.auth.AuthInterceptor; | ||
import cart.auth.BasicAuthorizationParser; | ||
import cart.auth.CredentialThreadLocal; | ||
import cart.dao.CredentialDao; | ||
import java.util.List; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.web.method.support.HandlerMethodArgumentResolver; | ||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry; | ||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; | ||
|
||
@Configuration | ||
public class WebConfiguration implements WebMvcConfigurer { | ||
|
||
private final CredentialDao credentialDao; | ||
private final BasicAuthorizationParser basicAuthorizationParser; | ||
private final CredentialThreadLocal credentialThreadLocal; | ||
|
||
public WebConfiguration( | ||
final CredentialDao credentialDao, | ||
final BasicAuthorizationParser basicAuthorizationParser, | ||
final CredentialThreadLocal credentialThreadLocal | ||
) { | ||
this.credentialDao = credentialDao; | ||
this.basicAuthorizationParser = basicAuthorizationParser; | ||
this.credentialThreadLocal = credentialThreadLocal; | ||
} | ||
|
||
@Override | ||
public void addInterceptors(final InterceptorRegistry registry) { | ||
final AuthInterceptor authInterceptor = new AuthInterceptor( | ||
credentialDao, | ||
basicAuthorizationParser, | ||
credentialThreadLocal | ||
); | ||
registry.addInterceptor(authInterceptor).addPathPatterns("/cart-products/**"); | ||
} | ||
|
||
@Override | ||
public void addArgumentResolvers(final List<HandlerMethodArgumentResolver> resolvers) { | ||
resolvers.add(new AuthArgumentResolver(credentialThreadLocal)); | ||
} | ||
} |
Oops, something went wrong.