Java/Spring Boot
[Java] Spring Boot Security 3.x + JWT 이해하기 -1 : 구조 및 Client / Server 처리과정
adjh54
2024. 10. 6. 14:02
반응형
해당 환경에서는 Spring Boot Security 3.x 기준으로 JWT와 함께 이해하고 처리되는 과정에 대해 알아봅니다.
💡[참고] Spring Security 관련 글 및 Github Repository 경로입니다. 참고하시면 도움이 됩니다.
분류 | 상세 분류 | 주제 | 링크 |
Spring Boot 2.x | 이론 | Spring Boot Security 이해하기 -1 : 2.7.x 버전 구조 및 파일 이해 | https://adjh54.tistory.com/91 |
Spring Boot 2.x | 환경 설정 | Spring Boot Security 이해하기 -2 : 2.7.x 버전 구현하기 | https://adjh54.tistory.com/92 |
Spring Boot 2.x | 이론 | Spring Boot Security 이해하기 -3: JWT(JSON Web Token) 이해하기 | https://adjh54.tistory.com/93 |
Spring Boot 2.x | 환경 설정 | Spring Boot Security 이해하기 -4: JWT 환경 설정 및 구성 하기 | https://adjh54.tistory.com/94 |
Spring Boot 2.x | 소스코드 | Spring Boot Security 2.x + JWT 기반 구성 Github Repository | https://github.com/adjh54ir/blog-codes/tree/main/spring-boot2-security |
기타 | 이론 | Spring Boot 2.x.x 버전 프로젝트 생성: 지원 종료 및 다운그레이드 | https://adjh54.tistory.com/361 |
Spring Boot 3.x | 이론 | Spring Boot Security 3.x + JWT 이해하기 -1 : 구조 및 Client / Server 처리과정 | https://adjh54.tistory.com/576 |
Spring Boot 3.x | 환경 설정 | Spring Boot Security 3.x + JWT 이해하기 -2 : 환경 설정 및 구성 | https://adjh54.tistory.com/577 |
Spring Boot 3.x | 환경 설정 | Spring Boot Security 3.x + JWT 이해하기 -3 : Refresh Token 활용 | https://adjh54.tistory.com/583 |
Spring Boot 3.x | 환경 설정 | Spring Boot Security 3.x + JWT 이해하기 -4 : 로그아웃 + 블랙리스트 활용 | https://adjh54.tistory.com/592 |
Spring Boot 3.x | 소스코드 | Spring Boot Security 3.x + JWT 기반 구성 Github Repository | https://github.com/adjh54ir/blog-codes/tree/main/spring-boot3-security |
React | 소스코드 | Spring Boot 3.x와 통신하여 로그인을 수행하는 클라이언트 구성 Github Repository | https://github.com/adjh54ir/blog-codes/tree/main/react-login |
1) Spring Boot Security
💡Spring Boot Security
- Spring 기반 애플리케이션 내에서 보안을 담당하는 프레임워크를 의미합니다. 이는 사용자에 대한 ‘인증’과 ‘인가(권한부여)’에 대한 처리를 담당합니다.
분류 | 설명 |
인증(Authentication) | - 사용자는 자신을 입증할 수 있는 ‘정보’를 시스템에 제공하며, 시스템은 사용자에 대한 정보를 ‘검증’하여 시스템을 이용할 수 있는 사용자 인지에 대해 확인을 하는 과정을 의미합니다. |
인가(Authorization) | - 애플리케이션에서 보호된 자원(메서드 접근 혹은 요청에 대한 자원)에 대해서 접근을 허가하거나 거부하는 기능을 의미합니다. |
💡[참고] 상세한 Spring Boot Seucrity에 대해 알고 싶으시면 아래의 글을 참고하시면 도움이 됩니다.
1. Spring Boot Security 인증과 인가
💡Spring Boot Security 인증과 인가
- 해당 과정에서는 클라이언트에서 로그인 정보를 전달하면, 데이터베이스에서 이를 조회하여 ‘인증’을 수행합니다.
- 그리고 이 인증이 완료된 경우, JWT 토큰을 발급하여 인증이 된 사용자로 ‘인가(권한부여)’를 할 예정입니다.
2. 주요 특징
특징 | 설명 |
인증(Authentication) | 사용자의 신원을 확인합니다. 폼 로그인, HTTP 기본 인증, JWT 등 다양한 인증 방식을 지원합니다. |
인가(Authorization) | 인증된 사용자의 리소스 접근 권한을 확인합니다. URL 기반, 메소드 기반 등 다양한 접근 제어를 제공합니다. |
보안 위협 방어 | CSRF, XSS 등 다양한 웹 보안 위협에 대한 기본적인 보호 기능을 제공합니다. |
세션 관리 | 사용자 세션을 안전하게 관리합니다. 동시 세션 제어, 세션 고정 보호 등의 기능을 제공합니다. |
암호화 | 비밀번호 등 중요 정보를 안전하게 저장하기 위한 암호화 기능을 제공합니다. |
통합성 | Spring 생태계와 완벽하게 통합됩니다. 다른 Spring 프로젝트들과 쉽게 연동할 수 있습니다. |
2) JWT(JSON Web Token)
💡JWT(JSON Web Token)
- ‘사용자 인증’을 위해 ‘인증’에 필요한 정보를 토큰에 담아서 암호화를 하여 사용하는 인터넷 표준 인증방식을 의미합니다.
- 가볍고 자가 수용적인(self-contained) 방식으로 정보를 안전성 있게 전달해 줍니다.
[ 더 알아보기 ]
💡 자가수용적인 (self-contained) 방식
- JWT 내에 필요한 정보를 토큰 내에 자체적으로 지니고 있음을 의미합니다.
- 이는 JWT에서 발급된 토큰은 기본 정보, 전달할 정보, 검증에 대한 모든 Signature도 포함하고 있으며, 웹 서버 환경에서는 HTTP 헤더에 포함을 시키거나 URL의 파라미터로 전달이 가능합니다.
1. JWT 이용방법
💡JWT 이용방법
- 사용자의 인증을 통과한 경우에 이에 대한 인가(권한부여)를 위해서 JWT를 발급해 줍니다.
- 이 발급받은 JWT를 클라이언트에서는 HTTP/HTTPS 통신 시 Header에 ‘Authorization’ 키와 ‘Bearer JWT’ 형태의 값 형태로 전달을 하여 유효한 토큰일 경우 JWT를 통해 로그인된 사용자로 ‘인가(권한부여)’를 받을 수 있습니다.
2. JWT 구조
💡JWT 구조
- Token의 구조는 아래의 이미지와 같이 점(.)을 기반으로 구분되어 세 부분으로 구성이 됩니다.(header.payload.signature) 이 세 부분은 암호화로 Base64Url로 인코딩 되어 있어서 읽기 어려운 형태로 변환이 됩니다.
- 특히 Signature 부분은 비밀 키를 사용하여 생성되므로, 토큰의 위조나 변조를 방지할 수 있습니다.
- 단 Header와 Payload 부분은 단순히 인코딩만 되어 있을 뿐, 암호화되지 않았다는 점입니다. 따라서 중요한 정보는 Payload에 포함시키지 않는 것이 좋습니다.
구성 요소 | 설명 |
헤더 (Header) | 토큰의 타입과 서명(Signature)에서 사용할 알고리즘을 포함합니다. |
페이로드 (Payload) | 인증에 필요한 실제 정보들(클레임)을 담고 있습니다. |
서명 (Signature) | 인코딩된 헤더와 페이로드, 비밀 키, 그리고 헤더에 명시된 알고리즘을 사용하여 생성됩니다. |
💡[참고] Header, Payload, Signature는 아래와 같은 형태로 구성이 되어 있습니다.
// [Header] typ : 토큰의 타입 / alg : 토큰의 서명에서 사용할 암호화 알고리즘 종류
{
"typ": "JWT",
"alg": "HS256"
}
// [payload] 등록 클레임, 공개 클레임, 비공개 클레임
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
// [Signature] 서명(Signature) = 인코딩 헤더 + 인코딩 비밀키 + 비밀 키 + 헤더의 알고리즘
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret
)
💡[참고] 상세 구조에 대해 궁금하시면 아래의 글을 참고하시면 도움이 됩니다.
💡[참고] JWT 실제 값 예시
- 아래와 같은 형태의 JWT 형태의 구조를 가지고 있습니다.
- 또한, 디버깅을 목적으로 공식 사이트에서 인코딩 된 JWT를 디코딩하여서 볼 수도 있습니다.
Authorization: Bearer
eyJyZWdEYXRlIjoxNzI3NzY5NDcwNTU3LCJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.
eyJ1c2VyTm0iOiJsZWUiLCJ1c2VySWQiOiJhZGpoNTQiLCJzdWIiOiIwIiwiZXhwIjoxNzI3Nzk4MjcwfQ.
wdfzdWNQU04aV9b18o23FCtYEtFgd1xSxEhxYVHSFBgBWLd1CfIcF1_cyDbjBS_F0VntE4Lv0kIyKzly8bRiwA
[더 알아보기]
💡 공식 사이트에서 JWT만 알면 디코딩을 할 수 있는데 이거 위험한 거 아닌가?
- 네, 맞습니다. JWT의 Header와 Payload 부분은 단순히 Base64Url로 인코딩 되어 있을 뿐이며, 실제로 암호화되지 않았습니다. 이는 보안상의 우려를 일으킬 수 있는 부분입니다.
- 그러나 이것이 반드시 위험하다고 볼 수는 없습니다. JWT의 보안은 주로 Signature 부분에 의존합니다. Signature는 비밀 키를 사용하여 생성되므로, 토큰의 위조나 변조를 방지할 수 있습니다.
- 그럼에도 불구하고, 중요한 정보는 Payload에 포함시키지 않는 것이 좋습니다. 예를 들어, 비밀번호나 민감한 개인정보 등은 JWT의 Payload에 포함해서는 안 됩니다.
- 공식 사이트에서 JWT를 디코딩할 수 있다는 것은 단순히 JWT의 구조를 이해하고 디버깅을 돕기 위한 도구일 뿐입니다. 실제 애플리케이션에서는 서버 측에서 비밀 키를 사용하여 Signature를 검증함으로써 토큰의 무결성을 확인합니다.
3. 인증 타입 : Bearer Token
💡 인증 타입 : Bearer Token
- 토큰을 소지한 사람(bearer)에게 접근 권한을 부여하는 인증 방식을 의미합니다.
- HTTP 요청의 Authorization 헤더에 "Bearer" 키워드와 함께 토큰을 포함하여 사용이 됩니다.
- 해당 인증 타입은 구현이 간단하고, 서버 측에서 세션 상태를 유지할 필요가 없어서 확장성이 좋습니다.
(즉, API 통신 시마다 Header에 Token을 담아서 전송하기에 서버 측에서 세션 상태를 유지하지 않습니다)
반응형
3) Spring Security + JWT 처리과정 : Client 관점
💡 Spring Security + JWT 처리과정: Client 관점
1. Client는 로그인을 수행합니다.
- 사용자 아이디, 비밀번호를 기준으로 로그인을 수행합니다.
2. Client는 API Server로 API 호출 시 사용자 정보를 함께 전달하여 ‘인증’을 요청합니다 : 인증(Authentication)
- 사용자 아이디, 비밀번호를 기준으로 API Server로 호출을 하면 데이터베이스를 조회하여 실제 인증이 된 회원인지 여부를 확인합니다.
3. API Server에서 클라이언트에게 인증 결과 값을 반환해 줍니다.
- 데이터베이스 조회 결과를 통해서, 회원 여부에 따라 맞는 응답 결과를 반환해 줍니다.
4. 로그인 성공 시 '인가(권한 부여)'로 JWT를 발급하며, 조회된 사용자 정보를 응답 값으로 전달합니다.
- 로그인 성공 시 JWT와 사용자 정보를 함께 반환해 주고, 실패인 경우에는 에러 코드 및 메시지를 전달하며, 다시 로그인을 요청합니다.
5. 클라이언트는 전달받은 JWT 내부 스토리지에 저장합니다.
- 발급받은 JWT를 유지하며, 이후 API 호출마다 이를 Header에 포함하여 전달하기 위해 저장해 둡니다.
6. 클라이언트가 API Server로 API 통신 시 JWT를 Header에 추가하여 호출합니다.
- 이를 통해 인가받은 사용자만 API 통신을 수행할 수 있음을 확인합니다.
1. 로그인
💡로그인
- 클라이언트는 아이디와 비밀번호를 입력하고 ‘로그인’ 버튼을 누르면 입력한 아이디, 비밀번호를 기반을 API 통신을 수행합니다.
- 아래에서는 간단한 코드를 통해서 흐름에 대해서만 확인해 봅니다.
💡클라이언트 측 API 통신
- 아래와 같이 http://localhost:8080/api/v1/user/login이라는 엔드포인트로 API를 호출하며 해당 값을 전달합니다.
💡Spring Boot API 서버 내 코드 : Spring Security 구성 -1
- 추후 Spring Security 내에서 해당 Endpoint를 지정하여서 로그인을 위한 URL로 지정을 하였습니다.
💡Spring Boot API 서버 내 코드 : Spring Security 구성 -2
- 해당 로그인의 인증을 위해서 아이디/비밀번호를 확인하는 곳은 authenticationManage() 메서드 내에서 이를 수행합니다.
2. 로그인 성공여부 : 로그인 실패
💡로그인 성공여부 : 로그인 실패
- 로그인 실패를 하는 경우 resultCode : 9999라는 값을 통해서 오류 메시지와 함께 응답이 되도록 처리하였습니다.
💡Spring Boot API 서버 내 코드 : Spring Security 구성 -1
- 해당 로그인 실패에 대한 처리는 customLoginFailureHandler에서 처리가 됩니다.
💡Spring Boot API 서버 내 코드 : Spring Security 구성 -2
- 아래의 customLoginFailureHandler 내에서는 실패에 대한 응답 값을 구성해서 반환해 줍니다.
3. 로그인 성공여부 : 로그인 성공
💡로그인 성공여부 : 로그인 성공
- 해당 값을 기반으로 Spring Security가 처리되어서, 최종적으로는 로그인이 완료되어 클라이언트에게 응답 값으로 사용자 정보와 JWT를 발급해 줍니다.
💡Spring Boot API 서버 내 코드 : Spring Security 구성 -1
- 해당 로그인 실패에 대한 처리는 customLoginSuccessHandler에서 처리가 됩니다.
💡Spring Boot API 서버 내 코드 : Spring Security 구성 -2
- 아래의 customLoginSuccessHandler 내에서는 성공에 대한 응답 값(사용자 정보, JWT)을 구성해서 반환해 줍니다.
4) Spring Security + JWT 처리과정 : API Server 관점
💡Spring Security + JWT 처리과정 : API Server
- 클라이언트의 호출 이후 내부적으로 처리되는 API Server의 과정에 대해 확인합니다.
1. 클라이언트의 API 호출 : 엔드포인트(api/v1/user/login)
- 클라이언트는 사용자 아이디, 비밀번호를 기반으로 API 호출을 수행합니다.
2. 서버 내에서 해당 엔드포인트에 대한 감지 하여 이를 처리합니다. : CustomAuthenticationFilter
- CustomAuthenticationFilter 내에서 사전에 지정한 엔드포인트를 통해서 수행처리 합니다.
3. 감지 이후 Filter에서 우선적으로 감지하여 해당 엔드포인트로 호출되는 정보를 조회합니다. : JwtAuthorizationFilter
- 3.1. JWT가 필요가 없는 URL인 경우는 다음 필터를 수행하도록 처리가 됩니다.
- 3.2. JWT가 필요한 경우는 JWT에 대한 존재 및 유효성을 검증합니다.
4. 사용자 아이디/비밀번호를 감지하여 전달합니다.: CustomAuthenticationFilter
- CustomAuthenticationFilter 내에서 사용자 아이디/비밀번호 값을 CustomAuthenticationProvider로 전달을 합니다.
5. 전달받은 값을 통해 데이터베이스 내의 사용자 정보를 조회하여 사용자 여부를 확인합니다. : CustomAuthenticationProvider
- 데이터베이스를 호출하여 사용자를 조회하여 실제 사용자 여부인지 여부를 성공(CustomAuthSuccessHandler)과 실패(CustomAuthFailureHandler)로 전달을 합니다.
6. 이전과정에서 사용자 여부가 비교되어서 상황에 따른 처리를 수행합니다
- 6.1. 성공 시, 조회된 사용자 정보와 토큰을 발급하여 클라이언트에게 전달합니다.
- 6.2. 실패 시, 클라이언트에게 오류 코드와 오류 메시지를 전달합니다.
오늘도 감사합니다. 😀
반응형