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주차] 과제 #4

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open

[3주차] 과제 #4

wants to merge 9 commits into from

Conversation

JungYoonShin
Copy link
Member

@JungYoonShin JungYoonShin commented Nov 4, 2024

💗 Work Description

  • 우선 로그인/회원가입 API는 따로 구현하지 않았고,, DB에 임시 유저를 생성해서 작업했습니다..ㅎㅎ
  • 모든 요구사항을 반영하지는 못했습니다..(ex. 목록조회시 정렬기준, 공개하기 기능)

🔆 구현 과정

- 메인 홈(794d204)

  • request parameter를 통해 전달받은 Category에 대한 유효성 검증은 Controller단에서 진행했습니다. String값으로 전달 받은 category 값을 enum 타입의 객체로 변환함과 동시에 정의된 카테고리 값들 중에 해당하지 않는 경우에는 예외를 던집니다.

public static Category findCategory(String name) {
if (name == null) {
return null;
}
return Arrays.stream(Category.values()).filter(category -> category.name.equals(name)).findAny()
.orElseThrow(() -> new DiaryException(NOT_EXISTS_CATEGORY));
}


- 내 일기 모아보기(7973e97)

  • 저의 경우 유저를 식별하기 위해, request headerusenamepassword를 받아 '인증''인가' 처리를 하도록 구현했습니다!
  • 해당 인증 및 인가 처리를 위해 유저 정보를 받는 API가 다수 있어서 헤더 키 값을 PASSWORD_HEADER 처럼 상수 처리 했습니다!
    // 내 일기 모아보기
    @GetMapping("/mypage/diary")
    public ResponseEntity<DiaryListResponse> getMyDairyList(
    @RequestHeader(USERNAME_HEADER) String username,
    @RequestHeader(PASSWORD_HEADER) String password,
    @RequestParam(required = false) String category
    ) {
    DiaryListResponse diaryListResponse = diaryService.getMyDiaryList(Category.findCategory(category), username, password);
    return ResponseEntity.ok().body(diaryListResponse);
    }

- interceptor를 통한 유저 인가 기능 구현(3e2c210)

  • 우선 일기 수정, 삭제 기능의 경우 해당 일기를 작성한 유저만 삭제 및 수정 권한을 가져야 한다고 판단했습니다!

  • 해당 인가 처리를 어디서 할지 고민을 했는데요.. 고민한 방안은 다음과 같이 2가지였습니다!

    1. Controller단에서 Validator 클래스를 호출하여 인가 과정 처리
    1. interceptor를 구현하여 컨트롤러의 메서드에 도달하기전 인가 과정 처리
  • 제가 선택한 방법은 후자이며, 이유는 모든 컨트롤러 마다 1번과 같은 로직을 추가하기 보다는 interceptor를 통해 전역적인 인가처리를 하는 것이 좋을 것 같다고 판단하였습니다.


👉 1. 커스텀 어노테이션 생성


👉 2. HandlerInterceptor 인터페이스 구현

  • handler가 메서드인 경우 그리고 위에서 만든 어노테이션이 적용된 메서드일 경우에만 인가 로직을 거치게 됩니다.
    @Component
    @RequiredArgsConstructor
    public class CheckUserAuthInterceptor implements HandlerInterceptor {
    private final String USERNAME_HEADER = "username";
    private final String PASSWORD_HEADER = "password";
    private final DiaryRepository diaryRepository;
    private final UserRepository userRepository;
    @Override
    @Transactional
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    // Handler가 메소드인 경우에만 진행
    if (handler instanceof HandlerMethod) {
    HandlerMethod handlerMethod = (HandlerMethod) handler;
    // 핸들러 메서드에 @CheckUserAuth 어노테이션이 있는지 확인
    if (handlerMethod.getMethod().isAnnotationPresent(CheckUserAuth.class)) {
    String username = request.getHeader(USERNAME_HEADER);
    String password = request.getHeader(PASSWORD_HEADER);
    Map<String, String> pathVariables = (Map<String, String>) request
    .getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
    Long diaryId = Long.valueOf(pathVariables.get("diaryId"));
    User loginUser = getLoginUser(username);
    Diary diary = getDiary(diaryId);
    boolean isOwner = loginUser.equals(diary.getUser());
    if (!isOwner) {
    throw new UserException(NO_PERMISSON_FOR_DAIRY);
    }
    }
    }
    return true;
    }
    private User getLoginUser(String username) {
    return userRepository.findByUsername(username)
    .orElseThrow(() -> new UserException(NOT_EXISTS_USER));
    }
    private Diary getDiary(long diaryId) {
    return diaryRepository.findById(diaryId)
    .orElseThrow(() -> new DiaryException(NOT_EXISTS_DIARY_WITH_ID));
    }
    }

👉 3. Webconfiginterceptor등록

  • interceptor 등록을 위한 Config 파일이며, @component로 Bean으로 등록된 Interceptor를 가져와 registry에 등록해줍니다.
    @Configuration
    @RequiredArgsConstructor
    public class WebConfig implements WebMvcConfigurer {
    private final CheckUserAuthInterceptor checkUserAuthInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(checkUserAuthInterceptor);
    }
    }

💬 To Reviewers

  • 팟짱님이 말씀해주신 부분 반영해서 request dto 객체 자체를 service단으로 전달하지 않고 풀어서 파라미터로 전달해보았습니다!
  • 커스텀 예외처리에 대해 신경써보았는데, 어떨지 모르겠슴당 ㅎㅎ

🔗 Reference

interceptor를 통한 인가 처리

- 성공 및 실패 응답을 커스텀 처리하였습니다.
- 제목과 본문 제약조건 추가(null 불가)
- 카테고리 필드 추가
- username, password를 Request Header를 통해 전달받습니다.
- 삭제 및 조회에 대한 권한(인가) 처리 추후 필요
- 일기 수정 및 삭제 API에 대해 interceptor를 통해 인가 과정을 거치도록 하였습니다.
- service단에서 request에 대한 의존성 제거
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant