### 스프링 시큐리티에 대한 설명

스프링 시큐리티(Spring Security)는 스프링 기반 애플리케이션에서 보안 기능을 제공하는 강력하고 유연한 프레임워크입니다. 인증(Authentication)과 인가(Authorization)를 비롯한 다양한 보안 요구사항을 처리하며, 웹 애플리케이션, REST API, 마이크로서비스 등에서 널리 사용됩니다. 스프링 부트와 통합 시 자동 설정 기능을 통해 최소한의 코드로 보안을 구현할 수 있다는 점이 큰 장점입니다. 아래에서 주요 개념, 동작 방식, 설정 방법 등을 설명합니다.

---

#### 1. 스프링 시큐리티의 주요 기능
- **인증(Authentication)**: 사용자가 누구인지 확인합니다 (예: 로그인).
- **인가(Authorization)**: 인증된 사용자가 어떤 작업을 수행할 수 있는지 권한을 확인합니다 (예: 관리자만 접근 가능).
- **보안 필터 체인**: HTTP 요청을 가로채 보안 로직을 적용합니다.
- **세션 관리**: 로그인 상태 유지 및 세션 공격 방지.
- **CSRF 방어**: 크로스 사이트 요청 위조 공격 방지.
- **비밀번호 암호화**: 안전한 비밀번호 저장을 위한 해시 알고리즘 제공.

---

#### 2. 스프링 시큐리티의 동작 원리
스프링 시큐리티는 **필터 기반 아키텍처**를 사용합니다. HTTP 요청이 들어오면 `FilterChain`을 통해 보안 필터들이 순차적으로 실행되며, 각 필터는 특정 보안 작업(인증, 인가 등)을 처리합니다.

- **핵심 컴포넌트**:
  - `SecurityFilterChain`: 요청을 처리하는 필터 체인을 정의.
  - `AuthenticationManager`: 인증 요청을 처리.
  - `UserDetailsService`: 사용자 정보를 로드 (예: DB에서 조회).
  - `PasswordEncoder`: 비밀번호 암호화 및 검증.

- **동작 흐름**:
  1. 사용자가 `/login`으로 로그인 요청.
  2. `UsernamePasswordAuthenticationFilter`가 요청을 가로채 인증 시도.
  3. `AuthenticationManager`가 `UserDetailsService`로 사용자 정보를 조회.
  4. 비밀번호 일치 여부 확인 후 인증 객체(`Authentication`) 생성.
  5. `SecurityContextHolder`에 인증 정보 저장.
  6. 이후 요청에서 인가 확인 후 접근 허용/차단.

---

#### 3. 스프링 부트에서 기본 설정
스프링 부트에 `spring-boot-starter-security` 의존성을 추가하면 기본 보안 설정이 활성화됩니다.

- **의존성 추가** (`pom.xml`):
  ```xml
  <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-security</artifactId>
  </dependency>
  ```

- **기본 동작**:
  - 모든 요청에 대해 인증 요구.
  - 기본 사용자 생성 (ID: `user`, 비밀번호: 실행 시 로그에 출력).
  - `/login` 폼 제공.

- **application.yaml로 기본 사용자 설정**:
  ```yaml
  spring:
    security:
      user:
        name: admin
        password: password123
  ```

---

#### 4. 커스터마이징 예시
스프링 시큐리티를 커스터마이징하려면 `SecurityFilterChain`을 정의하는 설정 클래스를 작성합니다.

- **예시**: 기본 인증 및 인가 설정
  ```java
  @Configuration
  @EnableWebSecurity
  public class SecurityConfig {

      @Bean
      public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
          http
              .authorizeHttpRequests(auth -> auth
                  .requestMatchers("/public/**").permitAll()   // 공개 경로
                  .requestMatchers("/admin/**").hasRole("ADMIN") // 관리자만 접근
                  .anyRequest().authenticated()                // 나머지는 인증 필요
              )
              .formLogin(form -> form
                  .loginPage("/login")  // 커스텀 로그인 페이지
                  .permitAll()
              )
              .logout(logout -> logout
                  .logoutUrl("/logout")
                  .logoutSuccessUrl("/login?logout")
              );
          return http.build();
      }

      @Bean
      public UserDetailsService userDetailsService() {
          UserDetails user = User.withUsername("user")
              .password(passwordEncoder().encode("pass"))
              .roles("USER")
              .build();
          UserDetails admin = User.withUsername("admin")
              .password(passwordEncoder().encode("admin"))
              .roles("ADMIN")
              .build();
          return new InMemoryUserDetailsManager(user, admin);
      }

      @Bean
      public PasswordEncoder passwordEncoder() {
          return new BCryptPasswordEncoder();
      }
  }
  ```
  - **설명**:
    - `/public/**`: 인증 없이 접근 가능.
    - `/admin/**`: "ADMIN" 역할 필요.
    - 그 외: 인증된 사용자만 접근.
    - 메모리 내 사용자 정의 (실제로는 DB 연동 추천).

