너무 오랜만인듯...
정처기 실기와 방통대 과제를 끝내고 돌아왔습니다.
그동안 갠프젝을 안했던건 아닌데 상대적으로 투자할 시간이 확 줄어서ㅠㅠ
이제 정처기가 끝났으니 그동안 못했던 기록들을 천천히 기록해보겠습니다.
JWT를 이용하는 방식을 다시 한 번 정리하자면
(1) 유저가 로그인할 때
(2) 서버가 인증 정보를 보내주는데, 암호화나 시그니처 추가가 가능한 데이터 패키지안에 인증 정보(accessToken과 refreshToken)를 담아 보내줍니다.
(3) accessToken과 refreshToken이 이후 유저 인증에 사용되는데
(4) 이 정보를 클라이언트에 저장해둡니다.
(5) 이 accessToken을 유저에게만 보여줄 수 있는 정보에 접근할 때 서버에 보내면
(6) 서버는 그 토큰이 유효한지 확인하는 방식으로 인증한다.
클라이언트에서 처리하기
먼저, React 최상단 index.js에서 axios에 withCredentials를 true로 설정해줘야 refreshToken cookie를 주고받을 수 있다고 합니다.
⇒ 저의 경우 App.js에서 설정해주었습니다.
axios.defaults.withCredentials = true;
Login.js
import React, { useState } from "react";
import '../css/Login.css';
import Nav2 from "../components/Nav2";
import { NavLink, useNavigate } from "react-router-dom";
import axios from "axios";
import { useCookies } from "react-cookie";
let Login = () => {
let [cookies, setCookies] = useCookies(['accessToken']);
// 아이디와 비밀번호
let [userId, setUserId] = useState("");
let [password, setPassword] = useState("");
let navigate = useNavigate(); // useNavigate 훅 사용
let onUserIdHandler = (e) => {
setUserId(e.target.value)
console.log("userId: ", e.target.value);
}
let onPasswordHandler = (e) => {
setPassword(e.target.value)
console.log("password: ", e.target.value);
}
// 로그인 함수
let loginHandler = () => {
// 로그인 요청 보내기
axios({
url: 'http://localhost:8082/api/v1/auth/n/login',
method: 'POST',
data: {
userId: userId,
password: password
}
})
.then((res) => {
// 로그인 성공
console.log(res.data)
if (res.status === 200) {
// 쿠키에 사용자 정보 저장
// setCookies('user', { userId, password });
// 서버로부터 받은 액세스 토큰을 쿠키에 저장
setCookies('accessToken', res.data.accessToken);
// API 요청하는 콜마다 헤더에 accessToken 담아 보내도록 설정
axios.defaults.headers.common['Authorization'] = `Bearer ${res.data.accessToken}`;
alert("로그인 성공!");
navigate("/"); // 메인페이지로 이동
} else {
// 로그인 실패
alert("아이디 또는 비밀번호가 잘못되었습니다.");
}
})
.catch((error) => {
// 서버 오류 또는 네트워크 오류
alert("로그인에 실패하였습니다.");
console.error("Error:", error);
});
}
return (
<div>
<div>
<Nav2/>
</div>
<div className="loginForm">
<div>
<h1 id='login_title'>로그인</h1>
</div>
<div>
<div className="input">
<input type="text" className="userId" id="userId" placeholder="아이디" value={userId} onChange={onUserIdHandler} autoFocus></input>
<input type="password" className="password" id="password" placeholder="비밀번호" value={password} onChange={onPasswordHandler}></input>
<button onClick={loginHandler}>Login</button>
</div>
<div className="link">
<NavLink to="/findId">아이디 찾기</NavLink>
<span> | </span>
<NavLink to="/findPw">비밀번호 찾기</NavLink>
<span> | </span>
<NavLink to="/register">회원가입</NavLink>
</div>
</div>
</div>
</div>
);
}
export default Login;
- import { useCookies } from "react-cookie";
React 애플리케이션에서 상태(state)나 효과(effect)를 관리하기 위해 React Hook을 사용하는 것처럼, useCookies Hook을 사용하여 쿠키와 관련된 상태를 관리할 수 있습니다.
- let [cookies, setCookies] = useCookies(['accessToken']);
useCookies Hook을 사용하여 쿠키를 읽어옵니다. - setCookies('accessToken', res.data.accessToken);
setCookies 함수를 사용하여 쿠키를 설정.
결과
클라이언트에서 리프레쉬토큰을 잘 받아온 것을 확인할 수 있습니다.
아래의 스프링 UserService 사진 처럼 쿠키의 HttpOnly 속성이 true로 설정되면 JavaScript가 해당 쿠키에 접근할 수 없으며, 이는 쿠키가 XSS(크로스 사이트 스크립팅) 공격으로부터 안전하다는 것을 의미합니다.
이 경우, 리프레시 토큰이 쿠키에 저장되어 있고 HttpOnly 속성이 true로 설정되어 있으므로 JavaScript 코드에서 해당 토큰에 직접 액세스할 수 없습니다. 일반적인 보안 상의 이유로 이렇게 설정했습니다.
'Project 댕린이집' 카테고리의 다른 글
[사진첩] 저장경로와 파일명 지정 (0) | 2024.05.08 |
---|---|
[사진첩] 파일 업로드 (0) | 2024.05.06 |
[Spring Security+JWT] JWT는 어디에 저장해야할까? localStorage vs cookie (0) | 2024.04.16 |
[로그인] 로그인 기능 구현 (0) | 2024.04.11 |
[트러블슈팅] org.springframework.security.authentication.InternalAuthenticationServiceException (0) | 2024.04.08 |