보안 규칙(필터 체인) 설계도
@Configuration
public class SecurityConfig {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/login", "/error").permitAll()
.anyRequest().authenticated()
)
.formLogin(Customizer.withDefaults())
.logout(Customizer.withDefaults());
return http.build();
}
}
- Configuration → 이 클래스가 스프링 설정 클래스를 의미
- 애플리케이션 시작 시 Spring이 읽어서 빈(Bean)을 등록함
- @Bean SecurityFilterChain ...
- Spring Security가 적용되는 핵심 객체를 직접 등록하는 방식
- Spring Security는 내부적으로 Filter들의 묶음(필터 체인)을 만들어서 Tomcat의 Filter Chain에 끼워 넣는데, 그 구성이 바로 SecurityFilterChain.
즉, 이 메서드는 “요청이 들어올 때 어떤 순서/규칙으로 보안 필터가 동작할지”를 정의한다.
- .csrf(csrf -> csrf.disable())
- CSRF 보호를 끔.
- 폼 로그인 + POST 요청이 있는 경우 보통 CSRF가 필요하지만, 지금 단계는 “동작 확인”이 목적이라 끄고 진행한 것.
- 나중에 진짜 서비스 형태가 되면 다시 켤지 판단하면 됨.
- authorizeHttpRequests(...)
- 접근 제어 규칙(인가, Authorization)
- /login, /error는 누구나 접근 가능
- 나머지는 전부 로그인(인증)된 사용자만 접근 가능
- 그래서 /oauth2/authorize는 여기에 걸려서: (/oauth2/authorize로 접속하면 /login으로 넘어감)
- 로그인 안 했으면 → 로그인 페이지로 리다이렉트
- 로그인 했으면 → 컨트롤러까지 도달
- 그래서 /oauth2/authorize는 여기에 걸려서: (/oauth2/authorize로 접속하면 /login으로 넘어감)
- .formLogin(Customizer.withDefaults())
- 기본 폼 로그인을 켠다
- 따로 로그인 페이지를 만들지 않아도 /login에 기본 UI가 뜬다
- 인증되지 않은 사용자가 보호된 URL로 접근하면 Spring Security가 자동으로
- 로그인 페이지로 보내고
- 로그인 성공 후 원래 가려던 URL로 돌아오게 해준다.
- .logout(Customizer.withDefaults())
- 기본 로그아웃을 켠다.
- 보통 /logout 요청으로 세션을 무효화하고 SecurityContext를 지운다.
로그인 가능한 사용자(테스트 계정) - 공급자
@Configuration
public class TestUsersConfig {
@Bean
UserDetailsService userDetailsService() {
UserDetails user = User.withUsername("user")
.password("{noop}1234")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
}
- Spring Security에서 폼 로그인이 동작하려면 입력한 username/password가 맞는지 확인할 사용자 저장소가 필요함 → 그 역할을 하는 표준 인터페이스가 UserDetailsService
- UserDetailsService
- Spring Security가 로그인 시도할 때 내부적으로 호출하는 메소드가 있음
- loadUserByUsername(username)
- 아직 DB를 붙이지 않았으니 가장 간단한 인 메모리 사용자 저장소를 제공한 것
- Spring Security가 로그인 시도할 때 내부적으로 호출하는 메소드가 있음
- {noop} → Spring Security는 기본적으로 비밀번호 인코딩(해시) 방식이 필요함 {noop}는 “인코딩 안 함(평문 비교)”이라는 뜻
- InMemoryUserDetailsManager
- 메모리에 사용자 정보를 저장하는 구현체.
- user/1234로 로그인 가능해짐.
로그인 상태인지 확인하는 테스트 엔드포인트
@RestController
public class AuthDebugController {
@GetMapping("/oauth2/authorize")
public String authorizeDebug(@AuthenticationPrincipal UserDetails user) {
return "OK - logged in as: " + user.getUsername();
}
}
- 왜 /oauth2/authorize를 이렇게 만들었나?
- 지금은 아직 OAuth2 인가 로직(클라이언트 검증, code 발급)이 없으니
- “로그인/세션이 제대로 동작하는지” 확인하기 위해 /oauth2/authorize를 디버그 용으로 임시 구현한 것
나중에 이 엔드포인트가
- client_id, redirect_uri 검증
- code 발급
- redirect
를 하게 될 거임
- @AuthenticationPrincipal이 뭐냐?
- 현재 요청을 보낸 사용자의 인증 정보(Principal)를 Spring Security가 SecurityContext에 저장해두는데 그걸 컨트롤러에서 꺼내 쓰기 쉽게 주입해주는 어노테이션.
- 즉, 이 메소드가 호출된다다는 것 자체가 이미 SecurityFilterChain을 통과했고 인증된 사용자라는 뜻
시나리오
A. 로그인 안 한 상태로 /oauth2/authorize 접근
- 브라우저가 GET /oauth2/authorize 요청
- Spring Security 필터 체인이 먼저 실행됨
- .anyRequest().authenticated() 규칙에 걸림
- 인증 정보가 없으니 로그인 페이지 /login으로 리다이렉트(302)
B. 로그인 성공 후 다시 /oauth2/authorize
- /login에서 user/1234 입력
- UserDetailsService가 user 조회 → 비밀번호 비교
- 성공하면 인증 정보가 세션 + SecurityContext에 저장됨
- Spring Security가 원래 목적지였던 /oauth2/authorize로 이동시킴
- 컨트롤러 호출
- @AuthenticationPrincipal로 user 주입
- "OK - logged in as: user" 반환
Spring Security 순서
- Spring Security에서 클라이언트가 요청했을 때 어떻게 돌아가는지부터 보자
'RFC > OAuth 2.0' 카테고리의 다른 글
| [OAuth 2.0] OAuth Client 테이블 설계(비밀 클라이언트 기준) (0) | 2026.02.04 |
|---|---|
| [OAuth 2.0] 인메모리 DB 사용자 테스트 (0) | 2026.02.04 |
| [OAuth 2.0] Spring Authorization Server 코드 분석(OAuth2AuthorizationEndpointFilter 클래스) (0) | 2026.02.04 |
| [OAuth 2.0] 인가 엔드포인트 큰 흐름 (0) | 2026.02.04 |
| [OAuth 2.0] Spring Security (0) | 2026.02.03 |