Spring

Spring Boot, Spring Security란?

greenyellow-s 2025. 3. 12. 10:51
728x90
반응형

 

 

 

 

 

 

Spring Security

Spring 기반의 어플리케이션의 보안(인증과 권한)을 담당하는 프레임워크

인증, 인가 기능을 표준화하고 자동화 할 수 있어 개발자가 보안 관련 코드를 직접 작성하지 않고도 높은 수준의 보안 기능을 쉽게 구현할 수 있다.


Spring Security 주요 아키텍처

  Web Context Java Spring Context
Client HTTP Request Filter DispatcherServlet Controller 1
/login
  Controller 2
/main/index

 

Filter 단계에서 인증, 인가, 로깅 등을 수행하면 Java Spring 컨테이너에 도달하기 전에 보안 위협을 차단하여 시스템 안정성을 높일 수 있다.

 


1. Spring Security의 주요 개념

인증(Authentication)

사용자가 누구인지 확인하는 과정

  • 예) 로그인 (ID/PW), OAuth2, JWT 토큰 인증 등

인가(Authorization)

사용자가 특정 리소스에 접근할 수 있는지 확인하는 과정

  • 예) 관리자 페이지 접근 권한, 특정 API 요청 제한

필터(Filter) 기반 보안

  • Spring Security는 Filter 기반으로 동작
  • SecurityFilterChain을 사용하여 필터 체인을 구성

Spring Security 기본 설정

 

1. 의존성 추가

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

 

 

2. 설정 클래스 생성

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .csrf(AbstractHttpConfigurer::disable) // CSRF 보호 비활성화 (필요 시 활성화)
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/", "/public").permitAll()  // 특정 경로는 모두 접근 가능
                .requestMatchers("/admin").hasRole("ADMIN")   // 관리자만 접근 가능
                .anyRequest().authenticated()                 // 그 외 요청은 인증 필요
            )
            .formLogin(Customizer.withDefaults()) // 기본 로그인 폼 사용
            .logout(LogoutConfigurer::permitAll); // 로그아웃 허용

        return http.build();
    }
}

 

@Configuration : config 파일로 설정

@EnableWebSecurity : WebSecurity 활성화

 

- SecurityFilterchain 인터페이스 형으로 메소드를 생성

- 예외처리를 위한 throws Exception

- httpbuild하여 리턴


👀 authorizeHttpRequests()

.authorizeHttpRequests(auth -> auth
                .requestMatchers("/", "/public").permitAll()  // 특정 경로는 모두 접근 가능
                .requestMatchers("/admin").hasRole("ADMIN")   // 관리자만 접근 가능
                .anyRequest().authenticated()                 // 그 외 요청은 인증 필요
            )

 

특정 경로로 요청이 왔을 때 어떤 사용자에게 오픈하여 줄지 정할 수 있다.


** Spring Security 5.7 이하 : authorizeRequests()

    Spring Security 5.7 + : authorizeHttpRequest()

 

기본 형식 : (auth) -> auth.requestMatchers("/")

 .requestMatchers  메소드 안에 경로를 적으면 그 경로에 대해서 권한을 준다는 의미

 .anyRequest().authenticated()  나머지는 로그인 필요

 

 

권한 종류

 .permitAll()  : 모든 사용자에게 로그인 하지 않아도 접근 할 수 있도록 설정

 .hasRole()  : 특정한 role이 있어야 한다. 로그인을 한 뒤에 특정한 규칙이 있어야 경로로 접근 가능

 .authenticated()  : 로그인만 진행하면 모두 접근 가능

 .denyAll()  : 모든 사용자가 로그인을 진행해도 접근 못함

 

 

 

 

엔드 포인트 별 접근 권한 설정이 없는 경우, 

기본적으로 정보를 보호하기 때문에 로그인을 해야 접근 가능하도록 설정된다.

즉, 로그인 하지 않으면 모든 요청이 차단된다.

 

 

 

 

* .hasRole("권한")

.hasRole("ADMIN") → 내부적으로 "ROLE_ADMIN"을 의미한다.
.hasAuthority("ROLE_ADMIN") → 정확히 "ROLE_ADMIN"을 의미


DB에 저장된 역할이 "ROLE_XXX"라면 → hasRole("XXX") 사용
여러 역할을 허용하려면 hasAnyRole("ADMIN", "USER") 사용

 

 


👀  headers()

보안 관련 헤더 설정

 

단순히 @EnableWebSecurity를 클래스 레벨에 붙이는 것만으로 아무것도 안붙은 headers()가 자동으로 설정되며, 대부분의 경우 가장 좋은 보안설정이 디폴트로 되어 있다.

 

 

 

📌 기본 디폴트 (설정을 하지 않아도 자동으로 설정되어 있다.)

 

X-Frame-Options: DENY
→ 클릭재킹(Clickjacking) 방지를 위해 <iframe>로 페이지를 로드할 수 없게 함.

 

