Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
package com.example.solidconnection.auth.controller;

import com.example.solidconnection.auth.controller.config.RefreshTokenCookieProperties;
import com.example.solidconnection.auth.domain.TokenType;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseCookie;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
public class RefreshTokenCookieManager {

private static final String COOKIE_NAME = "refreshToken";
private static final String PATH = "/";
private static final String SAME_SITE = "Strict";

private final RefreshTokenCookieProperties properties;

public void setCookie(HttpServletResponse response, String refreshToken) {
long maxAge = convertExpireTimeToCookieMaxAge(TokenType.REFRESH.getExpireTime());
setRefreshTokenCookie(response, refreshToken, maxAge);
}

private long convertExpireTimeToCookieMaxAge(long milliSeconds) {
// jwt의 expireTime: millisecond, cookie의 maxAge: second
// jwt의 expireTime 단위인 millisecond를 cookie의 maxAge 단위인 second로 변환
return milliSeconds / 1000;
}

Expand All @@ -35,7 +39,8 @@ private void setRefreshTokenCookie(
.secure(true)
.path(PATH)
.maxAge(maxAge)
.sameSite(SAME_SITE)
.domain(properties.cookieDomain())
.sameSite(properties.sameSite())
.build();
response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.example.solidconnection.auth.controller.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.server.Cookie.SameSite;

@ConfigurationProperties(prefix = "token.refresh")
public record RefreshTokenCookieProperties(
String cookieDomain
) {

public String sameSite() {
if (isDomainSet()) {
return SameSite.STRICT.attributeValue(); // 도메인을 지정한 경우 SameSite=Strict
}
return SameSite.NONE.attributeValue(); // 도메인을 지정하지 않은 경우 SameSite=None
}

private boolean isDomainSet() {
return cookieDomain != null && !cookieDomain.isBlank();
}
}
2 changes: 1 addition & 1 deletion src/main/resources/secret
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,35 @@

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.mockito.BDDMockito.given;

import com.example.solidconnection.auth.controller.config.RefreshTokenCookieProperties;
import com.example.solidconnection.auth.domain.TokenType;
import com.example.solidconnection.support.TestContainerSpringBootTest;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.mock.web.MockHttpServletResponse;

@DisplayName("리프레시 토큰 쿠키 매니저 테스트")
@TestContainerSpringBootTest
class RefreshTokenCookieManagerTest {

@Autowired
private RefreshTokenCookieManager cookieManager;

@MockBean
private RefreshTokenCookieProperties refreshTokenCookieProperties;

private final String sameSite = "Strict";
private final String domain = "example.com";

@BeforeEach
void setUp() {
cookieManager = new RefreshTokenCookieManager();
given(refreshTokenCookieProperties.cookieDomain()).willReturn(domain);
given(refreshTokenCookieProperties.sameSite()).willReturn(sameSite);
}

@Test
Expand All @@ -37,7 +51,8 @@ void setUp() {
() -> assertThat(header).contains("Secure"),
() -> assertThat(header).contains("Path=/"),
() -> assertThat(header).contains("Max-Age=" + TokenType.REFRESH.getExpireTime() / 1000),
() -> assertThat(header).contains("SameSite=Strict")
() -> assertThat(header).contains("Domain=" + domain),
() -> assertThat(header).contains("SameSite=" + sameSite)
);
}

Expand All @@ -58,7 +73,9 @@ void setUp() {
() -> assertThat(header).contains("Secure"),
() -> assertThat(header).contains("Path=/"),
() -> assertThat(header).contains("Max-Age=0"),
() -> assertThat(header).contains("SameSite=Strict")
() -> assertThat(header).contains("SameSite=Strict"),
() -> assertThat(header).contains("Domain=" + domain),
() -> assertThat(header).contains("SameSite=" + sameSite)
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.example.solidconnection.auth.controller.config;

import static org.assertj.core.api.Assertions.assertThat;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.boot.web.server.Cookie.SameSite;

@DisplayName("리프레시 토큰 쿠키 설정 테스트")
class RefreshTokenCookiePropertiesTest {

@Test
void Domain을_지정했으면_SameSite가_Strict() {
// given
RefreshTokenCookieProperties properties = new RefreshTokenCookieProperties("example.com");

// when
String sameSite = properties.sameSite();

// then
assertThat(sameSite).isEqualTo(SameSite.STRICT.attributeValue());
}

@Test
void Domain을_지정하지_않았으면_SameSite가_None() {
// given
RefreshTokenCookieProperties properties = new RefreshTokenCookieProperties(null);

// when
String sameSite = properties.sameSite();

// then
assertThat(sameSite).isEqualTo(SameSite.NONE.attributeValue());
}
}
3 changes: 3 additions & 0 deletions src/test/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,6 @@ cors:
- "http://localhost:8080"
news:
default-thumbnail-url: "default-thumbnail-url"
token:
refresh:
cookie-domain: "test.domain.com"
Loading