2025. 2. 21. 16:48ㆍ프론트엔드
상황
S3까지 모두 세팅하고 이제 CreatePetInfo 컴포넌트에서 등록하기 버튼을 눌렀는데 스프링부트에서 Missing or invalid Authorization header에러가 나타났다. 딱봐도 헤더의 토큰에 뭔가 문제가 있는 상황.
정확한 확인을 위해 프론트에서 console.log("Authorization 쿠키 값: ", authCookie); 로 확인을 해봤지만 undefined가 나왔다.
아니 쿠키를 확인해보면 잘 들어가 있는데 왜 뭐가 문제인 건가요,,
- 백엔드(스프링부트) 코드
// 로그인
@PostMapping("/login")
public ResponseEntity<ResponseMessage> login(@RequestBody UserCommonDto userCommonDto, HttpServletResponse response) throws BadRequestException {
String token = userService.login(userCommonDto);
// 쿠키 생성
// String base64Token = Base64.getEncoder().encodeToString(("Bearer " + token).getBytes());
Cookie cookie = new Cookie("Authorization", token);
cookie.setHttpOnly(true);
cookie.setSecure(false); // HTTPS를 사용하는 경우에만 설정
cookie.setPath("/");
cookie.setMaxAge(3600); // 쿠키 유효 시간 설정 (1시간)
// 응답에 쿠키 추가
response.addCookie(cookie);
ResponseMessage responseMessage = ResponseMessage.builder()
.data(token) // 토큰 다시 준거
.statusCode(200)
.resultMessage("Login successful")
.build();
return ResponseEntity.ok(responseMessage);
}
먼저 코드를 보자면 백엔드에서 로그인할 때 이렇게 쿠키에 토큰을 넣어줬다.
- 프론트(리액트) 코드
import axios from 'axios';
import Cookies from "js-cookie";
const createHeaders = () => {
const authCookie = Cookies.get("Authorization"); // 쿠키에서 토큰 가져오기
return authCookie ? {
"Content-Type": "application/json",
"Authorization": `Bearer ${authCookie}`,
} : { "Content-Type": "application/json" }
};
const petApiClient = axios.create({
baseURL: 'http://localhost:8080/api/v1/pet',
withCredentials: true, // 모든 요청에서 쿠키 자동 포함
headers: createHeaders(),
});
export default petApiClient;
그리고 프론트에서 withCredentials: true 을 사용해서 쿠키가 헤더에 자동으로 붙어서 오게 했고, Cookies.get("Authorization")을 사용해서 잘 가져왔다고 생각했는데 어디가 문제인걸까 하핫😀 (힘들때 웃는편,,)
과정
1. CORS allowedOrigins 대신 allowedOriginPatterns 사용 - 실패 ❌
@Configuration
public class CorsConfig {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") // 모든 경로에 대해
// .allowedOrigins("http://localhost:5173") // 변경 전
.allowedOriginPatterns("*") // 프론트엔드 주소 허용
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") // 허용할 HTTP 메서드
.allowedHeaders("*") // 모든 헤더 허용
.allowCredentials(true); // 인증 정보 포함 허용
}
};
}
}
백엔드의 CORS 설정에 문제가 있나 싶어 확인해봤다. Spring Boot 2.4 이후부터 allowedOrigins 설정은 와일드카드를 사용할 수 없어 여러 도메인을 지원하려면 allowedOriginPatterns("*") 을 사용해야 해야한다고 한다. 그래서 바꿔주었지만 소용없었다.
2. 백엔드에서 SameSite=None 속성 추가 - 실패 ❌
@PostMapping("/login")
public ResponseEntity<ResponseMessage> login(@RequestBody UserCommonDto userCommonDto, HttpServletResponse response) throws BadRequestException {
String token = userService.login(userCommonDto);
Cookie cookie = new Cookie("Authorization", token);
cookie.setHttpOnly(true);
cookie.setSecure(false);
cookie.setPath("/");
cookie.setMaxAge(3600);
// SameSite 설정 추가
cookie.setAttribute("SameSite", "None");
response.addCookie(cookie);
ResponseMessage responseMessage = ResponseMessage.builder()
.data(token)
.statusCode(200)
.resultMessage("Login successful")
.build();
return ResponseEntity.ok(responseMessage);
}
브라우저의 보안 정책이 강화되면서 쿠키를 withCredentials: true로 전송하려면 SameSite=None; 속성을 추가해야 해야한다고 한다. 바로 넣어주자.
참고로 SameSite 속성은 서로 다른 도메인간의 쿠키 전송에 대한 보안을 설정하는 것이다. TMI로 이것저것 글을 읽다 CSRF 문제라는 것도 알게 됐는데 굉장히 흥미롭다 나중에 공부해 봐야겠다!
하지만 이 방법도 실패했다.
3. allowCredentials, exposeHeaders 추가 - 실패 ❌
@Configuration
public class CorsConfig {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("*")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.exposedHeaders("Authorization") // 추가!
.allowedHeaders("*")
.allowCredentials(true); // 추가! 인증 정보 포함 허용
}
};
}
}
백엔드와 프론트에 모두 allowCredentials 속성을 추가해줘야 한다고 해서 추가해주었다. 하지만 실패 했다.
해결
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setError("");
try {
const data = await loginUser(email, password);
Cookies.set("Authorization", data.data); // 추가! 쿠키 저장
console.log(data.data);
navigate("/createPetInfo");
} catch (err) {
setError("로그인에 실패했습니다.");
console.log(err);
}
};
해결이라기 보다는 다른 방법이라고 하는게 더 맞을 것 같다,,
백엔드에서 토큰을 쿠키에 넣어줬었는데 프론트에서 로그인할 때 넣어주는 걸로 변경했더니 성공했다.
이런식으로 찝찝하게 해결하고 싶진 않았는데 🥲
저 세가지 방법 외에도 여러가지 자잘하게 시도해봤지만 모두 실패였다. 내가 생각하기에는 cookie.setHttpOnly(true); 때문인것 같은데 false로 바꿔도 똑같았다. 하지만 여전히 의심되는 부분..
거의 하루동안 끙끙 앓았지만 시간상 넘어가기로 했다. 나중에 꼬옥 다시 고쳐보는 걸로,,~
'프론트엔드' 카테고리의 다른 글
반려동물 목록의 이미지와 이름이 표시되지 않는 오류 해결 (0) | 2025.02.27 |
---|---|
Vite + React + TypeScript 프로젝트에 테일윈드(Tailwind) 설치하는 방법 (0) | 2025.02.11 |
자바스크립트를 타입스크립트로 전환하기 (1) | 2025.02.10 |