서버에서 헤더에 AccessToken과 Refresh Token을 담아 프런트로 응답을 보냈을 때 다음과 같이 Response Headers에 Set-Cookie를 보면 정상적으로 응답에 Cookie가 포함된 것을 볼 수 있다.
하지만 브라우저에는 해당 Cookie들이 저장되지 않는 문제가 있었다.
Client와 Server 모두 HTTPS를 적용하면 쿠키가 저장되지 않는 문제를 해결할 수 있다는 글을 보고 HTTPS를 적용했다.
HTTPS를 적용하기 위해 mkcert 설치를 위한 Scoop 설치(Windows의 경우)
Set-ExecutionPolicy RemoteSigned -scope CurrentUser
irm get.scoop.sh | iex
다음 명령어를 입력해 mkcert를 설치한다.
scoop bucket add extras
scoop install mkcert
다음 명령어를 입력해 로컬 루트 CA에 mkcert를 추가한다.
인증서 설치 보안 경고가 뜨는 경우 [예]를 누른다.
mkcert -install
Server - Spring boot에 HTTPS 적용
터미널에서 다음 명령어를 사용해 key를 발급 받는다.(커스텀 호스트명을 사용하는 경우 localhost 대신 입력)
mkcert -pkcs12 -p12-file keystore.p12 localhost
프로젝트 내 resources 폴더에 발급된 key를 넣는다.
application.yml 파일에 다음과 같이 코드를 추가한다.
server:
port: 443
ssl:
key-store-type: PKCS12
key-store: classpath:keystore.p12
key-store-password: changeit # default password
Client - React에 HTTPS 적용(with Vite)
다음 명령어를 입력해 mkcert 패키지를 설치한다.
npm i -D vite-plugin-mkcert
vite.config.js 내 plugins에 mkcert를 추가한다.
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import mkcert from'vite-plugin-mkcert'
export default defineConfig({
plugins: [react(), mkcert()],
})
Server로 요청을 보내는 fetch 코드 예시
credentials: "include"가 포함되어야 함
export const getJwtToken = async (accessToken) => {
return fetch(`${BASE_URI}/users/signin/kakao?accessToken=${accessToken}`, {
method: "GET",
credentials: "include",
})
.then((res) => {
if (!res.ok) {
const errorMessage = res.json();
console.log("errorMessage: ", errorMessage);
}
return res.json();
})
.catch((err) => console.error(err));
};
Client로 응답을 보내는 Kotlin 코드 예시
val refreshTokenCookie = Cookie("TODOLIST_REFRESHTOKEN", refreshToken)
refreshTokenCookie.path = "/" // 모든 경로에서, 하위 경로를 지정할 경우 해당 경로의 하위 경로에서만 접근 가능
refreshTokenCookie.maxAge = 60 * 60 * 24 * 30 // 유효기간(초)
refreshTokenCookie.secure = true // 보안 채널(HTTPS)을 통해 전송되는 경우 쿠키 전송(암호화 되지 않은 요청에 쿠키 전달 X)
refreshTokenCookie.isHttpOnly = true // 브라우저에서 쿠키 접근 X(document.cookie X), HTTP 통신으로만 접근
response.status = HttpStatus.OK.value()
response.contentType = MediaType.APPLICATION_JSON_VALUE
response.addCookie(refreshTokenCookie) // 응답 헤더에 Cookie를 포함
// accessToken은 Client LocalStorage에 저장하기 위해 응답 본문으로 보냄
jacksonObjectMapper().writeValue(response.writer, mapOf("accessToken" to accessToken))
Client에서 Refresh 요청을 GET으로 하는데 preflight 요청이 발생했는데 그 이유는
- get요청은 단순요청이라 일반적으로 preflight 요청이 가지 않음
- Authorization 같은 사용자정의 헤더가 포함되거나 Credentials 설정이 포함된 경우 preflight 요청이 발생
'TIL(Today I Learned)' 카테고리의 다른 글
TIL - Spring Security (1) | 2024.06.11 |
---|---|
TIL - Kotest 테스트 코드 작성하기 (0) | 2024.06.10 |
TIL - React에서 useState를 사용할 때 호출이 여러 번 발생 (0) | 2024.06.05 |
Spring AOP(Aspect-Oriented Programming) (1) | 2024.06.04 |
TIL - React, Spring Boot로 카카오 소셜 로그인 구현 STEP 3 (0) | 2024.06.03 |