Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

3단계 - 즐겨찾기 기능 구현 #355

Open
wants to merge 29 commits into
base: hscom96
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
b199287
feat: 1단계 초기화
Aug 15, 2022
7730e74
feat: 권한 enum 추가
Aug 15, 2022
bf6f801
refactor: 메서드 위치 변경
Aug 15, 2022
0ebcbd6
feat: custom exception 추가
Aug 16, 2022
ea1eca3
fix: 테스트 오류 수정
Sep 4, 2022
dcb9838
refactor: XXXAuthenticationFilter의 구조화
Sep 5, 2022
8109ffa
refactor: auth 패키지와 member 패키지에 대한 의존 제거
Sep 5, 2022
73ba170
refactor: UserDetail 의존성 역전
Sep 5, 2022
3d45966
refactor: AuthNotChainInterceptor 메서드 구조화
Sep 5, 2022
9570e2b
refactor: AuthChainInterceptor 메서드 구조화
Sep 5, 2022
4aa8fb1
style: 클래스명 UserDetailService 변경
Sep 6, 2022
6ed9daf
Merge branch 'hscom96' into step2
Sep 7, 2022
18e19c0
refactor: given() 메서드 간소화
Sep 7, 2022
797aff7
feat: @Overide 태그 추가
Sep 12, 2022
1859e9b
style: userdetailservice 변수명 변경
Sep 12, 2022
6b73538
style: 클래스명 변경 및 메서드명 통일
Sep 12, 2022
b794805
refactor: AuthenticationPrincipalArgumentResolver 클래스 member 패키지 의존성 제거
Sep 14, 2022
91d8c97
feat: 인수테스트 작성
Sep 26, 2022
05305bc
feat: 즐겨찾기 등록 controller 구현
Sep 26, 2022
2d746dd
feat: 즐겨찾기 도메인 구현
Sep 27, 2022
0c419ad
feat: 즐겨찾기 등록 service 구현
Sep 27, 2022
a8d4b2a
feat: 즐겨찾기 조회 구현
Sep 29, 2022
fefa7ed
feat: 즐겨찾기 삭제 구현
Sep 29, 2022
d9c8b9b
merge commit
Sep 29, 2022
49171fe
merge commit
Sep 29, 2022
d1b97b5
feat: 트랜잭션 추가
Oct 1, 2022
33bc833
feat: 예외클래스 구체화
Oct 1, 2022
e194e54
test: 유효하지 않은 사용자 예외 케이스 검증 추가
Oct 13, 2022
0e880a2
feat: UserDetail 인터페이스 생성 및 User 객체 추상화
Oct 13, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/main/java/nextstep/auth/AuthConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@
@RequiredArgsConstructor
@Configuration
public class AuthConfig implements WebMvcConfigurer {
private final UserDetailService loginMemberService;
private final UserDetailService userDetailService;
hscom96 marked this conversation as resolved.
Show resolved Hide resolved
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));
}

Expand Down
17 changes: 9 additions & 8 deletions src/main/java/nextstep/auth/authentication/BasicAuthFilter.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,20 @@

import lombok.RequiredArgsConstructor;
import nextstep.auth.context.Authentication;
import nextstep.auth.context.SecurityContextHolder;
import nextstep.auth.user.UserDetail;
import nextstep.auth.user.User;
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;
import javax.servlet.http.HttpServletResponse;

@RequiredArgsConstructor
public class BasicAuthFilter extends AuthChainInterceptor {
public class BasicAuthFilter extends AuthContextChainInterceptor {
private final UserDetailService userDetailService;

@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();
}
Expand All @@ -25,12 +24,14 @@ 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());
}

