본 시리즈는 작성자의 이해와 경험을 바탕으로 실습 위주의 설명을 기반으로 작성되었습니다.
실습 위주의 이해를 목표로 하기 때문에 다소 과장이 많고 생략된 부분이 많을 수 있습니다.
따라서, 이론적으로 미흡한 부분이 있을 수 있는 점에 대해 유의하시기 바랍니다.
또한, 본 시리즈는 ChatGPT의 도움을 받아 작성되었습니다.
수 차례의 질문을 통해 도출된 여러가지 다양한 방식의 코드를 종합하여
작성자의 이해와 경험을 바탕으로 가장 정석으로 생각되는 코드를 재정립하였습니다.
What is JSON Web Token?
JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA.
Although JWTs can be encrypted to also provide secrecy between parties, we will focus on signed tokens. Signed tokens can verify the integrity of the claims contained within it, while encrypted tokens hide those claims from other parties. When tokens are signed using public/private key pairs, the signature also certifies that only the party holding the private key is the one that signed it.
[Reference] https://jwt.io/introduction
JSON 웹 토큰이란 무엇입니까?
JWT(JSON Web Token)는 당사자 간에 정보를 JSON 개체로 안전하게 전송하기 위한 간결하고 독립적인 방법을 정의하는 개방형 표준(RFC 7519)입니다. 이 정보는 디지털 서명되어 있으므로 확인하고 신뢰할 수 있습니다. JWT는 비밀(HMAC 알고리즘 포함) 또는 RSA 또는 ECDSA를 사용하는 공개/개인 키 쌍을 사용하여 서명할 수 있습니다.
JWT를 암호화하여 당사자 간에 비밀성을 제공할 수도 있지만 여기서는 서명된 토큰에 중점을 둘 것입니다. 서명된 토큰은 그 안에 포함된 클레임의 무결성을 확인할 수 있는 반면 암호화된 토큰은 다른 당사자로부터 해당 클레임을 숨깁니다. 공개/개인 키 쌍을 사용하여 토큰에 서명할 때 서명은 개인 키를 보유한 당사자만이 서명한 당사자임을 인증합니다.
JWT(JSON Web Token)는 개방형 웹 표준 RFC 7519로 지정된
JSON 객체 형태의 Web Token으로 Token 자체에 정보를 저장하는 인증 방식입니다.
OAuth, SAML(Security Assertion Markup Language), OpenID Connect 등과 함께
대표적으로 사용되는 인증 방식으로 다른 인증 방식에 비해 가볍고 간편하다는 장점이 있습니다.
JWT는
Header
,Payload
,Signature
로 구성됩니다.
Header
: Signature를 해싱하기 위한 알고리즘 정보Payload
: 서버와 클라이언트가 서로 주고받을 실질적인 정보Signature
: Token의 유효성 검증을 위한 문자열예를 들어 다음과 같이 Base64 URL Safe Encoding
을 사용하여
Token을 암호화하고 Token 속에 정보를 포함하여 전송하는 것입니다.
JWT Base64 URL Decode 결과
{
"header": {
"alg":"HS512"
}
"payload": {
"sub": "test2"
"userId": 3
"userName": "test2"
"userEmail": "test2@test.com"
"iat": 1681801541
"exp": 1681887941
}
"signature": 1BwC=ͼq4�TY<5WWK/\Qd\_ٙ4rfRfoo
}
장점
단점
실습을 위해 대략적인 프로젝트의 구조와
build.gradle
의dependency
를 확인합니다.
com.example
├── config
│ ├── JwtTokenFilter.java
│ ├── JwtTokenProvider.java
│ └── WebSecurityConfig.java
│ └── CustomUserDetails.java
│ └── CustomUserDetailsService.java
├── domain
│ ├── Auth.java
│ └── User.java
├── dto
│ ├── AuthRequestDTO.java
│ ├── AuthResponseDTO.java
│ ├── UserRequestDTO.java
│ └── UserResponseDTO.java
├── repository
│ ├── AuthRepository.java
│ └── UserRepository.java
├── service
│ ├── AuthService.java
│ └── UserService.java
└── controller
├── AuthController.java
└── UserController.java
위 프로젝트 구조는 MVC 패턴에 기반한 프로젝트 구조입니다.
기본적인 구조는 위와 같고, 필요에 따라 package 구조를 변경하셔도 좋습니다.
CustomUserDetails
,CustomUserDetailsService
는
원래는domain
,service
에 위치해야 하지만,
편의상config
에 위치시켰습니다.
build.gradle
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-security' // 추가
implementation 'org.springframework.boot:spring-boot-starter-websocket'
implementation 'io.jsonwebtoken:jjwt:0.9.1' //추가
implementation 'mysql:mysql-connector-java'
implementation 'org.projectlombok:lombok:1.18.26'
annotationProcessor 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test' //추가
}
dependencies
를 추가한 후에는refresh
하는 것을 잊지 않도록 합니다.
IntelliJ
에서는Reload All Gradle Projects
에 해당합니다.