문제:
사용자가 입력한 비밀번호가 데이터베이스에 암호화되지 않은 평문으로 저장됨.
원인:
PasswordEncoder를 설정하지 않았거나 잘못 사용함.
해결 방법:
Spring Security에서 제공하는 BCryptPasswordEncoder를 사용해 비밀번호를 암호화.
비밀번호 검증에도 동일한 암호화 방식을 사용.
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Service
public class UserService {
@Autowired
private PasswordEncoder passwordEncoder;
public void registerUser(User user) { user.setPassword(passwordEncoder.encode(user.getPassword()));
// 사용자 저장 로직
}
}
문제:
특정 역할(Role)을 가진 사용자만 접근 가능한 경로에 아무 사용자나 접근 가능.
원인:
Spring Security에서 역할(Role)을 매칭하는 규칙이 설정되지 않음.
ROLE_ 접두사가 누락되거나 잘못 설정됨.
해결 방법:
역할 권한에 ROLE_ 접두사가 자동으로 붙으므로, 이를 일치시키도록 설정.
데이터베이스에 저장된 역할 이름에도 ROLE_을 포함하거나 매칭 규칙 수정.
http.authorizeHttpRequests() .requestMatchers("/admin/**").hasRole("ADMIN") // ROLE_ADMIN .requestMatchers("/user/**").hasAnyRole("USER", "ADMIN") // ROLE_USER 또는 ROLE_ADMIN .anyRequest().authenticated();
문제:
동일한 계정으로 여러 디바이스에서 동시에 로그인 가능하며, 이를 제한하고 싶음.
원인:
세션 제어가 기본 설정으로 활성화되지 않음.
해결 방법:
세션 관리 정책을 설정해 동일 계정의 중복 로그인을 제한.
http.sessionManagement()
.maximumSessions(1) // 최대 1개의 세션만 허용
.maxSessionsPreventsLogin(true); // 새 세션이 기존 세션을 대체하지 못하도록 설정
문제:
로그인 실패 시 기본적인 에러 메시지만 출력되고, 사용자에게 적절한 피드백을 제공하지 못함.
원인:
로그인 실패 처리가 기본 설정으로 되어 있으며, 이를 커스터마이징하지 않음.
해결 방법:
로그인 실패 시 호출되는 핸들러를 구현해 커스터마이징.
http.formLogin()
.loginPage("/login")
.failureHandler((request, response, exception) -> {
request.setAttribute("error", "로그인에 실패했습니다: " + exception.getMessage());
request.getRequestDispatcher("/login").forward(request, response);
});
- response는 클라이언트에게 응답을 보내는 객체로, 데이터를 전송하거나 상태 코드를 설정할 때 사용됩니다.
- request.setAttribute는 현재 요청에 데이터를 추가하여 JSP 등으로 전달할 때 사용합니다.
- RequestDispatcher는 요청(Request)을 특정 경로로 전달하거나 포워딩할 때 사용됩니다.
- 요청을 내부적으로 다른 경로로 전달할 때는 request를 사용해야 하며, 이는 response와는 다른 책임입니다.
response.sendRedirect()를 통해 클라이언트가 새로운 요청을 하도록 합니다. 하지만 이 경우 데이터를 전달하려면 URL에 쿼리 파라미터로 포함해야 합니다:
.failureHandler((request, response, exception) -> {
String errorMessage = "로그인에 실패했습니다: " + exception.getMessage();
response.sendRedirect("/login?error=" + URLEncoder.encode(errorMessage, "UTF-8"));
});
이 방식은 클라이언트가 /login 페이지로 이동하면서 에러 메시지를 받을 수 있게 합니다. 다만, URL에 에러 메시지가 노출되는 단점이 있습니다.
REST API 기반 로그인이라면 JSON으로 응답을 작성할 수 있습니다:
.failureHandler((request, response, exception) -> {
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
String jsonResponse = String.format(
"{\"error\": \"로그인에 실패했습니다\", \"message\": \"%s\"}",
exception.getMessage()
);
response.getWriter().write(jsonResponse);
});
이 방식은 API 클라이언트(예: React, Angular)와 통신할 때 적합합니다.
문제:
REST API를 사용하지만 브라우저 기반 세션이 유지되어 API 호출 시 인증 문제가 발생함.
원인:
REST API는 일반적으로 stateless 방식으로 동작하지만, Spring Security가 기본적으로 세션을 사용.
해결 방법:
SessionCreationPolicy.STATELESS를 설정해 세션을 비활성화.
http.sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf().disable();
문제:
로그아웃 후 재로그인 시 이전 세션 정보가 남아있어 문제가 발생.
원인:
로그아웃 시 세션이 제대로 무효화되지 않음.
해결 방법:
로그아웃 시 세션을 명시적으로 삭제.
http.logout()
.logoutUrl("/logout")
.invalidateHttpSession(true) // 세션 무효화
.deleteCookies("JSESSIONID"); // 쿠키 삭제
문제:
사용자 정의 인증 필터를 추가했는데, Spring Security 기본 필터와 충돌.
원인:
커스텀 필터가 기본 필터 체인의 올바른 위치에 추가되지 않음.
해결 방법:
커스텀 필터를 적절한 위치에 추가.
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.addFilterBefore(new CustomAuthFilter(), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
문제:
HTTPS가 아닌 HTTP로 요청할 때, 접근이 차단되거나 리디렉션 문제 발생.
원인:
Spring Security 기본 설정으로 HTTP 요청을 HTTPS로 리디렉션.
해결 방법:
HTTPS 강제를 비활성화하거나 조건부로 적용.
http.requiresChannel()
.anyRequest().requiresSecure(); // HTTPS 강제
// HTTPS 강제 비활성화
http.requiresChannel().anyRequest().requiresInsecure();
http.requiresChannel은 Spring Security에서 특정 요청이 반드시 특정 프로토콜(예: HTTP 또는 HTTPS)을 통해 처리되도록 요구할 때 사용하는 메서드입니다. 이 메서드를 사용하면 보안 요구 사항에 따라 HTTP 요청을 자동으로 HTTPS로 리다이렉트하거나 특정 프로토콜만 허용하도록 설정할 수 있습니다.
문제:
CSS, JS, 이미지 파일과 같은 정적 리소스에 접근이 차단됨.
원인:
Spring Security가 정적 리소스 요청을 인증 필요로 간주.
해결 방법:
정적 리소스 경로를 허용 경로에 추가.
http.authorizeHttpRequests()
.requestMatchers("/css/**", "/js/**", "/images/**").permitAll();
문제:
프론트엔드와 백엔드가 다른 도메인에서 동작하며, CORS 관련 오류 발생.
원인:
Spring Security에서 CORS 정책이 제대로 설정되지 않음.
해결 방법:
Spring Security와 CorsConfiguration을 설정.
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration(); configuration.addAllowedOrigin("http://localhost:3000");
// 프론트엔드 URL
configuration.addAllowedMethod("");
configuration.addAllowedHeader("");
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
http.cors().configurationSource(corsConfigurationSource());