package com.goi.erp.auth; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import java.util.Arrays; import java.util.Optional; @Service public class AuthCookieService { public static final String AUTH_COOKIE_NAME = "AUTH_TOKEN"; public static final String REFRESH_COOKIE_NAME = "REFRESH_TOKEN"; @Value("${auth.cookie.secure:true}") private boolean secure; @Value("${auth.cookie.same-site:Lax}") private String sameSite; @Value("${auth.cookie.path:/}") private String path; // ===================== Access Token ===================== public void addAuthCookie(HttpServletResponse response, String jwt) { addCookie(response, AUTH_COOKIE_NAME, jwt, -1); } public void clearAuthCookie(HttpServletResponse response) { addCookie(response, AUTH_COOKIE_NAME, null, 0); } public Optional extractJwt(HttpServletRequest request) { return extractCookie(request, AUTH_COOKIE_NAME); } // ===================== Refresh Token ===================== public void addRefreshCookie(HttpServletResponse response, String refreshToken) { addCookie(response, REFRESH_COOKIE_NAME, refreshToken, -1); } public void clearRefreshCookie(HttpServletResponse response) { addCookie(response, REFRESH_COOKIE_NAME, null, 0); } public Optional extractRefreshToken(HttpServletRequest request) { return extractCookie(request, REFRESH_COOKIE_NAME); } // ===================== 내부 공통 ===================== private void addCookie( HttpServletResponse response, String name, String value, int maxAge ) { Cookie cookie = new Cookie(name, value); cookie.setHttpOnly(true); cookie.setSecure(secure); cookie.setPath(path); cookie.setMaxAge(maxAge); response.addCookie(cookie); addSameSiteAttribute(response, cookie); } private Optional extractCookie(HttpServletRequest request, String name) { if (request.getCookies() == null) { return Optional.empty(); } return Arrays.stream(request.getCookies()) .filter(c -> name.equals(c.getName())) .map(Cookie::getValue) .filter(v -> v != null && !v.isBlank()) .findFirst(); } /** * SameSite 수동 세팅 (Servlet Cookie API 한계) */ private void addSameSiteAttribute(HttpServletResponse response, Cookie cookie) { StringBuilder sb = new StringBuilder(); sb.append(cookie.getName()).append("="); sb.append(cookie.getValue() == null ? "" : cookie.getValue()); sb.append("; Path=").append(cookie.getPath()); sb.append("; HttpOnly"); if (cookie.getSecure()) { sb.append("; Secure"); } if (sameSite != null && !sameSite.isBlank()) { sb.append("; SameSite=").append(sameSite); } if (cookie.getMaxAge() == 0) { sb.append("; Max-Age=0"); } response.addHeader("Set-Cookie", sb.toString()); } }