
홈페이지를 사용할 때, 대부분의 서비스를 이용하기 위해서는 우리는 로그인이라는 인증 과정을 거치게 된다.
같은 도메인에서 페이지를 이동하는데 어떻게 나의 정보가 계속 따라다닐 수 있을까? 해당 섹션에서 웹 토큰 기반 인증 방식을 이해하고 직접 구현해 보도록 하자
JWT는 Header, Payload, Signature로 구성되고, .으로 구분한다.
토큰의 구조는 다음과 같다.
xxxxx.yyyyy.zzzzz
실제 예시:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.kK9JnTXZwzNo3BYNXJT57PGLnQk-Xyu7IBhRWFmc4C0
헤더에는 다음과 같은 정보가 있다.
Encoded:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
Decoded:
{
"alg": "HS256",
"typ": "JWT"
}
페이로드에는 우리가 사용할 데이터를 담는 공간이며, Key-Value 형식의 JSON타입으로 정의되어 있다.
Encoded:
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
Decoded:
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
페이로드에는 Claim(토큰에 저장되는 메타데이터)이 존재한다.
Registered Claim Names:
미리 지정된 클레임이다.
Public Claim Names:
공개 사용자 클레임으로, 직접 페이로드에 등록한 데이터이다.
예를 들어, email, name, age 등이 있을 수 있다.
Private Claim Names:
비공개 사용자 클레임이다.
서명(Signature)은 인코딩된 헤더와 페이로드, 비밀 키, 위에서 지정한 알고리즘로 암호화 하여 만든다.
Encoded:
kK9JnTXZwzNo3BYNXJT57PGLnQk-Xyu7IBhRWFmc4C0
Decoded:
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret-key
)
토큰 인증 방식으로 Access Token과 Refresh Token이 존재한다. JWT 를 통해 생성한 두 개의 토큰으로 인증 절차를 거치게 된다.
암호화된 토큰이다. 해당 엑세스 토큰으로 사용자를 인증하는데 사용한다. 보안을 위해 짧은 유효기간(1시간, 1일, 7일 등)을 가진다.
엑세스 토큰을 재생성 할때 사용되는 토큰이다. 액세스 토큰보다 만료시간이 더 길다. (7일, 한달 또는 그이상)
보안 향상
토큰은 Authorization 헤더에 Bearer 스키마를 사용하여 서버에 전송한다.
Authorization: Bearer <token>
Bearer이라는 키워드는 토큰의 타입을 나타내는 것이다. 간단하게 인증에 사용되는 토큰을 표시한 것이라고 생각하자.
토큰은 오랜 기간 보관해서는 안된다. 보안을 위해 짧은 시간 뒤, 파기하고 자주 재발급 받는 방식이다.
패키지 설치
npm install jsonwebtoken
JWT 암호화
함수:
jwt.sign(payload, secretOrPrivateKey, [options, callback])
jwt.sign함수를 사용하여 웹 토큰을 발급시킨다. 함수의 인자에는 다음과 같은 데이터가 들어간다.
사용법:
const ACCESS_SECRET_KEY = 'abmkcpwenf'
const accessToken = jwt.sign({
name: 'jm'
}, ACCESS_SECRET_KEY, {
expiresIn: '1m',
issuer: 'access issuer'
})
JWT Decoded
함수:
jwt.verify(token, secretOrPublicKey, [options, callback])
jwt.verify 함수로 토큰을 복호화 시킨다. 함수의 인자에는 다음과 같은 데이터가 들어간다.
사용법:
const ACCESS_SECRET_KEY = 'abmkcpwenf'
const decoded = jwt.verify(token, ACCESS_SECRET_KEY, (err,decoded) => {
console.log(decoded); // { name: 'jm' }
});
npm 문서에서 세 번째 인자 옵션과 콜백 함수에 대해 자세히 알아볼 수 있다.
전체코드는 nodejs의 express 환경에서 jwt를 구현한 것이다. 오로지 이해를 위해서 단순하게 작성되었기 때문에 Authorization 헤더, 미들 웨어 사용이 없는 코드이다. 이것에 대해서는 뒤에서 설명할 것이다.
로그인 단계
인증 및 인가 단계
※ 참고
!) 인증은 빈번하게 발생하며 홈페이지 이동, 버튼과의 상호작용 등에 주로 일어난다.
!) 토큰을 전송할 때는 Authorization 헤더에 붙여 Bearer 키워드와 함께 전송한다.
보안을 위해 토큰은 Authorization 헤더에 담아 전송한다. 해당 섹션에서 인증 헤더에 토큰을 담아 전송 및 수신 하는 코드를 알아 보자.
Usage in Client
import axios from 'axios'
await axios.post(serverUrl, body, {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
Usage in Server
Express 미들웨어 방식으로 동작한다면 next() 함수가 사용된다.
const auth = (req, res, next) => {
const token = req.headers.authorization.split("Bearer ")[1];
jwt.verify(token, "secret_key", (error) => {
if (error) {
res.status(401).json({ error: "Auth Error" });
} else {
next();
}
});
};
//route.js
app.get('/userInfo', auth, fetchUserInfo) //example
잘못된 정보나 새로운 정보는 댓글에 알려주시면 감사합니다. 😐