---

#### 5. 데이터베이스 연동
실제 애플리케이션에서는 `UserDetailsService`를 구현해 데이터베이스에서 사용자 정보를 가져옵니다.

- **예시**:
  ```java
  @Service
  public class CustomUserDetailsService implements UserDetailsService {
      private final UserRepository userRepository;

      public CustomUserDetailsService(UserRepository userRepository) {
          this.userRepository = userRepository;
      }

      @Override
      public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
          UserEntity user = userRepository.findByUsername(username)
              .orElseThrow(() -> new UsernameNotFoundException("User not found: " + username));
          return User.withUsername(user.getUsername())
              .password(user.getPassword())
              .roles(user.getRole())
              .build();
      }
  }
  ```
  - `SecurityConfig`에 주입:
    ```java
    @Bean
    public UserDetailsService userDetailsService(CustomUserDetailsService customUserDetailsService) {
        return customUserDetailsService;
    }
    ```

---

#### 6. REST API와 JWT
REST API에서는 폼 로그인 대신 JWT(JSON Web Token)를 사용해 stateless 인증을 구현할 수 있습니다.

- **구현 개요**:
  1. `/login`에서 사용자 인증 후 JWT 발급.
  2. 클라이언트가 `Authorization` 헤더에 JWT 포함.
  3. `JwtAuthenticationFilter`로 요청마다 토큰 검증.

- **필터 예시**:
  ```java
  public class JwtAuthenticationFilter extends OncePerRequestFilter {
      @Override
      protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
              throws ServletException, IOException {
          String token = request.getHeader("Authorization");
          // JWT 검증 로직 추가
          chain.doFilter(request, response);
      }
  }
  ```

---

#### 7. 주요 어노테이션
- `@EnableWebSecurity`: 시큐리티 설정 활성화.
- `@PreAuthorize`, `@PostAuthorize`: 메서드 수준에서 인가 확인.
  - 예: `@PreAuthorize("hasRole('ADMIN')")`
- `@Secured`: 역할 기반 접근 제어.

---

#### 8. 모범 사례
- **비밀번호 암호화**: `BCryptPasswordEncoder` 사용.
- **CSRF 비활성화**: REST API에서는 `http.csrf().disable()` 설정.
- **HTTPS**: 프로덕션에서 보안 통신 강제.
- **에러 처리**: 인증/인가 실패 시 커스텀 응답 제공.

---

### 결론
스프링 시큐리티는 강력한 보안 기능을 제공하며, 스프링 부트와의 통합으로 설정이 간편합니다. 기본 설정으로 빠르게 시작할 수 있고, 커스터마이징을 통해 복잡한 요구사항도 충족할 수 있습니다. 웹 애플리케이션부터 REST API, OAuth2 통합까지 다양한 시나리오를 지원하므로, 보안이 중요한 애플리케이션에서 필수적인 도구입니다. 추가 질문이 있다면 언제든 물어보세요!




---




스프링 시큐리티와 관련된 주요 어노테이션과 클래스들을 표로 정리하겠습니다. 스프링 시큐리티는 인증(Authentication)과 인가(Authorization)를 관리하기 위한 다양한 어노테이션과 클래스를 제공하며, 이를 통해 보안 설정을 코드 수준에서 유연하게 적용할 수 있습니다.

---

### 1. 스프링 시큐리티 관련 어노테이션

| **어노테이션**             | **설명**                                                                                   | **사용 예시**                                   |
|---------------------------|-------------------------------------------------------------------------------------------|------------------------------------------------|
| `@EnableWebSecurity`      | 스프링 시큐리티를 활성화하고, 웹 보안 설정을 커스터마이징할 수 있게 함                      | `@EnableWebSecurity public class SecurityConfig` |
| `@EnableGlobalMethodSecurity` | 메서드 수준 보안을 활성화 (예: `@PreAuthorize`, `@Secured` 등 사용 가능)                  | `@EnableGlobalMethodSecurity(prePostEnabled = true)` |
| `@PreAuthorize`           | 메서드 실행 전에 권한을 확인 (SpEL 지원)                                                 | `@PreAuthorize("hasRole('ADMIN')")`            |
| `@PostAuthorize`          | 메서드 실행 후 반환값을 기반으로 권한 확인                                               | `@PostAuthorize("returnObject.owner == authentication.name")` |
| `@Secured`                | 특정 역할(Role)이 있는 사용자만 메서드 실행 허용                                          | `@Secured("ROLE_ADMIN")`                       |
| `@RolesAllowed`           | JSR-250 표준 어노테이션으로 `@Secured`와 유사한 역할 기반 인가                             | `@RolesAllowed("ROLE_USER")`                   |
| `@AuthenticationPrincipal`| 현재 인증된 사용자의 `Principal` 객체를 메서드 파라미터로 주입                            | `@GetMapping("/me") public String getUser(@AuthenticationPrincipal UserDetails user)` |
| `@EnableMethodSecurity`   | 메서드 보안을 개별적으로 활성화 (Spring 6.1 이상에서 `@EnableGlobalMethodSecurity` 대체)   | `@EnableMethodSecurity`                        |

