Spring/Security

[Spring] Security - JWT 토큰 인증

한비Skyla 2024. 7. 9. 20:18

JWT 시그니처에는 헤더와 페이로드를 합친 문자열을 서명한 값이 들어 있다. 

서명은 헤더의 알고리즘과 secret key 를 이용해 생성. Base64 URL - safe 로 인코딩한다.

secret key 를 포함해서 암호화가 되어있다.

 

☘️ 사전 작업 

1. <SecurityConfiguration> 1 :  보안 구성 클래스 @Configuration 

  • SecurityFilterChain : 최소한의 보안 구성. 
  • passWordEncoder. : passwordEncoder Bean 객체 생성. 
  • corsConfigurationSource : Cors 정책 적용.
    • setAllowedOrigins() : 모든 출처(Origin) 에 대해 HTTP 통신을 허용하도록 함. 
    • setAlloweddMethods () : 파라미터로 지정한 HTTP Method 에 대한 HTTP 통신 허용 
    • UrlBasedCorsConfigurationSource : 구현 클래스. 

2. 회원 가입 로직 수정

  • <MemberDto> :  password 추가. 
  • <Member> : 엔티티에 password, roles 추가. @ElementCollection (fetch = FetchType.EAGER)
  • <MemberService> : createMember 에서
    • password 단방향 암호화
    • DB에 user role 저장 : email 을 받아서 Role 저장. authorityUtils  클래스 필요. 
      • authorityUtils 
        • Role 기반의 권한 생성 List<GrantedAuthority>,  DB 를 위한 List<String>. 
        • 회원 가입 시 Email 을 받아 List<String> Role 을 만듦. >> MemberService 에서 사용
        • 만든 List<String> 을 Spring Security 를 위한 List<GrantedAuthrity> 생성.  >>MemberDetailsService 에서 사용 

☘️ 로그인 인증 구현

1. <MemberDetailsService> :  DB에서 사용자의 크리덴셜을 조회 후 AuthenticationManager 에게 전달. 

  • UserDetailsService 인터페이스를 구현, loadUserByUsername 오버라이딩. 
  • <MemberDetails> 내부 클래스 : UserDetails 구현, Member  상속
    • MembeDetails 생성자로 DB의 member 요소 set 으로 받음.  
    • getAuthorities 메서드로 권한 생성. DB의 roles 을 Authories 로 반환. 
    • getUsername 메서드로 DB의 Email을 username 으로 반환. 

2. <LoginDto> :  로그인 인증 정보 역직렬화. 

  • 역직렬화: Json 형태를 객체로 반환. security filter에서 사용 목적
  • 클라이언트의 Username, password 정보만 담음. 

3. <JwtTokenizer> : 로그인 인증에 성공하면, JWT 생성 밑 발급, 요청이 들어올 때마다 전달된 JWT 검증. 

- Jws : jwt 의 signature 

- Claims : Payload 에 있는 토큰에서 사용할 정보들의 조각.  ⭐️ 중요한 credential 정보는 담지 않는다. 

  • iss : 토큰 발급자(issuer) – Public Claims
  • sub : 토큰 제목(subject) – Public Claims
  • iat : 토큰 발급 시간(issued at) – Public Claims
  • exp : 토큰 만료 시간(expiration) – Public Claims
  • roles : 권한 – Private Cliams
  • JWT 생성 및 검증 시 사용되는 Secret Key, Access Token 만료 시간 정보, Refresh Token 만료 시간 정보. 
  • encodeBase64SecretKey() : secretKey 암호화. 
  • getKeyFromBase64EncodeKey() : base64EncodedSecretKey 복호화 >> 해시함수로 암호화. 
  • generateAccessToken (): claims 를 포함한, accesstoken. 
  • generateRefreshToken () :  RefreshToken 생성.
  • getClaims (): jwt 에서 Jws<Claims> 를 만들어서 반환. 나중에 검증할 때 사용.
  • verifySignature () : signature 검증용. 
  • getTokenExpiration() : 만료 시간 반환. 

4. <JwtAuthenticationFilter> : UsernamePasswordAuthenticationFilter 상속 

  • UsernamePasswordAuthenticationFiler : Username/Password 기반의 인증 처리 위해 확장해서 구현 가능.
  • AuthenticationManager : 의존성 주입. 로그인 인증 정보를 전달받아, 인증 여부 판단. 
  • JwtToenizer : 의존성 주입. 인증에 성공했으면 JWT 생성 및 발급. 
  • attemptAuthentication : 인증을 시도하는 로직 구현. 
    • ObjectMapper: json 형태의 Username, Password 를 LoginDto 클래스의 객체로 역직렬화. 
    • UsernamePasswordAuthenticationToken : 받은 Username, Password 로 생성해서, AutneticationManager 로 전달해서 인정 처리 위임. 
  • successfulAuthentication : AuthenticationManager내부에서 인증 성공되면 인증된 Authentication 객체 생성. Member 객체 할당
    • delegateAccessToken(member) : Access Token 생성
    • delegateRefreshToken(member) : Refresh Token 생성
      • responseheader 에 Access Token 추가, Refresh Token 추가. 

