jwt,spring security를 활용한 사용자 인증 처리

Always·2024년 9월 25일
1

Backend&Devops

목록 보기
3/15
post-thumbnail

도입 배경

기존의 프로젝트에서의 문제점

  • 아래의 코드는 기존의 프로젝트내에서 Controller내에서 user의 정보를 반환하는 메서드입니다.

  • 위 같은 코드의 문제점은 해당 url을 통해서 어떤 사용자든 사용자의 정보를 확인 할 수 있다는 것입니다.
    • ex) http://…./main/user/phoneNumber=010~
    • 위와 같은 url을 통해서 어떤 사용자든 접근을 하여 사용자 및 여러 db의 정보를 확인 할 수 있어 보안에 큰 문제가 있습니다.
  • 이러한 보안상의 이슈를 해결하기 위해서, jwt를 이용해서 RequestParm을 대신하여, 사용자가 Api 요청을 할 때마다 처리하고자 합니다.

JWT란?

JWT( JSON Web Token )는 JSON 형식으로 클라이언트와 서버 간에 인증 정보를 안전하게 전달하기 위해 사용하는 토큰 기반 인증 방식입니다.

JWT의 구성

  • JWT는 크게 header,payload,signature로 나뉩니다.

  • header: signature를 암호화할 알고리즘으로써 일반적으로
    *HS256* 알고리즘이 많이 사용됩니다.

  • payload: 사용자의 정보를 담고 있으며 토큰 제목,토큰 대상자, 발급시각, 만료시각,유저의 id등을 담고 있습니다.

    • 또한 이 payload에는 민감한 유저의 정보를 담지 않아야합니다
    • 왜냐하면 복호화의 가능성이 있기 때문입니다.
  • signature: header와 payload를 합친 문자열을 암호화 알고리즘과 비밀키를 이용해서 암호화 한 것입니다.→중간에 변조를 막고, 토큰이 유효한 서버로부터 발급된 것임을 인증됩니다

    • 만약에 이 jwt가 중간에 탈취자에 의해서 변조가 되면,변조되어서 온 jwt의 signature와 header와 payload, 암호화 알고리즘,비밀키를 이용해서 만든 이 signature와 다를 것입니다.
    • 즉, jwt의 signature은 토큰의 변조되지 않았다는 무결성을 보장해줍니다.
  • jwt 예시)
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI5ODc2NTQzMjEwIiwibmFtZSI6IkphbmUgRG9lIiwiaWF0IjoxNjI1MjM5MDIyfQ.abcd1234efgh5678ijkl9012mnop3456qrst7890uvwx1234yzab5678cdef9012
  • 위처럼 맨 앞부분은 header, header다음의 .다음부분은 payload, 그 다음 부분은 signature입니다.

그렇다면 어떻게 적용?

https://datatracker.ietf.org/doc/html/rfc7519#section-1

“JWT는 HTTP의 Authorization Header나 URI Query Parameters와 같은 공간이 제한된 환경을 위한 간결한 클레임 표현 형식입니다.” 라고 소개하고 있습니다.

  • 위에서 보다싶이 jwt는 Http Authorization header에 담아서 보내는 것입니다.
  • 이렇게 보내진 jwt는 spring의 경우 spring security filter를 거치는 spring security가 적용됩니다.

JWT를 활용한 Spring security

Architecture

기본적으로 jwt를 활용한 spring security는 아래와 같은 아키텍쳐를 따릅니다.

(위 그림에서 token검증이 Authentication Filter에 들어가야한다고 봄)

로그인시에 jwt를 발급 후, client단에 넘겨주고 저장.

  1. 로그인한 클라이언트는 그 후의 작업을 할 때, API화 함께 Authorizaion header에 jwt를 담아서 넘겨줌.

  2. 그 후 AuthenticationFilter에서 통해서, 토큰이 유효한지를 검증

  3. token이 유효하면, 해당 토큰을 통해서 사용자의 id를 파싱

  4. 파싱한 id를 통해서 userDetail객체를 생성하고, 이 객체의 정보와 인가 정보를 담고 있는 UsernamePasswordAuthenticationToken 을 생성

  5. 생성한 토큰을 Spring Context에 보관.

실제 코드

기본적으로 Client→Fliter→DispatcherServlet→ Controller 순으로 동작하여 spring security가 동작합니다.

  • 먼저 securityconfig를 통해서 SecurityFilterChain을 반환하는 빈을 등록합니다

  • 여기서 addFilterBefore덕분에 모든 필터 중 우리가 설정한 JwtAuthFilter가 먼저 실행됩니다.
  • 제일 먼저 실행되는 JwtAuthFilter내의 doFilter에서는 토큰이 유효한지를 검사합니다

  • 아래는 토큰이 유효한지를 검사하는 메서드이며

  • 이 메서드를 통해서 토큰이 변조 되었는지, 만료되었는지, 등을 검증합니다.

  • 검증된 토큰을 통해서 id값을 파싱합니다.

  • 파싱한 id값,userDetailService를 통해서 user객체를 생성합니다

  • 생성한 객체를 통해서 SecurityContextHolder에 담을 UsernamePasswordAuthentication token을 생성합니다

  • 생성한 후 ContextHolder에 담슴니다.

이 정보를 SecurityContextHolder에 왜 담을까요??

 public String getPhoneNumberByToken(HttpServletRequest request){
        String phoneNumber = "";
        String authorizationHeader = request.getHeader("Authorization");
        if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
            String token = authorizationHeader.substring(7);
            if (jwtUtils.validateToken(token)) {
            ,,,중략
  • 만약 SecurityContextHolder에 해당 정보가 없다면 다음과 같이 받은 request에 대해서 다시 유효한 token인지를 검증하고, id값을 parsing하는 과정을 다시 거쳐야합니다.

  • 그러나 SecurityContextHolder 에 담으면 아래와 같이 작성이 가능합니다.

  CustomUserDetails userDetails= (CustomUserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
  • SecurityContextHolder.getContext().getAuthentication()을 호출하면 현재 인증된 사용자의 정보를 포함하는 Authentication 객체를 가져올 수 있습니다.
  • 또한 SecurityContextHolder에는 일반적으로 UserDetails 가 저장되어 있습니다
  • getPrincipal():인증된 사용자 객체를 반환합니다. 일반적으로 UserDetails 객체가 반환되며, 이를 통해 사용자 정보를 확인할 수 있습니다
(@AuthenticationPrincipal CustomUserDetails userDetails)
  • 또한 CustomUserDetails userDetails= (CustomUserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal() 대신 @AuthenticationPrincipal을 이용하면 현재 인증된 사용자의 정보를 가지고 올 수 있습니다
  • Spring Security는 Spring Mvc를 이용해서 HandlerMethodArgumentResolver를 통해서 SecurityContext에 저장된 userDetails를 주입시켜줍니다.

결론

이번 프로젝트에서 사용한 jwt방식의 구조,jwt의 원리 그리고 spring security와 관련한 기본적인 원리에 대해서 살펴보았습니다. 그러나 spring security내의 filterchain,secutirycontextholder에서 어떻게 관리 되는지,cors,csrf의 깊은 이야기는 아직 다루지 못하였습니다. 추후에 spring security에 대해서 조금더 깊은 이야기로 포스팅 하도록 하겠습니다.

profile
🐶개발 블로그

0개의 댓글