Spring Security
2025. 6. 13. 10:50ㆍspring
반응형
Spring Security는 Spring 기반 애플리케이션에서 인증(Authentication)과 권한 부여(Authorization)를 처리해주는 보안 프레임워크입니다. 보안은 모든 웹 애플리케이션의 필수 요소이며, Spring Security는 이 과정을 강력하고 유연하게 구현할 수 있도록 도와줍니다.
🛡️ 1. Spring Security의 핵심 개념
1-1. 인증 (Authentication)
- 사용자가 누구인지 확인하는 절차입니다.
- 주로 ID/PW 입력 → 검증 → 로그인 성공 or 실패
- 결과로는 Authentication 객체가 생성됩니다.
1-2. 권한 부여 (Authorization)
- 인증된 사용자가 어떤 리소스에 접근할 수 있는지 결정하는 절차입니다.
- 예: 관리자만 접근 가능한 페이지
🔄 2. Spring Security의 기본 동작 흐름
[1] 사용자가 로그인 요청 (/login)
↓
[2] UsernamePasswordAuthenticationFilter가 요청을 가로챔
↓
[3] AuthenticationManager가 사용자 인증 처리 (UserDetailsService → DB 조회)
↓
[4] 인증 성공 시 Authentication 객체 생성 및 SecurityContext에 저장
↓
[5] 인증된 사용자만 접근 가능한 리소스에 접근
↓
[6] 권한 확인 후 접근 허용 or 거부
🧱 3. 주요 구성 요소 설명
✅ 3-1. SecurityContextHolder
- 인증 정보를 담는 객체입니다.
- Authentication 객체를 담고 있고, 현재 사용자의 보안 정보를 어디서든 조회할 수 있게 합니다.
✅ 3-2. Authentication
- 인증에 대한 정보를 담고 있는 인터페이스입니다.
- 주요 속성:
- principal: 사용자 정보 (User 객체 등)
- credentials: 비밀번호 (보통 인증 후에는 삭제됨)
- authorities: 권한 리스트 (ROLE_USER, ROLE_ADMIN 등)
✅ 3-3. UserDetails / UserDetailsService
- UserDetails: 사용자 정보를 담는 인터페이스
- UserDetailsService: DB에서 사용자를 조회하는 서비스
public interface UserDetailsService {
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}
✅ 3-4. AuthenticationManager
- 인증 처리를 담당하는 핵심 인터페이스
- authenticate() 메서드로 인증을 시도
✅ 3-5. FilterChainProxy
- 다양한 필터들을 순서대로 적용합니다.
- 예: UsernamePasswordAuthenticationFilter, BasicAuthenticationFilter, ExceptionTranslationFilter 등
🔍 Spring Security 필터 체인 개념
Spring Security는 FilterChainProxy라는 필터를 통해 수많은 보안 필터들을 체인으로 연결해서 동작합니다.
DispatcherServlet
↓
FilterChainProxy (SpringSecurityFilterChain)
↓
├── SecurityContextPersistenceFilter
├── UsernamePasswordAuthenticationFilter
├── ExceptionTranslationFilter
├── FilterSecurityInterceptor
└── ...
- 요청이 들어오면 FilterChainProxy가 실행되고,
- 등록된 여러 보안 필터들이 정해진 순서대로 실행됩니다.
📌 주요 필터들 순서와 역할
여기서는 Spring Security의 주요 필터들을 실제 동작 순서 기준으로 설명합니다:
1). SecurityContextPersistenceFilter
- 요청 시작 시 SecurityContext를 읽어와서 저장하고,
- 요청 완료 후 SecurityContext를 저장소(Session)에 저장합니다.
- SecurityContextPersistenceFilter는 Spring Security 필터 체인에서 가장 먼저 실행되는 핵심 필터 중 하나로,
사용자의 인증 정보를 담고 있는 SecurityContext를 관리하는 역할을 합니다. - 인증된 사용자 정보는 SecurityContextHolder.getContext().getAuthentication()을 통해 얻습니다.
- 이 정보가 매 요청마다 유지되려면 세션 등에서 저장/복원하는 처리가 반드시 필요합니다.
- 이 역할을 자동으로 해주는 게 바로 SecurityContextPersistenceFilter입니다.
📦 예: 이전 로그인 정보를 세션에서 꺼내와서 현재 요청에 연결함
2). LogoutFilter
- 요청 URL이 로그아웃 경로(/logout)이면 처리
- 로그아웃 후 세션 무효화, 쿠키 제거, SecurityContext 비우기
3). UsernamePasswordAuthenticationFilter
- 로그인 처리 담당 필터
- 기본적으로 /login에 POST로 요청 시,
- username, password를 추출하고
- AuthenticationManager로 인증 시도
🛠 커스텀 로그인 로직 만들고 싶을 때 이 필터를 확장하면 됨
4). BasicAuthenticationFilter
- HTTP 기본 인증(Authorization: Basic ...)을 처리
- API 호출 시 유용 (Postman 등에서)
5). BearerTokenAuthenticationFilter (JWT 사용 시)
- 요청 헤더에 담긴 JWT 토큰을 파싱하고 인증 객체 생성
- Spring Security 5.7+ 이후 사용 가능
❗ JWT 기반 인증할 때 사용하는 필터입니다
6). SecurityContextHolderFilter (Spring Security 6+)
- SecurityContext를 쓰레드에 보관하는 필터 (기존엔 SecurityContextPersistenceFilter가 하던 일 분리됨)
7). RequestCacheAwareFilter
- 인증 후 이전 요청 URL로 리다이렉트할 수 있게 요청 캐싱 처리
8). AnonymousAuthenticationFilter
- 인증되지 않은 사용자인 경우, **익명 사용자(AnonymousUser)**로 인증 객체 생성
- 권한 체크 시 "ROLE_ANONYMOUS"로 분류됨
9). ExceptionTranslationFilter
- 필터 중간에서 예외(Exception) 발생 시
- 인증 예외: 로그인 페이지로 리다이렉트
- 인가 예외: 접근 거부 페이지로 이동
❗ 이 필터가 예외 처리의 핵심입니다
10). FilterSecurityInterceptor
- 실제로 **권한 체크(인가)**가 이뤄지는 핵심 필터
- 사용자의 권한(ROLE)이 요청 URL에 접근 가능한지 검사
로그인 예시
🔧 4. Spring Security 설정 (SecurityConfig)
@Configuration
@EnableWebSecurity
//@EnableWebSecurity는 Spring Security의 필수 설정 클래스인 WebSecurityConfigurerAdapter를
//활성화하거나, 직접 설정한 SecurityFilterChain을 Spring Security에 연결해주는 역할을 합니다.
public class SecurityConfig {
@Autowired
private CustomUserDetailsService userDetailsService;
@Autowired
private LoginSuccessHandler loginSuccessHandler;
@Autowired
private LoginFailureHandler loginFailureHandler;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf().disable() // 필요에 따라
.authorizeHttpRequests(auth -> auth
.requestMatchers("/register", "/login", "/css/**").permitAll()
.anyRequest().authenticated()
)
.formLogin(form -> form
.loginPage("/login") // 커스텀 로그인 페이지
.loginProcessingUrl("/loginProc") // 실제 로그인 처리 요청 경로
.usernameParameter("username") // 폼 input name
.passwordParameter("password") // 폼 input name
.successHandler(loginSuccessHandler) // 로그인 성공 핸들러
.failureHandler(loginFailureHandler) // 로그인 실패 핸들러
.permitAll()
)
.logout(logout -> logout
.logoutSuccessUrl("/login?logout")
.permitAll()
);
return http.build();
}
@Bean
public AuthenticationManager authManager(HttpSecurity http) throws Exception {
return http.getSharedObject(AuthenticationManagerBuilder.class)
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder())
.and()
.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
로그인 성공 처리 핸들러
@Component
public class LoginSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response,
Authentication authentication)
throws IOException, ServletException {
// 사용자 정보
User user = (User) authentication.getPrincipal();
System.out.println("로그인 성공: " + user.getUsername());
// 리다이렉트
response.sendRedirect("/home");
}
}
로그인 실패 처리 핸들러
@Component
public class LoginFailureHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException exception)
throws IOException, ServletException {
// 실패 이유 로그
System.out.println("로그인 실패: " + exception.getMessage());
// 로그인 페이지로 이동 (실패 메시지 전달 가능)
response.sendRedirect("/login?error=true");
}
}
코드 설명
@Configuration
@EnableWebSecurity
public class SecurityConfig {
- @Configuration: 이 클래스가 Spring 설정 클래스임을 명시.
- @EnableWebSecurity: Spring Security를 활성화시키는 어노테이션입니다.
→ Spring Boot 사용 시 기본 보안 설정을 무시하고, 커스텀 설정을 적용하겠다는 의미입니다.
DI (의존성 주입)
@Autowired
private CustomUserDetailsService userDetailsService;
@Autowired
private LoginSuccessHandler loginSuccessHandler;
@Autowired
private LoginFailureHandler loginFailureHandler;
- 우리가 직접 만든 클래스를 Spring이 자동으로 주입합니다.
- CustomUserDetailsService: DB에서 사용자 정보를 가져옴
- LoginSuccessHandler: 로그인 성공 시 동작 정의
- LoginFailureHandler: 로그인 실패 시 동작 정의
SecurityFilterChain - Spring Security 설정의 핵심
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
Spring Security 5.7 이후부터는 WebSecurityConfigurerAdapter 대신 SecurityFilterChain Bean 등록 방식을 사용합니다.
http.csrf().disable()
- CSRF(Cross Site Request Forgery)는 웹 공격 방지용 보안 기능입니다.
- REST API나 단순한 로그인 테스트에는 주로 비활성화합니다.
→ 단, 운영 환경에서는 가능하면 켜두는 걸 권장합니다.
.authorizeHttpRequests(auth -> auth
.requestMatchers("/register", "/login", "/css/**").permitAll()
.anyRequest().authenticated()
)
- requestMatchers("/register", "/login", "/css/**").permitAll()
→ 회원가입, 로그인, CSS 파일은 누구나 접근 가능 - anyRequest().authenticated()
→ 나머지 모든 요청은 로그인한 사용자만 접근 가능
.formLogin(form -> form
.loginPage("/login") // 커스텀 로그인 페이지
.loginProcessingUrl("/loginProc") // 로그인 submit 경로
.usernameParameter("username") // 폼 input name
.passwordParameter("password") // 폼 input name
.successHandler(loginSuccessHandler) // 로그인 성공시 실행될 핸들러
.failureHandler(loginFailureHandler) // 로그인 실패시 실행될 핸들러
.permitAll()
)
- loginPage("/login"): 커스텀 로그인 HTML 페이지
- loginProcessingUrl("/loginProc"):
폼에서 POST로 요청 시, Spring Security가 자동으로 로그인 처리함 - usernameParameter, passwordParameter:
폼에서 사용하는 input 태그의 name 속성과 매칭됨 - successHandler, failureHandler:
로그인 성공/실패 시, 우리가 만든 클래스가 실행됨
.logout(logout -> logout
.logoutSuccessUrl("/login?logout")
.permitAll()
);
- 로그아웃 처리 후 이동할 URL 설정
(기본 로그아웃 요청 경로는 /logout)
@Bean
public AuthenticationManager authManager(HttpSecurity http) throws Exception {
return http.getSharedObject(AuthenticationManagerBuilder.class)
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder())
.and()
.build();
}
- Spring Security 내부에서 사용하는 AuthenticationManager를 수동 설정합니다.
- userDetailsService(userDetailsService)
→ 로그인 시 username을 기반으로 사용자를 조회 - passwordEncoder(passwordEncoder())
→ 비밀번호 비교 시 사용될 암호화 방식 (BCrypt 사용)
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
- 비밀번호 암호화에 사용할 PasswordEncoder Bean 등록
- BCryptPasswordEncoder는 가장 널리 쓰이는 보안 해시 함수 중 하나입니다.
회원가입 시 반드시 비밀번호를 이 인코더로 암호화 후 저장해야 합니다.
🔁 전체 동작 흐름
- 사용자가 /login에 접속 → 로그인 폼 표시
- 로그인 폼에서 /loginProc로 POST 요청
- Spring Security가 로그인 처리
- CustomUserDetailsService.loadUserByUsername() 호출
- userDetailsService에서 DB 사용자 정보 조회
- BCryptPasswordEncoder로 비밀번호 비교
- 성공 → LoginSuccessHandler 호출 후 리다이렉트
실패 → LoginFailureHandler 호출 후 로그인 페이지로 리턴 - /logout 호출 시 로그아웃 수행, /login?logout으로 이동
반응형
'spring' 카테고리의 다른 글
CSRF (Cross-Site Request Forgery) (1) | 2025.06.13 |
---|---|
스프링 JWT(JSON Web Token) 인증 (1) | 2025.06.13 |
Spring JDBC (1) | 2025.06.13 |
스프링 MVC (1) | 2025.06.13 |
스프링 컨테이너 (0) | 2025.06.13 |