5. <SecurityConfiguration>  2 : 설정 업데이트 

  • <CustomFilerConfigurer> : JwtAuthenticationFilter 를 등록하는 역할, AbstractHttpconfigurer 상속. 
    • HttpSecurity 의 getShareObject() : AuthenticationManager 를 얻을 수 있음. 
    • JwtAuthenticationFilter 생성, AuthenticationMaager, JwtTokenizer DI 함. 
    • setFilterProcessesUrl() : URL 설정. 
    • addFilter() : Spring Security Filter Chain 추가. 
  • SecurityFilterChain 에 CustomFilterConfigurer 추가.

- AuthenticationSuccessHandler, AuthenticationFailureHandler, JwtVerificationFilter 구현 해야 함. 


☘️ 로그인 성공, 실패에 따른 추가 처리 

1. <MemberAuthenticationSuccessHandler> : AuthenticationSuccessHandler 를 구현. 

  • 로그인 인증에 성공했을 때 로그 기록. 

2. <MemberAuthenticationFailureHandler> : AuthenticationFailureHandler 를 구현. 

  • 로그인 인증 실패 시 추가 작업. 
  • onAuthenticationFailure() 추상 메서드 : Response 의 status 가 401 임을 클라이언트에 알려줄 수 있어야 함. 
    • UNAUTHORIZED (401)

등록한 이메일 주소와 패스워드를 이용해 로그인 인증을 성공적으로 수행 >>> 인증 성공에 대한 정보가 담겨져 있는 JWT 전달 받음. 


☘️ 자격 증명 및 검증 구현

- 클라이언트 측에서, 자격 증명이 필요한 리소스에 대한 request 시,

- request header 를 통해 전달받은 JWT 를 서버 측에서 검증. 

1. <JwtVerificationFilter> : request 당 한번만 실행 하므로 OncePerRequestFilter

- claims : payload 에 담겨져 있음. Map의 key, value 형태로. 

- Jws : s는 서명된(Signed) JWT, signature, ??????????

  • verifyJws() : JWT 검증 하는데 사용되는 메서드. 
    • JWT 검증 위한 Secret Key 를 얻음. 
    • JWT 에서 Claims 파싱 :: 내부적으로 서명 검증에 성공했다는 의미.
      • 이게 정상적으로 오면 서명 검증이 자연스럽게 성공했다는 뜻. verify() 매서드가 따로 있는 것이 아님. 
      • JwtTokenizer 의 getClaim() 이 Jws<Claims>를 반환을 하려며는 검증이 되어야 할 수 있음. ?????????
  • setAuthenticationToContext() : Authentication 객체를 SecurityContext 에 저장. 
    • Username: JWT 에서 받은 Claims 에서 받음. 
    • List<GrantedAuthority> : JWT 의 Claims 를 기반으로 생성. 
    • Authentication 객체 : username, List<GrantedAuthority> 포함. >>> SecurityContext 에 저장. 
      • Context 에 저장된 Authentication 객체는 Spring Security 의 세션 정책에 따라 생성할 수도 아닐수도. 
  • shouldNotFilter() : 특정 조건에 부합하면 filter 동작 수행하지 않음.  
    • request 로 받은 Authorization header 값이 Null, Bearer 로 시작하지 않으면 filter 수행하지 않도록 함.
    • JWT 가 포함되지 않았으면, 자격증명이 필요 없는 리소스라 판단. >> 다음 filter 로 건너뜀.

2. <SecurityConfiguration> 3 설정 업데이트 

  • .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS): 세션 stateless 애플리케이션 유지.
    • 단일 요청에 대한 하나의 응답.
    • 서버가 클라 상태를 보존 하지 않음. 
  • .apply(new CustomFilterConfigurer()) : <CustomFilterConfigurer> 에 JwtVerificationFilter 추가 
    • JwtAuthenticationFilter 뒤에 추가.
    • 로그인 인증에 성공한 후, 발급 받은 JWT 가 request header(Authorization)에 포함되어 있을 때에만 동작.
  • .authorizeHttpRequests: 접근 권한 설정 서버 측 리소스 역할(Role) 기반 권한 적용
    • antMatchers(HttpMethod.POST ~ ) :  회원 가입. 모든 사용자 권한 부여.
    • antMatchers(HttpMethod.PATCH ~ ) : 회원 정보 수정. USER 권한 부여.
    • antMatchers(HttpMethod.GET "/*/members" ) : 모든 회원 정보 조회. ADMIN 권한 부여.
    • antMatchers(HttpMethod.POST "/*/members/**" ) : 특정 회원 정보 조희. USER, ADMIN 권한 부여.
    • antMatchers(HttpMethod.DELETE ~ ) : 회원 탈퇴. USER 권한 부여.

☘️ 예외 처리 (JwtVerificationFilter 수정) 

  • SignautureException : JWT에 대한 서명 검증 실패 시 throw 됨. 
  • ExpiredJwtException : JWT 만료 시 throw 됨. 

'Spring > Security' 카테고리의 다른 글

@HttpServletRequest  (0) 2025.03.24
[Spring] OAuth2 인증 용어  (1) 2024.07.10