Spring-React jwt 쿠키전달하기

hanana·2024년 2월 20일
0
post-custom-banner

토큰을 reGenerate할 때
accessToken과 refreshToken을 함께 전달하여
서버에서 특정 작업 후 새로운 accessToken을 전달하는 작업이 필요하다.

이전 포스팅을 통해서
refreshToken을 localStorage애서 쿠키로 저장장소를 변경하였고,
이에따라 해당 api를 처리하는 과정에서 변경이 필요해졌다.

react

function cookieTest() {
    api.get('/hello/jwt')
    .then(
        response => {
            console.log(response.data);
        }
    ).catch(
        error => {
            console.log("ERROR : " + JSON.stringify(error))
        }
    )
}

Spring (포스팅에서는 서버에서 쿠키를 제대로 가져올 수 있는지 까지만 다룬다)

@GetMapping("/hello/jwt")
public String helloJwt (
    HttpServletRequest request,
    HttpServletResponse response
){
    Cookie[] cookies = request.getCookies();
    if(cookies!=null){
        for (Cookie c : cookies) {
            String name = c.getName(); // 쿠키 이름 가져오기
            String value = c.getValue(); // 쿠키 값 가져오기
            System.out.println("name = " + name);
            System.out.println("value = " + value);
            System.out.println("--");
        }
    }
    System.out.println("Controller - called");
    return "hello jwt";
}

1. CORS 설정 하기

CrossOriginResourceSharing의 약어로
요약하면 서로 다른 출처의 자원(쿠키)를 사용할 것인지에 대한 설정이다.

SecurityConfig

@Bean
public CorsConfigurationSource corsConfigurationSource() {
    CorsConfiguration configuration = new CorsConfiguration();
    configuration.addAllowedOriginPattern("*");
    configuration.setAllowedOrigins(List.of("https://localhost:8080", "http://localhost"));

    configuration.addAllowedOrigin("http://localhost:3000");
    configuration.addAllowedOrigin("http://localhost:3001");
    configuration.addAllowedOrigin("http://localhost:3002");
    configuration.addAllowedOrigin("http://127.0.0.1:3002");
    configuration.addAllowedOrigin("http://127.0.0.1:3002/");

    configuration.addAllowedHeader("*");
    configuration.addAllowedMethod("*");
    configuration.setAllowCredentials(true);


    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", configuration);
    return source;
}

의도대로 동작하지 않아서 이것저것 추가하느라 복잡하긴한데 핵심은 아래 두가지 속성이다.

setAllowCredentials(ture)
다른 출처의 자원의 활용을 허용하겠다. 라는 설정을 true로 해준다.

configuration.addAllowedOrigin("domain");
자원을 사용할 출처를 지정해준다. 이때 중요한점은
위 setAllowCredentials에 true 값을 주게되면 더이상 와일드카드 * 을 사용하지 못한다.
'설정을 열어준만큼 보안에 더 신경써라.' 라는 의미로 받아들이면 될 것 같다.

axios.create

const api = axios.create(
  {
    baseURL: process.env.REACT_APP_BASE_URL,
    withCredentials: true, // 서로다른 자원을 사용하겠다는 의미
  }
);

프론트엔드 api 요청부에 withCredentials: true 설정을 주어
다른 출처의 자원을 사용할 수 있게 열어준다.

로그인시 토큰생성

public TokenInfo generateToken(Authentication authentication, HttpServletResponse response) {
    // 권한 가져오기
    String accessToken = getAccessToken(authentication);
    // Refresh Token 생성
    String refreshToken = getRefreshToken(authentication.getName());

    ResponseCookie responseCookie = generateRefreshTokenCookie(refreshToken);
    response.addHeader(HttpHeaders.SET_COOKIE, responseCookie.toString());
    return TokenInfo.builder()
            .grantType("Bearer")
            .accessToken(accessToken)
            .build();
}



private static ResponseCookie generateRefreshTokenCookie(String refreshToken) {
    return ResponseCookie.from("refresh", refreshToken)
            .httpOnly(true)
            .secure(true)
            .path("/")
            .sameSite(Cookie.SameSite.NONE.attributeValue())
            .build();
}

조금 신경써야할게 많다.

1. Cookie 객체를 생성해서 reponse.addCookie가 아닌, ResponseCookie 객체를 이용하여
response 헤더에 해당 쿠키를 넣어준다.

2. sameSite() 속성에 NONE이 들어가게 해준다.
서로다른 사이트여도 쿠키를 사용할 수 있게 만들어주는 속성이다.
3. seucre() 속성을 true로 설정해준다.
sameSite() 속성에 NONE을 사용하게 되면, 보안상의 이유로 secure 설정을 함께 true로 설정해주어야 쿠키를 정상적으로 사용할 수 있게 된다.
일단 최소한의 설정을 하고 한단계씩 밟아가는 과정에서 한참 고생했던 부분이다..()


결과

로그인

로그인시 응답헤더

응답쿠키에도 정상적으로 값이 들어와있다.

마지막으로 브라우저에도 저장이 되어있는 모습을 확인해본다.

서버에서 쿠키 전달 확인

정상적으로 토큰 정보가 서버로 넘어오는지 확인한다.


끝으로 쿠키 삭제하기

쿠키를 생성할때와 마찬가지로 단순히 Cookie객체를 생성하고 expired 시켜주는 것이 아니고
ResponseCookie 객체를 생성하여 제거한다.
모든 정보는 생성시 정보와 동일하게 작성해야한다.(httpOnly 속성등을 포함하여)

ResponseCookie deleteCookie = ResponseCookie.from("refresh", "")
        .domain("127.0.0.1")
        .path("/")
        .httpOnly(true)
        .secure(true)
        .sameSite(Cookie.SameSite.NONE.attributeValue())
        .maxAge(0)
        .build();
response.addHeader("Set-Cookie", deleteCookie.toString());

참고
https://okky.kr/questions/1371485

profile
성숙해지려고 노력하지 않으면 성숙하기까지 매우 많은 시간이 걸린다.
post-custom-banner

0개의 댓글