🍀Spring/Security

스프링 시큐리티

말동말동현 2025. 4. 26. 15:16

스프링 실습을 하다 시큐리티부분이 궁금해져서 적어두게 되었다.

 

1. SecurityConfig 클래스

이 클래스는 Spring Security의 주요 설정을 담당합니다.

package com.shop.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.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        return http
                .csrf(csrf -> csrf
                        .csrfTokenRepository(new CookieCsrfTokenRepository())   // csrf 토큰 저장소를 http only cookie로 설정
                )
                .addFilterAfter(new CsrfCookieFilter(), BasicAuthenticationFilter.class)
                .authorizeHttpRequests(authorizeHttpRequestsCustomizer -> authorizeHttpRequestsCustomizer
                        .requestMatchers("/css/**", "/js/**", "/img/**").permitAll()
                        .requestMatchers("/", "/members/**", "/item/**", "/images/**").permitAll()
                        .requestMatchers("/admin/**").hasRole("ADMIN")
                        .anyRequest()
                        .authenticated()
                ).formLogin(formLoginCustomizer -> formLoginCustomizer
                        .loginPage("/members/login")
                        .defaultSuccessUrl("/", true)
                        .usernameParameter("email")
                        .failureHandler(new FormLoginAuthenticationFailureHandler())
                ).logout( logoutCustomizer -> logoutCustomizer
                        .logoutRequestMatcher(new AntPathRequestMatcher("/members/logout"))
                        .logoutSuccessUrl("/")
                ).exceptionHandling(e -> e
                        .authenticationEntryPoint(new CustomAuthenticationEntryPoint())
                )
                .build()
                ;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

 

  • @Configuration: Spring의 설정 클래스임을 나타냅니다.
  • @EnableWebSecurity: Spring Security 기능을 활성화합니다.
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

 

 

이 메서드는 Spring Security의 필터 체인을 구성합니다. 모든 보안 설정이 여기서 이루어집니다.

 

CSRF 설정

.csrf(csrf -> csrf
        .csrfTokenRepository(new CookieCsrfTokenRepository())   // csrf 토큰 저장소를 http only cookie로 설정
)
.addFilterAfter(new CsrfCookieFilter(), BasicAuthenticationFilter.class)

 

  • CSRF(Cross-Site Request Forgery) 공격을 방지하기 위한 설정입니다.
  • CookieCsrfTokenRepository를 사용하여 CSRF 토큰을 HTTP Only 쿠키에 저장합니다.
  • CsrfCookieFilter를 BasicAuthenticationFilter 뒤에 추가하여 CSRF 토큰 처리를 합니다.

URL 접근 권한 설정

.authorizeHttpRequests(authorizeHttpRequestsCustomizer -> authorizeHttpRequestsCustomizer
        .requestMatchers("/css/**", "/js/**", "/img/**").permitAll()
        .requestMatchers("/", "/members/**", "/item/**", "/images/**").permitAll()
        .requestMatchers("/admin/**").hasRole("ADMIN")
        .anyRequest()
        .authenticated()
)

 

 

  • 정적 리소스와 특정 경로는 모든 사용자에게 공개됩니다 (permitAll()).
  • /admin/** 경로는 ADMIN 역할을 가진 사용자만 접근 가능합니다.
  • 그 외 모든 요청은 인증된 사용자만 접근 가능합니다.

로그인 설정

.formLogin(formLoginCustomizer -> formLoginCustomizer
        .loginPage("/members/login")
        .defaultSuccessUrl("/", true)
        .usernameParameter("email")
        .failureHandler(new FormLoginAuthenticationFailureHandler())
)

 

 

  • 커스텀 로그인 페이지를 /members/login으로 지정합니다.
  • 로그인 성공 시 메인 페이지(/)로 리다이렉트합니다.
  • 사용자 이름 파라미터를 email로 설정합니다 (기본값은 username).
  • 로그인 실패 처리를 위한 핸들러를 설정합니다.

로그아웃 설정

.logout( logoutCustomizer -> logoutCustomizer
        .logoutRequestMatcher(new AntPathRequestMatcher("/members/logout"))
        .logoutSuccessUrl("/")
)

 

 

  • 로그아웃 URL을 /members/logout으로 설정합니다.
  • 로그아웃 성공 시 메인 페이지로 리다이렉트합니다.

예외 처리 설정

.exceptionHandling(e -> e
        .authenticationEntryPoint(new CustomAuthenticationEntryPoint())
)

 

  • 인증되지 않은 사용자가 인증이 필요한 리소스에 접근할 때 처리 방식을 정의합니다.

패스워드 인코더 빈

@Bean
public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}
  • 비밀번호 암호화를 위한 BCrypt 알고리즘을 사용하는 인코더를 제공합니다.

 

2. CsrfCookieFilter 클래스

public class CsrfCookieFilter extends OncePerRequestFilter {
  • OncePerRequestFilter를 상속하여 요청당 한 번만 실행되는 필터를 구현합니다.
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    // HttpServletRequest에서 CsrfToken 속성 조회
    CsrfToken csrfToken = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
    if (csrfToken != null) {
        // csrf 토큰 강제 생성
        csrfToken.getToken();
    }
    filterChain.doFilter(request, response);
}
  • 요청에서 CSRF 토큰을 검색하고, 존재하면 getToken() 메서드를 호출하여 토큰을 강제로 생성합니다.
  • 이렇게 함으로써 쿠키에 CSRF 토큰이 저장되도록 합니다.
  • 필터 체인을 계속 진행하기 위해 filterChain.doFilter()를 호출합니다.

 

 

3. CustomAuthenticationEntryPoint 클래스

public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
  • AuthenticationEntryPoint 인터페이스를 구현하여 인증되지 않은 사용자가 보호된 리소스에 접근할 때의 동작을 정의합니다.
 
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
    response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
}

 

  • 인증되지 않은 사용자가 보호된 리소스에 접근하면 401 Unauthorized 오류를 반환합니다.
  • "Unauthorized" 메시지와 함께 HTTP 상태 코드 401을 응답으로 보냅니다.

 

4. FormLoginAuthenticationFailureHandler 클래스

public class FormLoginAuthenticationFailureHandler implements AuthenticationFailureHandler {
  • AuthenticationFailureHandler 인터페이스를 구현하여 로그인 실패 시의 동작을 정의합니다.
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
    response.sendRedirect("/members/login/error");
}

 

 

  • 로그인 실패 시 /members/login/error 경로로 리다이렉트합니다.
  • 실패 원인은 전달하지 않고 단순히 오류 페이지로 이동시킵니다.

 

 

이 설정들은 함께 작동하여 웹 애플리케이션의 보안을 관리하고, 사용자 인증과 권한 부여, CSRF 방지, 오류 처리 등의 기능을 제공합니다.