본문 바로가기
부트캠프 개발일기/Spring Security

66일차: Spring Security에 JWT 적용

by shyun00 2023. 5. 17.

1. 의존 라이브러리 추가

JWT 를 적용하기 위해서는 build.gradle 설정파일에 Spring Security Srarter와 jjwt 라이브러리를 추가해주어야한다.

dependencies {
...

  // Spring Security Starter 추가
    implementation 'org.springframework.boot:spring-boot-starter-security'

  // JWT 기능을 위한 jjwt 라이브러리
	implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
	runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
	runtimeOnly	'io.jsonwebtoken:jjwt-jackson:0.11.5'
}

2. Spring Security를 이용해 보안 구성(설정파일 작성)

@Configuration을 사용해 보안 관련 설정 파일을 작성한다.

리턴 타입이 SecurityFilterChain, 메서드 파라미터가 HttpSecurity 타입인 메서드를 작성해 Bean으로 등록한다.

이때 CORS(Cross-Origin Resource Sharing)설정을 해줄 수 있다.

CORS는 출처(Origin)가 다른 스크립트 기반 HTTP 통신을 하더라도 선택적으로 리소스에 접근할 수 있는 권한을 부여하도록 하는 정책이다.특정 Origin에서 오는 요청을 허용하거나 특정 메서드만 허용하는 등의 설정을 추가할 수 있다.

 

회원가입을 구현할때 주의해야할점으로는 입력받은 패스워드를 문자 그대로 저장하지 않는 것이다.

입력된 패스워드는 암호화를 통해 인코딩 한 후 저장하도록 한다.

3. JWT 자격 증명을 위한 로그인 인증 구현

사용자의 Username과 Password가 로그인 인증에 성공하면 로그인 인증에 성공한 사용자에게 JWT를 생성 및 발급한다.

해당 과정은 다음과 같이 진행된다.

  1. 클라이언트가 서버측에 로그인 요청 (Username, Password 전송)
  2. 로그인 인증 담당 Security Filter(JwtAuthenticationFilter)가 요청 인증정보 수신
  3. Security Filter가 수신한 정보를 AuthenticationManager에게 위임
  4. AuthenticationManager가 Custom UserDetailsService에게 사용자 UserDetails 조회 위임 (Manager가 대신 처리함)
  5. Custom UserDetailsService가 사용자 크리덴셜을  DB에서 조회하고 AuthenticationManager에게 해당 UserDetails 전달
  6. AuthenticationManager가 로그인 인증정보와 UserDetails를 비교해서 인증 처리 (Manager가 대신 처리함)
  7. JWT 생성 후 클라이언트에게 응답으로 전달

로그인 인증 성공 및 실패에 따른 추가 처리도 가능하다.

로그인 인증 성공시 AuthenticationSuccessHandler를 구현하여 인증 성공시 수행될 내용을 작성할 수 있다.

로그인 인증 실패시 AuthenticationFailureHandler를 구현하여 인증 실패시 수행될 내용을 작성할 수 있다.

4. JWT를 이용한 자격 증명 및 검증 구현

로그인 인증이 성공적으로 수행되면 클라이언트는 JWT를 전달받는다.

해당 JWT를 사용해 자격 증명이 필요한 리소스에 대한 Request를 전송했을 때, 서버측에서는 JWT를 검증을 통해 적절한 권한이 있는지 확인한다.

  1. JWT를 검증하는 전용 Security Filter를 구현한다.
  2. SecurityConfiguration 설정에  Jwt 를 검증하는 필터 (1번에서 작성한 필터)를 추가하고, 세션 정책 설정을 추가한다.

5. 예외 처리 로직 추가

검증 과정에서 JWT에 대한 서명 검증에 실패하면 던져지는 SignatureException과 JWT가 만료되었을 경우 발생하는 ExpiredJwtException을 처리한다. 검증 과정에서 예외가 발생하면 Security Context에 클라이언트 인증정보가 저장되지 않는다.

최종적으로 AuthenticationException이 발생하게되며 AuthenticationException은 AuthenticationEntryPoint가 처리한다.

위에서 구현한 Jwt 검증용 필터에 예외 처리 로직을 추가해서 구현할 수 있다.

  1. try~catch문을 사용해서 발생한 예외를 HttpServletRequest의 Attribute로 추가한다.
    ex. request.setAttribute("exception", Exception 객체)
  2. AuthenticationEntryPoint 구현: 처리하고자 하는 로직을 commence() 메서드에 구현한다. (1~2는 인증 실패에 대한 조치)
  3. AccessDeniedHandler 구현: 인증에는 성공했지만 해당 리소스 권한이 없을때 호출되는 핸들러를 구현한다.
  4. SecurityConfiguration 설정에 AuthenticationEntryPoint와 AccessDeniedHandler 추가

Spring Security부분으로 넘어오고나서부터 내용이 어려워져서 이해하기가 어려웠다.

이번에도 여러번 반복해서 학습자료를 보고 실습도 다시 해보고 있는데 확실히 볼때마다 조금씩은 이해가 되는게 느껴진다.

강사님께서 앞으로 남은 과정은 이것보다 10배는 더 할게 많다고 하시는데 (...🫠) 일단 시간 나는대로 계속해서 반복 학습을 해야겠다.