728x90
이 글은 스프링에서 JWT,쿠키를 이용해 OAuth 인증을 구현하던 도중 아래 인용 글의 이론에 살을 붙여 코드를 작성하게 된 글입니다.
인용
https://prolog.techcourse.co.kr/studylogs/2272
JWT란
모바일이나 웹에서 클라이언트와 서버 간 통신시, 사용자의 인증을 위해 사용하는 암호화된 토큰을 의미한다. 즉, JWT를 도입하여 액세스 토큰 및 리프레시 토큰을 사용해 토큰 유효성 검사를 하여 사용자를 인증할 수 있다.
JWT 정보는 주로 통신 시, 헤더의 Authorization에 담아 전송되면 이것을 서버에서 검증하여 사용자의 정보 열람, 권한 부여 등의 인증/인가 작업을 수행할 수 있는 것이다.
JWT의 안전한 저장 위치 - 쿠키
옵션 없이 기본적으로 작동되는 쿠키는 XSS, CSRF 공격에 모두 취약하다. 하지만, 백엔드와의 협업을 통해 쿠키에 httpOnly, secure, SameSite 옵션을 사용하면 쿠키는 자바스크립트 코드 상에서 접근이 불가능해지고, HTTP 요청에만 포함되어 보내진다.
아래는 서버에서 보내는 응답 헤더이고, 이를 통해 쿠키 설정값이 정해진다.
Set-Cookie: 쿠키명=쿠키값; path=/; secure; SameSite=Lax
쿠키란?
쿠키란 사용자가 어떤 웹 사이트를 방문했을 때, 웹 사이트가 사용하는 서버에서 사용자의 로컬 환경에 저장하는 작은 데이터를 말한다. 이 값이 있기때문에 이전에 방문한적이 있는지 알 수 있고, 이전에 로그인을 했다면 로그인 정보도 알 수 있는 것이다. 쿠키는 키와 값으로 이루어져 있어 만료기간, 도메인 등의 정보를 가지고 있다.
JWT 핸들링하기
- 백엔드 -> 로그인시 인증 서버로부터 access token, refresh token을 받아온다.
- 프론트 -> access token은 메모리에 저장한다.
// 파라미터로 받은 토큰이 있다면, 토큰을 로컬 스토리지로 저장한다.
const token = searchParam('token')
if (token) {
localStorage.setItem("access_token", token)
}
function searchParam(key){
return new URLSearchParams(location.search).get(key)
}
- 백엔드 -> refresh token은 쿠키에 저장하고, 보안옵션을 지정한다.
- 프론트 -> 권한이 필요한 요청시 Authorization 헤더에 access token을 보내준다.
fetch(url, {
method: method,
headers: {
// 로컬 스토리지에서 액세스 토큰 값을 가져와 헤더에 추가한다.
Authorization: "Bearer " + localStorage.getItem("access_token"),
"Content-Type": "application/json"
},
body: body
})
- 백엔드 & 프론트 -> access token 이 만료되었을시, 서버 렌더링 과정 혹은 API 통신을 통해 재발급을 요청한다.
// HTTP 요청을 보내는 함수
function httpRequest(method, url, body, success, fail){
fetch(url, {
method: method,
headers: {
// 로컬 스토리지에서 액세스 토큰 값을 가져와 헤더에 추가한다.
Authorization: "Bearer " + localStorage.getItem("access_token"),
"Content-Type": "application/json"
},
body: body
}).then((response) => {
if (response.status === 200 || response.status == 201){
return success()
}
const refresh_token = getCookie("refresh_token")
// access_token 이 만료되어 권한이 없고, 리프레시 토큰이 있다면 그 리프레시 토큰을 이용해서 새로운 access token 을 요청
if (response.status === 401 && refresh_token) {
fetch("/api/token", {
method: "POST",
headers: {
Authorization: "Bearer " + localStorage.getItem("access_token"),
"Content-Type": "application/json"
},
body: JSON.stringify({
refresh_token: getCookie("refresh_token")
})
}).then((res) => {
if (res.ok){
return res.json()
}
}).then((result) => {
// refresh token 재발급에 성공하면 로컬 스토리지 값을 새로운 access token 으로 교체
localStorage.setItem("access_token", result.accessToken)
// 새로운 access token 으로 http 요청을 보낸다.
httpRequest(method, url, body, success, fail)
})
}
else {
return fail()
}
}).
}
- 백엔드 -> refresh token이 만료되었을 때, DB와 다시 한번 통신하여 갱신 혹은 로그아웃 상태로 렌더링해준다.
728x90
'Tech > JWT' 카테고리의 다른 글
JWT(JSON Web Token) - parse, signWith Deprecated 업데이트 버전 (0) | 2023.06.29 |
---|