X-Content-Type-Options: nosniff
→ MIME 타입 스니핑 방지.

 

X-XSS-Protection

→ XSS공격을 필터링하는 기능

 

HTTP Strict-Transport-Security (HSTS)
→ HTTPS 강제 사용.

 

Cache-Control
→ 응답 데이터를 캐시하지 않도록 설정.

 

Content Type Options

→ niff기능을 실행하지 않도록하는 설정

 

 

 

 ✔️ 디폴트가 아닌 설정

 

Content-Security-Policy (CSP)
→ XSS(크로스 사이트 스크립팅) 공격 방지.

 

Referrer Policy

→ 유저가 직전에 어디 페이지에 있었는지 알려주는 설정

 

HTTP Public Key Pinning(HPKP)

→ 브라우저에게 특정 암호화 방식이 사용된 공개키가 특정 서버의 것이라는 것을 알려주는 설정

 

Custom Headers

→ 개발자가 직접 보안 관련 설정을 포함

 

 

보안 헤더 적용 비활성화 방법

모든 보안 헤더 비활성화

http.headers(AbstractHttpConfigurer::disable);

 

특정 보안 헤더만 비활성화

http.headers(headers -> headers
    .frameOptions(FrameOptionsConfig::disable)   // X-Frame-Options 제거 (iframe 허용)
    .contentTypeOptions(contentType -> contentType.disable())  // X-Content-Type-Options 제거
    .xssProtection(xss -> xss.disable()) // X-XSS-Protection 제거
    .httpStrictTransportSecurity(hsts -> hsts.disable()) // HSTS(HTTPS 강제) 제거
);

 

기본 형식 : .frameOptions().disable()

 


👀  CORS()

cors와 관련된 설정을 한다.

 

 

 

✔️ CORS (Cross-Origin Resource Sharing)

웹 브라우저에서 다른 도메인에 있는 리소스를 요청할 수 있도록 허용하는 보안 매커니즘

 

기본적으로, 웹 브라우저는 하나의 도메인에서 실행 중인 웹 애플리케이션이 다른 도메인으로 요청을 보낸는 것을 [같은 출처 정책]으로 제한한다. Cors는 이를 우회하는 방법을 제공한다.

 

 

 

✔️ 동작 방식

 

1. Preflight 요청,

       서버가 해당 요청을 처리할 수 있는지 확인 - Options 메서드 사용하여 Preflight 요청을 보냄

2. 응답 헤더,

       서버는 요청을 수락할 수 있는 여부를 결정하고 해당 요청을 허용할 수 있는지 응답 헤더에 정보를 담는다.

3. 응답,

       브라우저가 실제 요청을 보내면, 서버가 해당 요청을 허용하는 경우에만 데이터를 반환하고, 브라우저는 데이터를 사용한다.

 

 

 

✔️ 기본 형식

httpSecurity.
  cors(corsConfigurer -> 
    corsConfigurer.configurationSource(this.corsConfigurationSource()))

 

 

 

✔️ 기본적인 corsConfigurationSource 설정

@Bean
CorsConfigurationSource corsConfigurationSource() {
  CorsConfiguration configuration = new CorsConfiguration();
  configuration.setAllowedOrigins(Arrays.asList("https://example.com")); //같은 오리진 외에 어떤 오리진을 허용할 것인지.
  configuration.setAllowedMethods(Arrays.asList("GET","POST")); //어떤 메서드를 허용할 것인지
  UrlBasedCorsConfigurationSource source =new UrlBasedCorsConfigurationSource(); //어느 엔트리 포인트에 적용할 것인지
  source.registerCorsConfiguration("/**", configuration);
  
  return source;
  }
}

 

 

 

✔️ Spring MVC’s CORS support (컨트롤러에서 어노테이션 활용)

 

메서드에 붙이는 경우
requestMapping에 명시된 method만 허용 허용할 source를 명시할 수도 있고, 안하면 모든 소스를 허용합니다.

 @CrossOrigin //메서드 레벨에 명시
 @RequestMapping(Method = RequestMethod.GET)

 

클래스에 붙이는 경우
origins, methods, allowedHeaders, exposedHeaders, allowCredentials, maxAge 모두 설정할 수 있다

 


👀  sessionManagment()

세션 관리 관련 설정

 

 

✔️ 세션 생성 정책 설정 옵션


등록 형식

http.sessionManagement().sessionCreationPolicy([정책 enum 타입])

 

- ALWAYS : 항상 새로운 세션 생성

http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.ALWAYS);

 

- NEVER : 생성하지는 않지만, 이미 생성된 것이 있으면 사용

http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER);

 

- IF_REQUIRED : 필요한 경우에만 세션을 생성, 기본값으로 사용됨

http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);

 

- STATELESS : 생성하지 않고, 절대 사용하지 않음

http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

 

 

 

✔️ 최대 동일 세션 설정

 