- **참고**:
  - `@PreAuthorize`와 `@PostAuthorize`는 Spring Expression Language(SpEL)를 지원해 복잡한 조건을 정의할 수 있습니다.
  - `@Secured`와 `@RolesAllowed`는 단순 역할 체크에 적합하지만, SpEL은 지원하지 않습니다.

---

### 2. 스프링 시큐리티 관련 주요 클래스

| **클래스**                    | **설명**                                                                                   | **주요 사용처**                                |
|------------------------------|-------------------------------------------------------------------------------------------|-----------------------------------------------|
| `SecurityFilterChain`        | HTTP 요청을 처리하는 보안 필터 체인을 정의                                                | `HttpSecurity` 설정에서 반환                  |
| `HttpSecurity`               | HTTP 보안 설정을 구성 (인증, 인가, CSRF, 로그인 등)                                       | `SecurityFilterChain` 구성                    |
| `Authentication`             | 인증된 사용자의 정보를 나타내는 인터페이스                                                | `SecurityContext`에서 가져옴                   |
| `AuthenticationManager`      | 인증 요청을 처리하고 `Authentication` 객체를 반환                                         | 사용자 인증 로직                               |
| `UserDetails`                | 사용자 정보를 나타내는 인터페이스 (ID, 비밀번호, 권한 등)                                  | `UserDetailsService`에서 반환                 |
| `UserDetailsService`         | 사용자 정보를 로드하는 인터페이스 (DB 연동 시 구현 필요)                                   | 커스텀 사용자 조회 로직                        |
| `PasswordEncoder`            | 비밀번호를 암호화하고 검증하는 인터페이스                                                 | `BCryptPasswordEncoder` 구현체 사용           |
| `SecurityContext`            | 현재 인증 상태를 저장하는 컨텍스트                                                       | `SecurityContextHolder`에서 관리              |
| `SecurityContextHolder`      | 현재 스레드의 보안 컨텍스트를 관리 (인증 정보 접근)                                        | `Authentication` 객체 가져오기                |
| `UsernamePasswordAuthenticationToken` | 사용자 이름과 비밀번호 기반 인증 토큰                                            | 로그인 인증 시 생성                           |
| `AccessDeniedException`      | 권한 부족으로 접근이 거부될 때 발생하는 예외                                              | 인가 실패 처리                                |
| `AuthenticationEntryPoint`   | 인증 실패 시 응답을 처리하는 인터페이스 (예: 401 반환)                                     | 커스텀 로그인 실패 처리                       |
| `OncePerRequestFilter`       | 요청당 한 번만 실행되는 필터를 구현하기 위한 추상 클래스                                   | JWT 인증 필터 구현                            |
| `InMemoryUserDetailsManager` | 메모리 내에서 사용자 정보를 관리하는 `UserDetailsService` 구현체                           | 테스트용 사용자 설정                          |
| `WebSecurityCustomizer`      | 웹 보안 설정을 커스터마이징 (특정 경로 무시 등)                                            | 정적 리소스 보안 제외                         |

---

### 추가 설명

#### 어노테이션 사용 팁
- **`@EnableWebSecurity`**: 필수 설정으로, `SecurityFilterChain` 빈을 정의할 때 사용됩니다.
- **`@PreAuthorize` vs `@Secured`**: `@PreAuthorize`는 조건이 복잡할 때, `@Secured`는 단순 역할 체크에 적합합니다.
- **`@AuthenticationPrincipal`**: 컨트롤러에서 현재 로그인한 사용자를 쉽게 가져올 수 있어 편리합니다.

#### 클래스 활용 예시
- **HttpSecurity 설정**:
  ```java
  @Bean
  public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
      http
          .authorizeHttpRequests(auth -> auth
              .requestMatchers("/admin/**").hasRole("ADMIN")
              .anyRequest().authenticated()
          )
          .formLogin();
      return http.build();
  }
  ```

