위키백과 ver )
인터넷 사용자들이 비밀번호를 직접 제공하지 않고 다른 웹사이트 상의 자신
들의 정보에 대해 웹사이트나 어플리케이션의 접근 **권한을 **부여할 수 있는 공
통적인 수단으로서 사용되는, 접근 위임을 위한 개방형 표준이다.
즉, 서드파티 어플리케이션이 사용자의 계정에 접근할 수 있는 권한을 부여하
기 위한 프로토콜을 말한다.
- OAuth 1.0
- 서드파티 어플리케이션이 사용자의 데이터에 접근할 권한을 얻기 위해 서명된 요청을 사용한다.
- 요청의 서명 부분은 어플리케이션의 비밀 키와 사용자의 토큰을 조합하여 생성되며, 이를 통해 보안이 유지된다.
- OAuth 2.0
- OAuth 2.0은 보다 간편하고 확장성이 있는 버전으로, 기본적으로 인증과 권한을 분리하여 다루는 것이 특징이다.
- Access Token을 통해 권한을 부여하고, 사용자의 실제 비밀 정보를 공유하지 않는다.
JWT(JSON Web Token) 형식의 토큰


- JWT 구성 자세한 설명
“alg” : “HS256”
// JWT 서명 알고리즘, HMAC SHA-256을 사용하여 서명 생성.
“typ” : “JWT”
// 토큰 유형이 JWT 임을 명시.
"sub" : “~”
// "sub"(subject) 클레임으로, JWT의 주체를 나타냄. (사용자 ID)
“name” : “~~”
// "name" 클레임으로, 사용자의 이름을 나타냄.
"iat" : ~~
// iat"(Issued At) 클레임으로, JMT가 발급된 시간을 타임스탬프 형식으로 나타냄.
HMACSHA256(...) :
// HMAC SHA-256 알고리즘을 사용하여 서명 생성 과정
base64UrlEncode(header) ~ “.”~(payload) :
// 헤더와 페이로드를 Base64 URI 인코딩 한 후 결합
your-256-bit-secret:
// 서명 생성 시 사용되는 비밀 키. 서버에서만 알고 있어야 함.

sign : 서버측에서 JWT를 생성할 때 사용
jwt.sign(payload, secretOrPrivateKey, [options, callback])
verify : 클라이언트나 서버에서 받은 JWT의 유효성을 검증할 때 사용
jwt.verify(payload, secretOrPrivateKey, [options, callback])
EX )
// JWT 생성
const token = jwt.sign(payload, secretKey, { expiresIn: '1h' });
// JWT 검증
jwt.verify(token, secretKey, (err, decoded) => {
if (err) {
console.log('Token is invalid or expired');
} else {
console.log('Decoded payload:', decoded);
}
});
npm install jsonwebtoken
이 명령어로 jsonwebtoken을 설치한다.
const jwt = require("jsonwebtoken");
사용하고자 하는 jsonwebtoken을 불러온다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>JWT</title>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>
<body>
<h1>JWT 실습</h1>
<div id="info"></div>
<script>
(async function () {
const token = localStorage.getItem("token");
const info = document.querySelector("#info");
let html;
if (!token) {
html = '<a href="/login">로그인</a>';
} else {
const { data } = await axios({
method: "POST",
url: "/verify",
headers: {
Authorization: `Bearer ${token}`,
},
});
if (data.result) {
html = `<p>${data.name}님 환영합니다!</p>
<button>로그아웃</button>
`;
}
}
info.innerHTML = html;
})();
function logout() {
localStorage.clear();
document.location.reload();
}
</script>
</body>
</html>
localStorage에서 token을 가져온다./verify POST 요청을 보내 인증을 확인한다.localStorage를 초기화하고 페이지를 새로고침한다.<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>login</title>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>
<body>
<h1>로그인</h1>
<form name="login-form">
<input type="text" id="id" placeholder="ID" /><br />
<input type="password" id="pw" placeholder="PW" /><br />
<button type="button" onclick="login()">로그인</button>
</form>
<script>
async function login() {
const form = document.forms["login-form"];
const { data } = await axios({
method: "POST",
url: "/login",
data: {
id: form.id.value,
pw: form.pw.value,
},
});
if (data.result) {
localStorage.setItem("token", data.token);
document.location.href = "/";
} else {
alert(data.message);
document.location.reload();
}
}
</script>
</body>
</html>
login 함수를 호출하여 서버에 /login POST 요청을 보낸다.localStorage에 JWT를 저장하고 홈 페이지로 리디렉션한다.const express = require("express");
const jwt = require("jsonwebtoken");
const app = express();
const PORT = 8000;
const SECRET = "9PBYbnIhfXEVQdeXrvPWrX6ydDAJkIqV";
const userInfo = { id: "banana", pw: "1234", name: "홍길동", age: 20 };
app.set("view engine", "ejs");
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.get("/", (req, res) => {
res.render("index");
});
app.get("/login", (req, res) => {
res.render("login");
});
app.post("/login", (req, res) => {
try {
const { id, pw } = req.body;
const { id: uId, pw: uPw } = userInfo;
if (id === uId && pw === uPw) {
// 토큰 생성
const token = jwt.sign({ id }, SECRET);
console.log(token);
res.json({ result: true, token });
} else {
res.json({ result: false, message: "로그인 정보가 올바르지 않습니다." });
}
} catch (error) {
console.log(error);
}
});
app.post("/verify", (req, res) => {
if (req.headers.authorization) {
const headers = req.headers.authorization;
console.log(headers);
const [bearer, token] = headers.split(" "); // ['Bearer', 'token']
try {
const result = jwt.verify(token, SECRET);
console.log(result); // { id: 'banana', iat: 1730078415 }
if (result.id === userInfo.id) {
res.json({ result: true, name: userInfo.name });
} else {
return res.status(403).json({ result: false, message: "검증 실패" });
}
} catch (error) {
console.log(error);
return res.status(403).json({ result: false, message: "검증 실패" });
}
} else {
res.redirect("/login");
}
});
app.listen(PORT, () => {
console.log(`http://localhost:${PORT}`);
});
GET /와 GET /login 엔드포인트를 통해 각각 메인 페이지와 로그인 페이지를 렌더링한다.POST /login 엔드포인트는 사용자가 제공한 ID와 비밀번호를 확인하여 JWT를 생성한다.POST /verify 엔드포인트는 클라이언트가 보내온 JWT를 검증한다.


🔍 공부하면서 어려웠던 부분
토큰 분리: authorization 헤더의 값을 공백으로 나누어 bearer와 token 두 개의 변수에 할당한다. bearer는 "Bearer" 문자열이고, token은 실제 JWT이다.
headers 변수: 이 변수는 req.headers.authorization의 값이다.
일반적으로 JWT를 포함하는 Authorization 헤더는 다음과 같은 형식이다 :
Authorization: Bearer <token>
Bearer 9PBYbnIhfXEVQdeXrvPWrX6ydDAJkIqV
split(" ") 메서드: split(" ")는 문자열을 공백(" ")을 기준으로 나누는 메서드이다.
위를 기준으로 설명하면 :
비구조화 할당:
⬇️ 위 코드를 실행 했을때 ⬇️
