이제까지 테스트를 통해서 검증했지만 그래도 클라이언트를 붙여봐야 웬지 동작이 잘 이해될것 같아서 화면을 만들고 동작을 테스트 해보자.
기억해줄 내용은 로그인 성공 후 @ResponseBody를 통해서 서버는 클라이언트에게 정보를 내려보내주고 클라이언트는 서버로 정보 요청 시 header에 jwt-auth-token이라는 이름으로 토큰을 전달해준다는 점이다.
클라이언트는 전달받은 토큰을 session storage에 저장해두자.
클라이언트 작성
화면 구성
간단한 화면 구성이고 ajax를 사용하기 위해서는 axios가 사용 되었다.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Jwt Test</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>
<body>
<div class="container">
<h1 class="my-5 ">jwt test</h1>
<div class="d-flex">
<input class="form-control" type="text" placeholder="email" id="email">
<input class="form-control" type="text" placeholder="pass" id="pass">
<input class="btn btn-primary" type="button" value="로그인" id="btn-login">
<input class="btn btn-primary" type="button" value="로그아웃" id="btn-logout">
<input class="btn btn-primary" type="button" value="정보조회" id="btn-info">
</div>
<hr>
<div>
<div class="mb-3">
<label for="token" class="form-label">토큰</label>
<textarea id="token" rows="3" class="form-control" readonly></textarea>
</div>
<div class="mb-3">
<label for="expire" class="form-label">유효기간</label>
<input type="text" class="form-control" id="expire" readonly="readonly">
</div>
<div class="mb-3">
<label for="status" class="form-label">상태</label>
<input type="text" class="form-control" id="status" readonly="readonly">
</div>
<div class="mb-3">
<label for="info" class="form-label">정보</label>
<input type="text" class="form-control" id="info" readonly="readonly">
</div>
</div>
</div>
</body>
이제 javascript 영역에서 이벤트를 처리하면서 ajax 요청을 처리해보자.
javascript 처리
먼저 화면의 요소들을 설정하기 위한 메서드이다.
let setInfo = function (token, expire, status, info) {
document.querySelector("#token").innerHTML =
token == null ? document.querySelector("#token").innerHTML : token;
// 전달된 시간은 min 단위
document.querySelector("#expire").value =
expire == null ? document.querySelector("#expire").value : new Date(expire * 1000);
document.querySelector("#status").value =
status == null ? document.querySelector("#status").value : status;
document.querySelector("#info").value =
info == null ? document.querySelector("#info").value : info;
}
다음은 전달받은 토큰과 사용자 정보를 저장하기 위한 함수이다. 전술했듯이 sessionStorage에 저장해두자. 그리고 sessionStorage에 jwt-auth-token 값이 있을 경우에는 화면에 반영해 주었다.
let setStorage = function (authToken, email) {
sessionStorage.setItem("jwt-auth-token", authToken);
sessionStorage.setItem("email", email);
}
document.querySelector("#token").innerHTML = sessionStorage.getItem("jwt-auth-token")
다음은 btn-login이 클릭되었을 때의 동작 처리이다.
document.querySelector("#btn-login").addEventListener("click", function () {
setInfo("", "", "", "");
setStorage("", "");
(async () => {
let res = "";
try {
res = await axios.post("/api/user/login", {
"email": document.querySelector("#email").value,
"pass": document.querySelector("#pass").value
});
setInfo(res.data["jwt-auth-token"], res.data.exp, "로그인 성공", "");
setStorage(res.data["jwt-auth-token"], res.data.user);
} catch (error) {
setInfo("", "", "로그인 실패", error.response.data.message);
}
})();
})
로그인에 성공하면 성공적으로 토큰 정보를 확인할 수 있다.
이제 정보를 조회하는 btn-info의 동작이다. 주의할 부분은 header에 jwt-auth-token을 전달하는 내용이다.
document.querySelector("#btn-info").addEventListener("click", function () {
setInfo(null, null, "", "");
(async () => {
let res;
try {
res = await axios.get("/api/info", {
headers: {
"jwt-auth-token": sessionStorage.getItem("jwt-auth-token")
}
});
}
catch (error) {
document.querySelector("#status").value += ">정보 조회 실패"
setInfo(null, null, "정보 조회 실패", error.response.data)
}
if (res) {
setInfo(null, null, ">정보 조회 성공", res.data.info)
}
})();
});
버튼을 클릭하면 서버로부터 전달받은 "정보"가 잘 출력되는 것을 볼 수 있다.
만약 유효기간이 지난 후 요청해보면 아래와 같이 오류가 표현된다.
마지막으로 logout 해보자. 사실 jwt token을 이용한 인증은 서버에 특별한 정보를 저장하지 않기 때문에 서버에서 처리할 일은 없다. 그냥 클라이언트의 storage에서 정보만 초기화 시키면 될듯하다.
document.querySelector("#btn-logout").addEventListener("click", function () {
setInfo("", "", "로그아웃 성공", "")
setStorage("", "");
})