- **UserDetailsService 구현**:
  ```java
  @Service
  public class CustomUserDetailsService implements UserDetailsService {
      @Override
      public UserDetails loadUserByUsername(String username) {
          // DB에서 사용자 조회 로직
          return new User(username, "{bcrypt}hashedPassword", Arrays.asList(new SimpleGrantedAuthority("ROLE_USER")));
      }
  }
  ```

- **PasswordEncoder 설정**:
  ```java
  @Bean
  public PasswordEncoder passwordEncoder() {
      return new BCryptPasswordEncoder();
  }
  ```

---

### 결론
스프링 시큐리티의 어노테이션은 설정과 메서드 수준 보안을 간편하게 적용할 수 있게 해주며, 관련 클래스들은 인증/인가 로직을 세밀하게 제어할 수 있도록 돕습니다. 이를 조합하면 웹 애플리케이션부터 REST API까지 다양한 보안 시나리오를 구현할 수 있습니다. 추가적인 질문이나 구체적인 예제가 필요하면 말씀해주세요!




---




### HttpSecurity 클래스의 메서드 정리와 예시 설명

스프링 시큐리티에서 `HttpSecurity` 클래스는 HTTP 요청에 대한 보안 설정을 구성하는 핵심 클래스입니다. `SecurityFilterChain`을 생성할 때 사용되며, 인증(Authentication), 인가(Authorization), 로그인, 로그아웃, CSRF 보호 등 다양한 보안 기능을 메서드 체이닝 방식으로 설정할 수 있습니다. 아래에서 주요 메서드를 표로 정리하고, 예시와 함께 설명하겠습니다.

---

### 1. HttpSecurity 주요 메서드 표

| **메서드**                     | **설명**                                                                                   |
|-------------------------------|-------------------------------------------------------------------------------------------|
| `authorizeHttpRequests()`     | HTTP 요청에 대한 인가 규칙을 정의 (경로별 접근 제어 설정)                                   |
| `formLogin()`                 | 폼 기반 로그인 설정 (로그인 페이지, 성공/실패 처리 등)                                      |
| `httpBasic()`                 | HTTP 기본 인증 설정 (클라이언트가 사용자 이름과 비밀번호를 헤더로 전송)                     |
| `logout()`                    | 로그아웃 설정 (로그아웃 URL, 성공 후 리다이렉션 등)                                         |
| `csrf()`                      | CSRF(Cross-Site Request Forgery) 보호 설정 (활성화/비활성화)                                |
| `cors()`                      | CORS(Cross-Origin Resource Sharing) 설정                                                  |
| `sessionManagement()`         | 세션 관리 설정 (세션 생성 정책, 최대 세션 수 등)                                            |
| `exceptionHandling()`         | 인증/인가 예외 처리 설정 (접근 거부, 인증 실패 시 동작)                                      |
| `headers()`                   | HTTP 헤더 보안 설정 (X-Frame-Options, XSS 보호 등)                                         |
| `requestMatchers()`           | 특정 요청 경로에만 보안 설정 적용                                                          |
| `anonymous()`                 | 익명 사용자에 대한 설정 (익명 사용자의 권한 정의)                                           |
| `oauth2Login()`               | OAuth2 기반 로그인 설정 (소셜 로그인 등)                                                   |
| `addFilter()`                 | 커스텀 필터를 필터 체인에 추가                                                            |
| `addFilterBefore()`           | 기존 필터 앞에 커스텀 필터 추가                                                            |
| `addFilterAfter()`            | 기존 필터 뒤에 커스텀 필터 추가                                                            |
| `authenticationProvider()`    | 커스텀 인증 제공자(AuthenticationProvider)를 추가                                          |
| `accessDeniedHandler()`       | 인가 실패(403) 시 호출되는 핸들러 설정                                                     |
| `and()`                       | 메서드 체이닝에서 이전 설정을 종료하고 새로운 설정 섹션으로 이동                           |

---

### 2. HttpSecurity 설정 예시와 설명

#### 기본 설정 예시
다음은 일반적인 웹 애플리케이션의 보안 설정 예시입니다.

