🔍 1. Spring Security의 보안 구성 흐름
Spring Security에서 보안 구성을 설정하는 주요 과정은 다음과 같습니다.
📌 (1) HttpSecurity를 활용한 보안 설정
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/", "/login").permitAll()
.requestMatchers("/admin").hasRole("ADMIN")
.anyRequest().authenticated()
)
.formLogin(auth -> auth
.loginPage("/login")
.loginProcessingUrl("/loginProc")
.permitAll()
)
.csrf(auth -> auth.disable());
return http.build();
}
✔️ HttpSecurity를 설정하면 Spring Security가 모든 요청을 가로채서 인증 및 권한 검사를 수행
✔️ 이 설정만으로도 기본적인 보안 필터 체인이 완성됨
📌 (2) Spring Security 내부에서 자동으로 처리하는 것
- SecurityFilterChain을 반환하면 Spring Security는 자동으로
1️⃣ 모든 요청을 보안 필터 체인을 통해 감시
2️⃣ 로그인 요청(/loginProc)을 가로채서 인증 수행
3️⃣ 인증된 사용자의 정보(SecurityContext)를 관리
4️⃣ 각 URL 접근 권한(Role)을 검사하여 허용/거부 결정
✔️ 즉, HttpSecurity만 설정하면 Spring Security의 보안 기능은 기본적으로 동작함.
✅ 2. 하지만 추가로 설정이 필요한 경우도 있다!
HttpSecurity만 설정하면 보안 구성이 거의 끝나지만, 추가적인 설정이 필요한 경우가 있습니다.
🔹 (1) 사용자 정보 관리 (UserDetailsService)
HttpSecurity 설정만으로는 사용자 정보(아이디, 비밀번호)를 직접 관리하지 않음
✔️ 로그인 시, 사용자 정보를 어디서 가져올 것인지 별도로 정의해야 함.
✔️ 기본적으로 UserDetailsService를 구현하여 DB 또는 메모리에서 사용자 정보를 가져옴.
@Bean
public UserDetailsService userDetailsService() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("admin")
.password("1234")
.roles("ADMIN")
.build();
return new InMemoryUserDetailsManager(user);
}
✔️ 위 설정이 없으면 로그인할 사용자 정보가 없기 때문에 로그인 자체가 불가능
✔️ 즉, HttpSecurity 설정만으로는 보안 구성이 완전하지 않고 사용자 정보 제공 설정이 추가로 필요함
🔹 (2) SecurityContext 관리 (로그인 세션, JWT 토큰 등)
기본적으로 Spring Security는 세션을 사용하여 사용자 인증 상태를 관리하지만,
JWT 같은 방식으로 세션 없이 인증을 유지하려면 추가 설정이 필요함
✔️ 기본 세션 기반 인증 방식:
http
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) // 필요할 때만 세션 생성
);
✔️ JWT 기반 인증 방식:
http
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 세션 없이 JWT 사용
);
✔️ HttpSecurity 설정만으로는 세션 관리 방식까지는 자동으로 설정되지 않으므로, 개발자가 직접 설정해야 함
🔹 (3) CORS & CSRF 설정
✔️ REST API 기반 애플리케이션에서는 CORS 및 CSRF 보안 설정을 직접 해줘야 함
✔️ HttpSecurity 기본 설정에는 CORS 설정이 포함되지 않으므로 개발자가 직접 추가해야 함
http
.cors(Customizer.withDefaults()) // CORS 활성화
.csrf(csrf -> csrf.disable()); // CSRF 보호 비활성화 (REST API에서는 주로 비활성화)
✔️ REST API에서는 대부분 CSRF를 비활성화해야 하지만, 웹 애플리케이션에서는 활성화가 필요할 수도 있음
✅ 3. 결론
✔️ 보안 구성을 설정하는 가장 핵심적인 부분은 HttpSecurity
✔️ 하지만, 추가로 필요한 설정 (사용자 정보 관리, SecurityContext 관리, CORS/CSRF 설정 등)은 개발자가 직접 추가해야 함
✔️ 즉, HttpSecurity만 설정한다고 모든 보안 설정이 끝나는 것은 아니며, 추가적인 설정이 필요할 수도 있다!
@Configuration // 이 클래스가 Spring Security 설정을 위한 구성 클래스임을 나타냅니다.
@EnableWebSecurity(debug = true) // Spring Security의 웹 보안을 활성화하고, 디버깅 정보를 출력하도록 설정합니다.
public class SecurityConfig {
/**
* TODO#2 - '/*' <-- 모든 요청에 대해서 인증받은 사용자만 서비스를 이용할 수 있습니다.
* HttpSecurity를 사용해서 보안 설정을 정의할 수 있습니다.
* - httpBasic 활성화 합니다. - 교재를 참고 합니다.
* - formLogin 활성화 합니다. - 교재를 참고 합니다.
*/
// HttpSecurity 객체를 이용해 보안 설정을 정의하는 메서드
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
// HTTP 요청에 대한 보안 설정
http
.authorizeHttpRequests(authorizeRequests ->
authorizeRequests.anyRequest().authenticated()) // 모든 요청에 대해 인증을 요구합니다.
.httpBasic(Customizer.withDefaults()) // HTTP Basic 인증을 사용합니다.
.formLogin(Customizer.withDefaults()); // 폼 로그인 기능을 사용합니다.
// 설정이 끝나면 SecurityFilterChain 객체를 반환하여 보안 구성을 완료합니다.
return http.build();
}
/**
* TODO#3 - UserDetailsService 빈을 정의하여 사용자 인증 정보를 제공합니다.
* UserDetailsService는 사용자의 인증 정보를 가져오는 역할을 합니다.
* Spring Security는 이 인터페이스를 통해 사용자 이름을 기반으로 사용자 정보를 조회하고,
* 그 정보를 사용하여 인증을 수행합니다. 기본적으로 아이디, 비밀번호, 권한 등의 정보를 제공합니다.
*/
@Bean
public UserDetailsService userDetailsService() {
// 사용자 정보를 정의하고, 기본 비밀번호 인코더를 사용하여 암호화합니다.
UserDetails userDetails = User.withDefaultPasswordEncoder() // 기본 비밀번호 인코더 사용
.username("marco") //TODO#3-1 사용자 이름
.password("nhnacademy") //TODO#3-2 비밀번호
.roles("MEMBER") //TODO#3-3 사용자 역할 (MEMBER)
.build(); // UserDetails 객체 생성
//TODO#3-4 InMemoryUserDetailsManager를 사용하여 메모리 내에서 사용자 정보를 관리합니다.
return new InMemoryUserDetailsManager(userDetails);
}
}
🔍 Spring Security 설정 (SecurityConfig) 설명
이 코드는 Spring Security를 사용하여 인증(Authentication)과 권한(Authorization)을 설정하는 클래스입니다.
특히 In-Memory 방식으로 사용자를 관리하며, HTTP Basic 및 Form Login을 활성화하는 기본적인 설정을 포함하고 있습니다.
✅ 1. 주요 개념
| 개념 | 설명 |
| SecurityFilterChain | 모든 요청이 통과하는 보안 필터 체인 (인증 & 권한 체크) |
| HttpSecurity | Spring Security 설정을 정의하는 객체 |
| UserDetailsService | 사용자 정보를 조회하는 서비스 |
| InMemoryUserDetailsManager | 메모리 기반으로 사용자 정보를 관리하는 방식 |
✅ 2. SecurityConfig 클래스 분석
@Configuration
@EnableWebSecurity(debug = true)
✔️ @Configuration → 이 클래스가 Spring Security 설정을 담당하는 구성 클래스임을 나타냄
✔️ @EnableWebSecurity(debug = true) → Spring Security를 활성화하고, 디버깅 정보를 출력하도록 설정
✅ 3. SecurityFilterChain (보안 필터 체인) 설정
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth.anyRequest().authenticated()) // 모든 요청에 대해 인증을 요구
.httpBasic(Customizer.withDefaults()) // HTTP Basic 인증 사용
.formLogin(Customizer.withDefaults()); // Form Login 사용
return http.build();
}
📌 1) 모든 요청에 대해 인증 요구
.authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
- anyRequest().authenticated() → 모든 요청에 대해 인증된 사용자만 접근 가능
📌 2) HTTP Basic 인증 활성화
.httpBasic(Customizer.withDefaults())
- HTTP Basic 인증을 활성화
- 로그인 창 없이 브라우저 기본 팝업을 이용하여 인증 수행
📌 3) Form Login (웹 로그인 폼) 활성화
.formLogin(Customizer.withDefaults());
- Spring Security 기본 제공 로그인 폼 활성화
- /login 페이지가 자동으로 생성됨
✅ 4. 사용자 정보 관리 (UserDetailsService)
@Bean
public UserDetailsService userDetailsService() {
UserDetails userDetails = User.withDefaultPasswordEncoder()
.username("marco") // 사용자 이름
.password("nhnacademy") // 비밀번호
.roles("MEMBER") // 사용자 역할 (MEMBER)
.build();
return new InMemoryUserDetailsManager(userDetails);
}
📌 1) UserDetailsService란?
- Spring Security에서 사용자 정보를 조회하는 서비스
- 로그인 시 입력된 아이디(username)로 DB 또는 메모리에서 사용자 정보를 가져옴
📌 2) InMemoryUserDetailsManager (메모리 기반 사용자 관리)
return new InMemoryUserDetailsManager(userDetails);
✔️ 메모리 기반으로 사용자 정보를 저장
✔️ DB 없이 간단한 테스트 용도로 사용 가능
✔️ 애플리케이션이 재시작되면 데이터가 초기화됨
📌 3) UserDetails 객체 생성
UserDetails userDetails = User.withDefaultPasswordEncoder()
.username("marco") // 사용자 이름
.password("nhnacademy") // 비밀번호
.roles("MEMBER") // 역할 (MEMBER)
.build();
✔️ 기본적인 사용자 정보 (아이디, 비밀번호, 역할)를 설정
✔️ withDefaultPasswordEncoder() → 비밀번호를 평문(암호화 없이) 저장 (테스트 용도)
✔️ "marco" 라는 사용자가 "MEMBER" 역할을 가짐
✅ 5. Spring Security의 동작 흐름
🔹 (1) 사용자가 /admin 페이지에 접근
GET /admin
✔️ Spring Security의 SecurityFilterChain이 요청을 가로챔
✔️ 인증되지 않은 사용자면 /login 페이지로 자동 리다이렉트
🔹 (2) 사용자가 로그인 요청을 보냄
POST /login
✔️ username=marco & password=nhnacademy 입력 후 로그인 시도
✔️ UserDetailsService에서 marco 계정을 조회
✔️ 비밀번호 검증 후 인증 성공 시 SecurityContextHolder에 사용자 정보 저장
🔹 (3) 로그인 성공 후 원래 요청한 페이지로 이동
.defaultSuccessUrl("/index", false);
✔️ 기본적으로 로그인 전에 요청했던 페이지로 이동
✔️ .defaultSuccessUrl("/index", false); 설정 시 특정 페이지로 이동 가능
✅ 6. 결론
💡 Spring Security는 모든 요청을 가로채서 인증과 권한을 검사한 후, 올바른 요청이면 원래 목적지로 보내는 방식으로 동작한다!
✔️ SecurityFilterChain을 통해 모든 요청을 인증 필요하게 설정
✔️ HTTP Basic 및 Form Login을 활성화하여 로그인 기능 제공
✔️ In-Memory 방식으로 사용자 정보를 저장 (테스트 용도)
✔️ 로그인 후 사용자의 역할(Role)을 기반으로 접근을 제한할 수 있음
package com.nhnacademy.springsecurity.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
public class SercurityConfig {
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((auth) -> auth
.requestMatchers("/", "/login", "/loginProc", "/join", "/joinProc").permitAll() // 인증 없이 접근 가능
.requestMatchers("/admin").hasRole("ADMIN") // ADMIN 권한이 있어야 접근 가능
.requestMatchers("/my/**").hasAnyRole("ADMIN", "USER") // ADMIN 또는 USER 권한 필요
.anyRequest().authenticated() // 그 외 모든 요청은 인증 필요
);
/*
.loginPage("/login")
→ 로그인하지 않은 사용자가 인증이 필요한 페이지(/admin 등)에 접근하면 자동으로 /login 페이지로 리다이렉트
.loginProcessingUrl("/loginProc")
→ 로그인 폼이 제출되면 /loginProc 경로에서 로그인 처리를 수행
*/
// loginPage 메서드 덕분에 이제부터 admin 페이지로 가도 오류 뜨지 않고 /login 페이지로 이동하게 된다.
// .loginPage("/login") 설정 덕분에 404 오류 없이 /login 페이지로 이동 가능
http
.formLogin((auth) -> auth
.loginPage("/login") // 로그인 페이지 지정
.loginProcessingUrl("/loginProc") // 로그인 요청을 처리하는 URL
.permitAll()
);
http
.csrf((auth) -> auth.disable());
return http.build();
}
}
✅ 1. SecurityFilterChain 설정 분석
이 설정을 통해 어떤 URL에 접근할 때 인증이 필요한지, 로그인 페이지는 어디인지 등을 지정할 수 있습니다.
🔹 (1) authorizeHttpRequests() - 접근 권한 설정
http
.authorizeHttpRequests((auth) -> auth
.requestMatchers("/", "/login").permitAll() // 인증 없이 접근 가능
.requestMatchers("/admin").hasRole("ADMIN") // ADMIN 권한이 있어야 접근 가능
.requestMatchers("/my/**").hasAnyRole("ADMIN", "USER") // ADMIN 또는 USER 권한 필요
.anyRequest().authenticated() // 그 외 모든 요청은 인증 필요
);
- requestMatchers("/", "/login").permitAll()
→ 인증 없이 누구나 접근 가능 - requestMatchers("/admin").hasRole("ADMIN")
→ ADMIN 권한이 있어야 /admin 페이지에 접근 가능 - requestMatchers("/my/**").hasAnyRole("ADMIN", "USER")
→ ADMIN 또는 USER 권한이 있어야 /my/... 페이지에 접근 가능 - anyRequest().authenticated()
→ 그 외 모든 요청은 반드시 로그인해야 접근 가능
🔹 (2) formLogin() - 로그인 페이지 설정
http
.formLogin((auth) -> auth
.loginPage("/login") // 로그인 페이지 지정
.loginProcessingUrl("/loginProc") // 로그인 요청을 처리하는 URL
.permitAll()
);
- .loginPage("/login")
→ 로그인하지 않은 사용자가 인증이 필요한 페이지(/admin 등)에 접근하면 자동으로 /login 페이지로 리다이렉트 - .loginProcessingUrl("/loginProc")
→ 로그인 폼이 제출되면 /loginProc 경로에서 로그인 처리를 수행 - .permitAll()
→ 로그인 페이지(/login)는 누구나 접근 가능
🔹 (3) csrf().disable() - CSRF 보호 비활성화
http
.csrf((auth) -> auth.disable());
- CSRF(Cross-Site Request Forgery) 공격 방지를 위한 보안 기능을 비활성화
- 보통 API 서버에서 사용하지만, CSRF 보호를 유지하는 것이 보안상 더 안전함
✅ 2. loginPage()가 admin 페이지 접근 시 오류 없이 /login으로 이동하는 이유
http
.formLogin((auth) -> auth
.loginPage("/login") // 로그인 페이지 지정
.loginProcessingUrl("/loginProc")
.permitAll()
);
🧐 🤔 /admin 페이지에 직접 접근할 때 왜 오류가 안 나고 /login으로 이동할까?
- 사용자가 /admin 페이지에 접근
- .requestMatchers("/admin").hasRole("ADMIN") 설정 때문에 ADMIN 권한이 있는 사용자만 접근 가능
- 사용자가 로그인하지 않았거나, ADMIN 권한이 없으면 Spring Security가 자동으로 로그인 페이지(/login)로 리다이렉트
- .loginPage("/login") 설정 덕분에 404 오류가 발생하지 않고, /login 페이지로 이동
🔥 즉, loginPage("/login")을 설정하지 않으면 기본 제공되는 로그인 페이지로 리다이렉트되지만, 설정하면 커스텀 페이지(/login)로 이동하도록 변경 가능!
✅ 3. 흐름 정리
📌 📍 사용자가 /admin 페이지에 접근하는 경우
1️⃣ 로그인 여부 확인
- 로그인하지 않은 경우 → 자동으로 /login 페이지로 리다이렉트
- 로그인한 경우 → 2️⃣번 과정 진행
2️⃣ 권한 확인 (hasRole("ADMIN"))
- ADMIN 권한이 있으면 /admin 페이지 접근 가능
- 없으면 403 Forbidden 오류 발생
✅ 4. 결론
🎯 🔹 핵심 정리
- Spring Security는 권한이 없는 사용자가 인증이 필요한 페이지에 접근하면 자동으로 로그인 페이지로 보냄
- .loginPage("/login") 설정 덕분에 404 오류 없이 /login 페이지로 이동 가능
- 로그인 성공 후 기본적으로 이전에 요청한 페이지로 이동(설정 변경 가능)
'Spring > 09. spring-security' 카테고리의 다른 글
| Spring Security는 로그인 & 로그아웃을 위한 API인가? (0) | 2025.03.05 |
|---|---|
| AuthenticationManager와 PasswordEncoder (0) | 2025.03.05 |
| .logout()과 .sessionManagement() (0) | 2025.03.05 |
| Spirng Security 개념 및 동작 방식 정리 (0) | 2025.03.05 |
| Form-Login (0) | 2025.03.04 |