동일한 사용자가 동시에 사용할 수 있는 세션의 최대 수를 제한할 수 있다.

사용자 계정으로 생성할 수 있는 최대 세션 수를 제한하고, 이를 초과하면 세션을 강제로 만료시키는 등의 설정을 할 수 있다.

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http
      .sessionManagement()
      .maximumSessions(1)  // 한 사용자가 최대 1개의 세션만 유지
      .maxSessionsPreventsLogin(true)  // 세션이 초과되면 새로 로그인하는 것을 방지
            .expiredUrl("/login?expired=true")  // 세션 만료 시 리다이렉트 URL 설정
      .and()
      .authorizeRequests()
            .antMatchers("/login").permitAll()  // 로그인 페이지는 모두 접근 가능
      .anyRequest().authenticated();  // 나머지 요청은 인증 필요
  }
}

 

이를 사용하려면 [SecurityConfig] 와 [ConcurrentSessionFilter]를 필요로 한다.

 

✔️ 둘다 포함된 설정 예시

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    // SessionRegistry 빈 정의
    @Bean
    public SessionRegistry sessionRegistry() {
        return new SessionRegistryImpl();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .sessionManagement()
                .maximumSessions(1)  // 한 사용자당 최대 1개의 세션만 허용
                .maxSessionsPreventsLogin(true)  // 세션 초과 시 새로운 로그인 방지
                .expiredUrl("/login?expired=true")  // 세션 만료 시 리다이렉트할 URL
                .sessionRegistry(sessionRegistry())  // SessionRegistry 설정
            .and()
            .authorizeRequests()
                .antMatchers("/login").permitAll()  // 로그인 페이지는 모두 접근 가능
                .anyRequest().authenticated();  // 나머지 요청은 인증 필요
    }

    // 로그인 이후 세션 관리 필터 추가 (보통 필터는 SecurityConfig에서 자동으로 처리됩니다)
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        super.configure(auth);
    }
}

 

 


👀 CSRF()

CSRF 공격 방어 기능

 

 

 

✔️ CSRF 공격

사용자가 인증된 상태에서 악의적으로 사이트가 해당 사용자를 가장하여 요청을 보내는 공격

사용자 인증 정보를 악용하여 사용자가 원하지 않는 작업을 대신 실행 할 수 있게 만든다.

 

이러한 공격을 방버하여 사용자가 의도하지 않은 요청을 방지할 수 있다.

 

 

 

✔️ 고려사항

- AJAX 요청, 클라이언트 측에서 AJAX 요청을 보내는 경우 CSRF 토큰을 포함시키는 것이 중요하다.

- Restful API 경우, 일반적으로 stateless 하므로, CSRF 를 비활성화하는 경우가 많다.

                               이 경우, API 토큰(JWT)을 사용하여 인증 및 권한 부여를 처리한다.

- 자동으로 Spring Security 가 CSRF 토큰을 처리해주므로 특별한 추가 작업이 필요하지 않다.

 

 

 

✔️ 설정 방법

@EnableWebSecurity 설정 시 자동으로 활성화 된다.

 

 

 

✔️ 비활성화 방법 (2가지)

1. csrf().disable()

2. csrf(AbstractHttpConfigurer::disable)

 

 

🔗 비활성화 구문, 두 개의 차이점

 

기능적으로는 동일하다.

1은 단순히 메서드 호출을 사용한 방식이고,

2는 AbstractHttpConfigurer의 disable 메서드를 통해 비활성화하는 방식 이다.

 

AbstractHttpConfigurer는 Spring Security에서 다양한 보안 설정을 위한 기본 클래스이므로,

이 방법은 좀 더 유연한 설정을 할 수 있는 기회를 제공한다.

 

하지만, 아무거나 사용해도 상관은 없다.

 

 


👀 formLogin()

주로 SSR 방식의 어플리케이션에서 Form 기반으로 로그인을 수행하는 경우 관련 설정을 할 때 사용한다.

하지만, 그렇지 않다면 disable하여 관련 필터 사용하지 않을 수 있다.

 

 

✔️ 형식

.formLogin(AbstractHttpConfigurer::disable)

 

 


👀 httpBasic()

username과 password를 요구하여, 서버에 저장된 것과 비교해서 인증하는 방법

 

credential이 암호화 되어 있지않고, 쿠키/세션/로긴 페이지를 사용하지 않기 때문에 보안이 취약하다.

disable()할 수 있다.

 


👀 addFilter()

커스텀한 필터를 추가한다.

 


👀 addFilterAfter()

 

✔️ 형식

addFilterAfter(추가할 필터 인스턴스, 추가할 기준이되는 필터 클래스정보.class)

 

두번째 인자를 기준으로 그 뒤에 필터를 추가합니다.

 


👀 apply()

외부에서 정의한 설정 클래스를 적용

 

✔️ 형식

http.apply(new CustomFilterConfiguerer())

 

 

 

728x90
반응형