protected AuthenticationToken getAuthenticationToken(final HttpServletRequest request) {
@Override
protected AuthenticationToken createAuthToken(final HttpServletRequest request) {
String authCredentials = AuthorizationExtractor.extract(request, AuthorizationType.BASIC);
String authHeader = new String(Base64.decodeBase64(authCredentials));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,23 @@
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
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<String> roles = jwtTokenProvider.getRoles(token.getPrincipal());
Expand All @@ -26,7 +28,8 @@ protected Authentication getAuthentication(final AuthenticationToken token) {
return authentication;
}

protected AuthenticationToken getAuthenticationToken(final HttpServletRequest request) {
@Override
protected AuthenticationToken createAuthToken(final HttpServletRequest request) {
String authCredentials = AuthorizationExtractor.extract(request, AuthorizationType.BEARER);
return new AuthenticationToken(authCredentials, authCredentials);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import nextstep.auth.context.Authentication;
import nextstep.auth.context.SecurityContextHolder;
import nextstep.member.domain.LoginMember;
import nextstep.auth.user.User;
hscom96 marked this conversation as resolved.
Show resolved Hide resolved
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
Expand All @@ -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());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -19,9 +19,9 @@ 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);
UserDetail userDetail = getUserDetail(authToken);
afterSuccessUserCheck(request, response, userDetail);
checkValidAuth(authToken);
User user = getUserDetail(authToken);
afterSuccessUserCheck(request, response, user);
return false;
} catch (Exception e) {
return true;
Expand All @@ -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 checkValidUser(AuthenticationToken authToken){
UserDetail loginMember = getUserDetail(authToken);
protected void checkValidAuth(AuthenticationToken authToken){
User loginMember = getUserDetail(authToken);

if (loginMember == null) {
throw new AuthenticationException();
Expand All @@ -45,7 +45,7 @@ protected void checkValidUser(AuthenticationToken authToken){
}
}

private UserDetail getUserDetail(final AuthenticationToken authToken) {
private User getUserDetail(final AuthenticationToken authToken) {
return userDetailService.loadUserByUsername(authToken.getPrincipal());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -27,11 +27,11 @@ public void checkAuthorities(JoinPoint joinPoint) {
List<String> 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));
}
}
6 changes: 3 additions & 3 deletions src/main/java/nextstep/auth/token/TokenAuthInterceptor.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
33 changes: 33 additions & 0 deletions src/main/java/nextstep/auth/user/User.java
Original file line number Diff line number Diff line change
@@ -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<String> authorities;

public static User of(String email, List<String> authorities) {
return new User(email, null, authorities);
}

public static User of(String email, String password, List<String> 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);
}
}
13 changes: 0 additions & 13 deletions src/main/java/nextstep/auth/user/UserDetail.java

This file was deleted.

2 changes: 1 addition & 1 deletion src/main/java/nextstep/auth/user/UserDetailService.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package nextstep.auth.user;

public interface UserDetailService {
UserDetail loadUserByUsername(String email);
User loadUserByUsername(String email);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UserDetail 인터페이스를 제거하고 User 클래스로 대체하기 보다
User 클래스가 UserDetail 인터페이스를 구현하는 형태는 어떨까요?

Copy link
Author

@hscom96 hscom96 Oct 13, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네 먼저 말씀하신대로 다음 PR과 같이 구현 해봤는데 0e880a2

말씀하신 부분에서 궁금한점이 있습니다.

인터페이스 (ex. UserDetail) 사용의 효과중 하나로 의존성 역전을 위한것으로 이해했는데
제가 만든 프로젝트에서는 member 패키지에서 LoginMember가 삭제되고, User객체는 auth 패키지에만 있어서
기존처럼 User클래스가 UserDetail 인터페이스를 상속 안해줘도 member에서 auth패키지로 의존성이 한방향으로만 흐르고 있고

또한 User와 UserDetail이 같은 패키지 내부에 있는데

여기서 User가 UserDetail을 구현하는 장점이 어떤게 있을지 알 수 있을까요? 🤔

}
2 changes: 1 addition & 1 deletion src/main/java/nextstep/common/CommonResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
public class CommonResponse<T> {
private int code;
private String message;
T data;
private T data;

public CommonResponse(ResponseCode responseCode) {
this.code = responseCode.getCode();
Expand Down
9 changes: 9 additions & 0 deletions src/main/java/nextstep/common/exception/AuthException.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
17 changes: 17 additions & 0 deletions src/main/java/nextstep/common/exception/code/AuthCode.java
Original file line number Diff line number Diff line change
@@ -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;
}
}
3 changes: 1 addition & 2 deletions src/main/java/nextstep/common/exception/code/CommonCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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());
}
}
Loading