220103 jwt 걷어내고 session을 이용해서 인증/인가 정책 구현하기

GuruneLee·2022년 1월 5일
1

GIST청원사이트-BE

목록 보기
8/11
post-thumbnail

왜 Jwt 대신 Session 을 이용하는가?

Jwt 를 이용하는 가장 큰 이유 중 하나가 토큰의 자가수용성 이라고 생각한다. 토큰 자체적으로 필요한 정보를 담고 있다는 것인데, 필요한 정보를 담아서 발급해 버리면 해당 토큰으로 요청을 보냈을 때 db 를 찔러볼 필요가 없어진다 (db I/O 의 횟수가 성능에 큰 영향을 미친다).

하지만 누구나 디코딩을 통해 json 에 담긴 정보를 볼 수 있어 토큰엔 민감한 정보를 담지 않는것이 좋다. 따라서 userId 정도를 토큰에 담아 주면, 추후 요청에 함께 온 토큰을 까보고 userId 를 이용해 db에 쿼리를 날려 권한 관련된 정보를 꺼내와 비교하게 된다.

???

그렇다. 이상하다.
우리 팀의 판단으로는 회원인증/인가 구현에 jwt 를 사용하는 이유가 전혀 없었다.
그래서 jwt 대신 Session 을 이용하기로 하였다

참고자료: Why JWTs Suck as Session Tokens

Jwt 걷어내고 Session 집어넣기

사실 리팩토링 전에 jwt 를 사용하고 있었는데, 아니나 다를까 email 을 집어넣고 그거 까서 db 에 권한 정보를 받아내고 있었다!!
일단 토큰 생성 및 검증하는 클래스를 싹 걷어내면서 권한 인증 부분을 무로 되돌렸다...

이제 세션을 집어넣기 전에 세션에 대한 인튜이션을 주자면,

SessionForOne = Map<key, Information>
Map<SessionId, SessionForOne>

세션은 위와 같은 느낌으로 생성되고 저장된다. SessionId 는 클라이언트에 전달되어 추후 자신의 세션을 찾을때 사용된다.
(https://velog.io/@chlee4858/Java-HttpSession)

세션 관리 흐름

로그인 -> 성공시 세션에 사용자 권한 정보 저장 -> 클라이언트에 sessionId (JSESSIONID) 전달 -> 권한이 필요한 요청에 sessionId 를 담아 디바 -> 해당 sessionId 와 매칭되는 세션 정보 불러오기 -> 굳

구현

  1. 로그인
// UserController.java
@PostMapping("/login")
public ResponseEntity<Void> login(@Validated @RequestBody SignInRequest request) {
	userService.signIn(request);
	return ResponseEntity.ok().build();
}
// UserService.java
@Transactional(readOnly = true)
public void signIn(SignInRequest request) {
    User user = userRepository.findByUsername(request.getUsername())
            .orElseThrow(() -> new CustomException("존재하지 않는 회원 입니다."));
    if (!encoder.isMatch(request.getPassword(), user.getPassword())) {
        throw new CustomException("비밀번호를 다시 확인해주세요");
    }
    httpSession.setAttribute("user", new SessionUser(user));
}
  1. 권한 인증 (creatAnswer)
// AnswerController.java
@PostMapping("/posts/{postId}/answer")
public ResponseEntity<Object> createAnswer(@PathVariable Long postId,
                                           @Validated @RequestBody AnswerRequest answerRequest) {
    SessionUser sessionUser = (SessionUser) httpSession.getAttribute("user");
    if (!sessionUser.getEnabled()) {
        throw new CustomException("이메일 인증이 필요합니다!");
    }
    if (sessionUser.getUserRole() != UserRole.MANAGER && sessionUser.getUserRole() != UserRole.ADMIN) {
        throw new CustomException("답변 권한이 없습니다.");
    }
    Long answerId = answerService.createAnswer(postId, answerRequest, sessionUser.getId());
    return ResponseEntity.created(URI.create("/posts/" + postId + "/answer/" + answerId)).build();
}
  1. 로그아웃
// UserController.java
@PostMapping("/logout")
public ResponseEntity<Void> logout(){
    httpSession.removeAttribute("user");
    return ResponseEntity.ok().build();
}

공부할 거리

  1. Servlet Container 란 무엇인가 웹_컨테이너
  2. Servlet 이란 무엇인가 자바_서블릿
  3. JVM 에 대한 이야기 자바가상머신
profile
Today, I Shoveled AGAIN....

1개의 댓글

comment-user-thumbnail
2022년 1월 6일

따봉드립니다

답글 달기