코드스테이츠_국비교육/[Section4]

66.02_[Spring Security]JWT_검증 기능 구현_22.11.24

생각없이 해도 생각보다 좋다. 2022. 11. 24. 23:48

JWT 검증 기능 구현
: HTTP Request header에 포함되어 전송되는 JWT를 request가 전송될 때마다 검증하는 기능을 구현하면 JWT 검증 기능을 구현할 수 있다.
: 현재 로그인 인증을 성공적으로 수행하면 response header의 Authorization, Refresh로 JWT를 전달 받을 수 있음.

==========================================
JWT 검증 기능 구현
==========================================
1. 검증용 Filter 구현(JwtVerificationFilter)
-OncePerRequestFilter 상속
: request 한 번당 OncePerRequestFilter를 한 번 거치기 때문에 검증 필터를 구현하기 적합함.
-JwtTokenizer DI
: JWT 검증 후, 토큰 내 정보를 추출하기 위해 DI (claims)
-CustomAuthorityUtils DI
: JWT 검증 성공 시, 권한을 부여하기 위해 DI
-doFilterInternal 메서드 구현
: OncePerRequestFilter의 메서드
: request header에 있는 JWT에서 claims를 추출하는 메서드 호출
: claims 정보를 기반으로 인증된 Authentication 객체를 만들고, 이를 SecurityContext에 저장함.
: doFilter() 를 통해서 다음 filter를 호출함.
-verifyJws 메서드 구현
: request header에 있는 JWT에서 claims를 추출하는 메서드
: request 헤더에서 "Authorization"부분의 value를 불러옴.
: value에서 "Bearer " 부분을 제거한 JWT 만 추출함.
: plain text의 SecretKey를 encoding한 secretkey로 불러옴.
: JwtTokenizer.getClaims(claim 추출)메서드 활용하여 JWT 객체형식으로 claims를 추출하고 이를 jwt.getBody() 메서드를 활용하여 Map 형태의 claims 원본을 추출함.
: 원본의 claims를 반환
-shodNotFilter() 구현
: OncePerRequestFilter의 메서드
: request header에 있는 JWT를 얻고, JWT 값이 null 이거나 "Bearer "로 시작하지 않으면 true 반환.
: 반환된 True 값은 해당 Filter를 수행하지 않고 다음 필터로 넘어가게끔 구현
: 즉, 자격 증명이 필요없는 리소스 요청에 대해 사용가능한 메서드
: 혹은 자격 증명이 필요한 리소스 요청인데 JWT가 잘못된 경우 Exception 출력

/*
SecurityContext에 Authentication을 저장하는 행위
: 클라이언트의 정보를 서버에 저장한다는 것.
: 즉, Http의 특징인 Stateless를 깨는 행위
: JWT는 Stateless하게 유지하는 방법이라고 배웠는데?
: 뒤에 내용 나옴
: 세션 정책에 따라 달라짐
: JWT를 사용할 땐, 세션이 생성되지 않도록 설정

근데 왜 굳이 저장을 할까?
AuthenticationException 예외를 이용하기 위해서.
Authentication(인증 정보)을 저장하지 않은 상태로 Security filter를 진행하다보면 발생하는 예외가 발생하고 해당 예외는 AuthenticationEntryPoint에서 처리된다.
이를 이용해서 예외 처리 로직을 구현하기 때문에 Authentication을 SecurityContext에 저장하는 단계를 구현한다.
*/
==========================================
2. SecurityConfiguration 설정 업데이트
: 세션 정책 설정 추가 =>filterChain 메서드에 내용 추가
: JwtVerificationFilter 사용 설정 추가
=>CustomFilterConfigurer.configure() 메서드에 필터 설정 내용 추가

/*
현재 완성된 단계
: 로그인 인증 완료
: 권한 증명 및 검증 완료
필요한 부분
: 권한에 따라 URI 설정
*/
==========================================
서버 측 리소스에 역할(Role) 기반 권한 적용
==========================================
1. SecurityConfiguration 설정 업데이트
: filterChain 메서드에서 각 권한마다 접근 가능한 URI(리소스 경로) 설정
: antMatchers 사용
: POST, PATCH, GET, DELETE 마다 각각 허용 가능한 접근 권한 설정

//확인
//1. JWT를 Authorization header에 포함하지 않을 경우
=> 403 에러 발생 (Forbidden, 권한 없음)
//2. 유효하지 않은 JWT를 Authorization header에 포함할 경우
=> 403 에러 발생 (수정할 부분, 권한 없음이 아닌 검증 실패(401 에러)로 수정)
//3. 권한이 부여되지 않은 리소스에 request를 전송할 경우
=> 403 에러 발생 (Forbidden, 권한 없음)
//4. 권한이 부여된 리소스에 request를 전송할 경우
=> 응답 발생

//추가 학습 : Coffee, Order 에도 적용해보기
//문제 : 멤버1이 멤버2의 정보를 조회할 수 있음. (URI 경로 설정 때문에)

==========================================
예외 처리
==========================================
// 인증, 보안은 구현 완료
// 부가적으로 더 명확한 예외를 발생시키기 위해서 예외 처리 로직 추가

1. JwtVerificationFilter에 예외 처리 로직 추가
-SignatureException
: Signature 검증 실패 시 발생하는 예외
: request의 attribute에 exception 정보를 저장
-ExpiredJwtException
: JWT가 만료되었을때 발생하는 예외
: request의 attribute에 exception 정보를 저장

==========================================
2. AuthenticationEntryPoint 구현
: SecurityContext에 Authentication이 저장되지 않을 경우 발생하는 AuthenticationException을 처리하는 핸들러
: 즉, 인증 자체가 실패된 경우의 작업을 처리하는 핸들러
: request의 attribute에 저장한 exception 정보를 사용
: 클라이언트로 Error 메세지를 전달하는 클래스 및 메서드 작성 후 호출
: Exception메세지 로그를 출력하는 클래스 및 메서드 작성 후 호출

==========================================
3. AccessDeniedHandler 구현
: 인증은 성공했으나 권한이 부여되지 않은 리소스 접근 시 발생하는 경우를 처리하는 핸들러
: 클라이언트로 Error 메세지를 전달하는 메서드 호출
: Exception메세지 로그를 출력하는 메서드 호출

==========================================
4. SecurityConfiguration 설정
: HttpSecurity의 메서드 체인 내에 설정을 추가

-HttpSecurity.authenticationEntryPoint()
: AuthenticationEntryPoint 사용 설정 추가
: 인자로 Custom AuthenticationEntryPoint 의 객체 생성자를 넣어줌
-HttpSecurity.accessDeniedHandler()
: AccessDeniedHandler 사용 설정 추가
: 인자로 Custom accessDeniedHandler 의 객체 생성자를 넣어줌