본문 바로가기
STUDY/NodeJS

[인증/권한] 클라이언트와 서버 간의 인증 JWT(JSON Web Token)

by Y.Choi 2024. 3. 28.
728x90
반응형

 

 

공식 페이지 https://jwt.io/

 

JWT(JSON Web Token)는 주로 인증(Authentication) 및 권한(Authorization)에 관련된 기술로 사용된다.

정보를 안전하게 전송하기 위한 인터넷 표준으로 주로 클라이언트와 서버 간의 인증을 위해 사용된다. JSON으로 구성된 가볍고 자가수용적인 토큰이며, 사용자에 대한 정보를 담고 있고, 전자 서명되어 있어서 정보가 변경되지 않았음을 검증할 수 있다.

 


인증(Authentication)

사용자의 신원을 확인하고 인증하는 데 사용된다. 사용자가 로그인한 후에 서버로부터 발급된 토큰을 클라이언트에게 전달하고, 이 토큰을 이용하여 사용자의 인증 상태를 유지한다. 클라이언트는 이 토큰을 포함하여 서버에 요청을 보내며, 서버는 토큰을 검증하여 사용자를 인증한다.

권한(Authorization)

사용자가 특정 자원 또는 기능에 접근할 수 있는 권한을 부여하는 데 사용된다. 토큰에 사용자의 권한 정보를 포함할 수 있으며, 서버는 이 정보를 이용하여 사용자가 특정 요청을 수행할 권한이 있는지를 확인하고 이를 통해 특정 자원이나 기능에 대한 접근 제어가 가능하다.

 

 

- 구성 - 

Header(헤더)

토큰의 유형과 해싱 알고리즘 등의 메타데이터를 포함한다. 예를 들어, 토큰의 유형은 "JWT"이며 해싱 알고리즘은 HMAC SHA256 또는 RSA를 사용할 수 있다.

Payload(내용)

토큰에 포함될 클레임(claim) 정보를 담고 있다. 클레임은 사용자에 대한 정보를 나타내며, 세 가지 유형이 있다.


- 등록된(Claimed) 클레임 

토큰에 대한 정보를 담고 있지만, 서비스나 클라이언트가 필요에 따라 선택적으로 사용한다.

토큰에 특정한 의미를 부여하기 위해 이미 정의된 것으로 토큰의 발급자, 만료 시간 등이 있다.

 

iss : 토큰 발급자 (Issuer)
sub : 토큰 주제 (Subject)
exp : 토큰 만료 시간 (Expiration Time)
iat : 토큰 발급 시간 (Issued At)
aud : 토큰 대상자 (Audience)


- 공개(Public) 클레임

표준화되지 않은 클레임 이름을 사용하여 충돌을 피하기 위한 것으로, 사용자가 직접 정의하여 사용할 수 있는 클레임들이다. 토큰의 발급자와 사용자 사이의 정보 교환에 사용된다. 예를 들어, 사용자의 이름, 이메일 주소 등이 있다.


- 비공개(Private) 클레임 

서버와 클라이언트 간에 사전에 정의된 클레임을 사용하여 정보를 교환하기 위한 클레임이다. 토큰을 발급받는 시스템 내부에서만 의미가 있다.

 

Signature(서명)

헤더, 페이로드 및 비밀키를 이용하여 생성된 서명으로, 토큰이 변조되지 않았음을 검증한다. 이를 통해 클라이언트는 토큰이 유효하고 신뢰할 수 있는지를 확인할 수 있다.

 

jwt.io 출처

 

- 특징 -

자가수용적(Self-contained)

JWT는 필요한 모든 정보를 자체적으로 포함하고 있기 때문에 데이터베이스 조회 없이도 사용자를 인증할 수 있다.

 

간단함(Simplicity)

토큰은 간단한 구조로 되어 있으며, HTTP 요청의 헤더 또는 URL 매개변수에 넣을 수 있다.


확장 가능성(Scalability)

토큰은 클레임을 통해 추가 정보를 저장할 수 있으며, 필요에 따라 쉽게 확장할 수 있다.

 

보안(Security)

서명된 토큰은 변조가 불가능하며, 페이로드를 해독하는 키를 가지고 있는 클라이언트만 토큰을 읽을 수 있다.

 

 

| 서버 측(node.js)

const jwt = require('jsonwebtoken');
const secretKey = 'your-secret-key';

// 로그인 시 JWT 생성
app.post('/login', (req, res) => {
  // 사용자 인증 처리 후
  const userId = 'user-id'; // 예시로 사용자 ID 설정
  const token = jwt.sign({ userId }, secretKey, { expiresIn: '1h' });
  res.json({ token });
});

// 클라이언트 요청에 대한 인증 검사
app.get('/protected-route', (req, res) => {
  const token = req.headers.authorization.split(' ')[1];
  jwt.verify(token, secretKey, (err, decoded) => {
    if (err) {
      return res.status(401).json({ message: '인증되지 않은 사용자입니다.' });
    } else {
      req.userId = decoded.userId;
      next();
    }
  });
});

 

/login 엔드포인트에서 사용자가 제공한 로그인 정보를 받아와서 사용자를 인증한다.


사용자가 인증되면, 서버는 사용자의 ID를 포함한 JWT(JSON Web Token)를 생성한다.


JWT는 jsonwebtoken 패키지를 사용하여 생성되며, 토큰에는 사용자의 ID와 만료 시간이 포함된다.


클라이언트에게 JWT가 반환되고, 클라이언트는 이를 로컬 스토리지나 쿠키에 저장하여 인증된 사용자임을 유지한다.


/protected-route 엔드포인트에서 클라이언트가 보호된 리소스에 액세스하는 경우, 클라이언트는 저장된 JWT를 서버로 전송한다.


서버는 클라이언트가 전송한 JWT를 검증하고, 유효한 경우 클라이언트에게 요청한 리소스를 제공한다.


JWT가 만료되었거나 유효하지 않은 경우, 서버는 클라이언트에게 인증 오류를 반환한다.

 

 

| 클라이언트 측 (React)

// 로그인 컴포넌트
const handleLogin = async () => {
  const response = await fetch('/login', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ username, password }),
  });
  const data = await response.json();
  localStorage.setItem('token', data.token);
};

// 보호된 라우트에서 JWT를 사용하여 요청 보내기
const fetchData = async () => {
  const token = localStorage.getItem('token');
  const response = await fetch('/protected-route', {
    headers: { Authorization: `Bearer ${token}` },
  });
  const data = await response.json();
  console.log(data);
};

 

로그인 컴포넌트에서 사용자가 로그인 양식을 제출하면, 클라이언트는 /login 엔드포인트로 사용자의 로그인 정보를 전송한다.


서버에서 반환된 JWT는 클라이언트에게 저장되고, 보호된 리소스에 액세스할 때마다 헤더에 포함되어 서버로 전송한다.


보호된 라우트에서 클라이언트는 저장된 JWT를 사용하여 서버에 요청을 보낸다.


서버가 반환하는 응답은 클라이언트에서 처리되어 사용자에게 적절한 UI를 제공하게 된다.

 

728x90
반응형