```java
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/public/**").permitAll()      // 공개 경로
                .requestMatchers("/admin/**").hasRole("ADMIN")  // 관리자만 접근
                .anyRequest().authenticated()                   // 나머지 인증 필요
            )
            .formLogin(form -> form
                .loginPage("/login")           // 커스텀 로그인 페이지
                .defaultSuccessUrl("/home")    // 로그인 성공 시 이동
                .permitAll()                   // 로그인 페이지는 누구나 접근 가능
            )
            .logout(logout -> logout
                .logoutUrl("/logout")          // 로그아웃 요청 URL
                .logoutSuccessUrl("/login?logout") // 로그아웃 후 이동
            )
            .exceptionHandling(ex -> ex
                .accessDeniedPage("/access-denied") // 인가 실패 시 이동 페이지
            )
            .csrf(csrf -> csrf
                .disable()                    // CSRF 비활성화 (테스트용)
            );

        return http.build();
    }

    @Bean
    public UserDetailsService userDetailsService() {
        UserDetails user = User.withUsername("user")
            .password("{noop}password") // {noop}은 암호화 없이 사용
            .roles("USER")
            .build();
        return new InMemoryUserDetailsManager(user);
    }
}
```

#### 메서드별 설명
1. **`authorizeHttpRequests()`**
   - **설명**: 요청 경로별로 접근 권한을 설정합니다. `requestMatchers()`로 경로를 지정하고, `permitAll()`, `hasRole()`, `authenticated()` 등으로 권한을 정의합니다.
   - **예시**: `/public/**`은 누구나 접근 가능, `/admin/**`은 "ROLE_ADMIN" 역할 필요.

2. **`formLogin()`**
   - **설명**: 폼 기반 로그인을 활성화하고, 로그인 페이지와 성공/실패 동작을 커스터마이징합니다.
   - **예시**: `/login` 페이지로 이동하며, 성공 시 `/home`으로 리다이렉션.

3. **`logout()`**
   - **설명**: 로그아웃 요청을 처리하고, 성공 후 이동할 경로를 설정합니다.
   - **예시**: `/logout` 요청으로 로그아웃 후 `/login?logout`으로 이동.

4. **`exceptionHandling()`**
   - **설명**: 인증/인가 실패 시 동작을 정의합니다. `accessDeniedPage()`로 403 오류 페이지를 지정할 수 있습니다.
   - **예시**: 권한 없는 사용자가 접근 시 `/access-denied`로 이동.

5. **`csrf()`**
   - **설명**: CSRF 보호를 설정합니다. REST API에서는 보통 비활성화(`disable()`)하지만, 웹 폼에서는 활성화 권장.
   - **예시**: `csrf().disable()`로 비활성화 (REST API 환경에서 유용).

---

#### REST API용 설정 예시 (CSRF 비활성화 및 HTTP Basic)
REST API에서는 폼 로그인 대신 HTTP Basic 인증을 사용할 수 있습니다.

```java
@Configuration
@EnableWebSecurity
public class RestSecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/public/**").permitAll()
                .anyRequest().authenticated()
            )
            .httpBasic(httpBasic -> httpBasic
                .realmName("My API")  // Basic 인증 영역 이름
            )
            .csrf(csrf -> csrf.disable())  // REST API는 CSRF 비활성화
            .sessionManagement(session -> session
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 세션 사용 안 함
            );

        return http.build();
    }
}
```

- **`httpBasic()`**: 클라이언트가 `Authorization: Basic base64(username:password)` 헤더로 인증.
- **`sessionManagement()`**: 세션 생성 정책을 `STATELESS`로 설정해 RESTful 특성 유지.

---

#### 커스텀 필터 추가 예시
JWT 기반 인증을 위해 커스텀 필터를 추가할 수 있습니다.

```java
public class JwtAuthenticationFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {
        String token = request.getHeader("Authorization");
        // JWT 검증 로직
        chain.doFilter(request, response);
    }
}

@Configuration
@EnableWebSecurity
public class JwtSecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .anyRequest().authenticated()
            )
            .addFilterBefore(new JwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
            .csrf(csrf -> csrf.disable());

        return http.build();
    }
}
```

- **`addFilterBefore()`**: `UsernamePasswordAuthenticationFilter` 앞에 JWT 필터 추가.

---

### 3. 결론
`HttpSecurity`의 메서드는 스프링 시큐리티에서 HTTP 요청 보안을 세밀하게 제어할 수 있게 해줍니다. `authorizeHttpRequests()`로 경로별 권한을 설정하고, `formLogin()`, `httpBasic()`, `logout()` 등으로 인증 방식을 정의하며, `csrf()`, `sessionManagement()`으로 추가 보안 옵션을 조정할 수 있습니다. 예시를 통해 웹 애플리케이션과 REST API 모두에 적용 가능한 설정을 확인할 수 있으며, 필요에 따라 커스텀 필터를 추가해 확장성도 확보할 수 있습니다. 추가 질문이 있다면 언제든 물어보세요!