๐ก Spring ํต์ฌ ๊ฐ๋ ์ธํฐ๋ทฐ Q&A
ํน์ง
๋์
์์
CORS์ ์ด๋ฐฉ๋ฒ1 : SpringMVC ์ ์ญ CORS ์ค์ ํ๋ ๋ฐฉ๋ฒ (WebMvcConfigurer, Spring ์์ค ์ค์ )
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") // ๋ชจ๋ ๊ฒฝ๋ก์ ๋ํด
.allowedOrigins("https://example.com") // ํ์ฉํ Origin
.allowedMethods("GET", "POST", "PUT", "DELETE") // ํ์ฉํ ๋ฉ์๋
.allowedHeaders("*")
.allowCredentials(true) // ์ฟ ํค ํฌํจ ์ฌ๋ถ
.maxAge(3600); // preflight ์์ฒญ ์บ์ ์๊ฐ
}
}
@Component
public class CorsFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletResponse res = (HttpServletResponse) response;
HttpServletRequest req = (HttpServletRequest) request;
res.setHeader("Access-Control-Allow-Origin", "https://example.com");
res.setHeader("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS");
res.setHeader("Access-Control-Allow-Headers", "*");
res.setHeader("Access-Control-Allow-Credentials", "true");
if ("OPTIONS".equalsIgnoreCase(req.getMethod())) {
res.setStatus(HttpServletResponse.SC_OK);
return;
}
chain.doFilter(request, response);
}
}
ํน์ง
์์
HttpSession์ ์ ์ฅ๋์ด ์๋ค๊ณ ๊ฐ์ ํ๊ณ , Interceptor๋ฅผ ์ด์ฉํด ๋ก๊ทธ์ธ ์ฌ๋ถ๋ฅผ ํ์ธํ๊ณ ๋น๋ก๊ทธ์ธ ์ฌ์ฉ์๋ฅผ ์ฐจ๋จํ๋ ์์public class LoginCheckInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
HttpSession session = request.getSession(false);
if (session == null || session.getAttribute("loginUser") == null) {
// ๋ก๊ทธ์ธ ์๋ ๊ฒฝ์ฐ
response.sendRedirect("/login");
return false; // ์์ฒญ์ Controller๋ก ๋ณด๋ด์ง ์์
}
return true; // ๋ก๊ทธ์ธ ๋์ด์์ผ๋ฉด Controller๋ก ์งํ
}
}
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginCheckInterceptor())
.addPathPatterns("/**") // ์ ์ฒด ๊ฒฝ๋ก ๊ฒ์ฌ
.excludePathPatterns("/", "/login", "/logout", "/css/**", "/js/**", "/images/**"); // ์์ธ ๊ฒฝ๋ก
}
}
ํด๋ผ์ด์ธํธ ์์ฒญ
โ
DispatcherServlet
โ
LoginCheckInterceptor (preHandle)
โ
Controller
โ
LoginCheckInterceptor (postHandle / afterCompletion)
preHandle()์์ false๋ฅผ ๋ฆฌํดํ๋ฉด ์ปจํธ๋กค๋ฌ๋ก ์์ฒญ์ด ๊ฐ์ง ์์session.getAttribute("loginUser") ๋๋ Spring Security ์ฌ์ฉ ์ SecurityContextHolder๋ก ํ์ธpostHandle() โ ์ปจํธ๋กค๋ฌ ์คํ ํ View ๋ ๋๋ง ์ afterCompletion() โ ์๋ต ์๋ฃ ์งํ, ์์ธ ์ฒ๋ฆฌ์ฉ์ผ๋ก๋ ์ฌ์ฉ ๊ฐ๋ฅJWT ๊ธฐ๋ฐ ๋ก๊ทธ์ธ ๊ฒ์ฌ Interceptor ์์
@Component
public class JwtAuthInterceptor implements HandlerInterceptor {
private final JwtTokenProvider jwtTokenProvider; //์ง์ ๊ตฌํํJWT ์ ํธํด๋์ค
// ์์ฑ์ ์ฃผ์
public JwtAuthInterceptor(JwtTokenProvider jwtTokenProvider) {
this.jwtTokenProvider = jwtTokenProvider;
}
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
String authHeader = request.getHeader("Authorization");
// ํ ํฐ ์์
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return false;
}
String token = authHeader.substring(7); // "Bearer " ์ ๊ฑฐ
// ํ ํฐ ๊ฒ์ฆ
if (!jwtTokenProvider.validateToken(token)) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return false;
}
// ์ฌ์ฉ์ ์ ๋ณด ์ถ์ถ (Optional)
String userId = jwtTokenProvider.getUserIdFromToken(token);
request.setAttribute("userId", userId); // ์ปจํธ๋กค๋ฌ์์ ํ์ฉ ๊ฐ๋ฅ
return true;
}
}
@Configuration
public class WebConfig implements WebMvcConfigurer {
private final JwtAuthInterceptor jwtAuthInterceptor;
public WebConfig(JwtAuthInterceptor jwtAuthInterceptor) {
this.jwtAuthInterceptor = jwtAuthInterceptor;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(jwtAuthInterceptor)
.addPathPatterns("/api/**")
.excludePathPatterns("/api/auth/**"); // ๋ก๊ทธ์ธ, ํ์๊ฐ์
์ ์์ธ
}
}
์์ฝ
(์: ๋ก๊ทธ์ธ ์ฒดํฌ, ํน์ ์์ฒญ ๋ก๊น
๋ฑ)๋์์์
1. ํด๋ผ์ด์ธํธ๊ฐ ์์ฒญ์ ๋ณด๋
2. DispatcherServlet์ด ์์ฒญ์ ์์
3. ๋ฑ๋ก๋ Interceptor์ preHandle() ๋ฉ์๋ ํธ์ถ
โ ์ด๋ preHandle()์์ false๋ฅผ ๋ฐํํ๋ฉด 4๋ฒ
4. ์ปจํธ๋กค๋ฌ ํธ์ถ ์์ด ์์ฒญ ์ข
๋ฃ
โ ์ดํ View๋ ๋ ๋๋ง๋์ง ์์
โ ์๋ต ์ฝ๋(์: 401) ๋๋ ๋ฆฌ๋ค์ด๋ ํธ ์ฒ๋ฆฌ ํ์
๋
(๋ฐ๋๋ก preHandle()์์ true๋ฅผ ๋ฐํํ๋ฉด 5๋ฒ)
5. ์ปจํธ๋กค๋ฌ ์คํ
6. postHandle(), afterCompletion() ์์๋๋ก ์คํ
๋