Book/스프링부트 핵심가이드

스프링 시큐리티(Spring Security) 구현(of. SecurityConfiguration)

블로그 주인장 2023. 12. 6.

스프링 시큐리티 구현

스프링 시큐리티와 관련된 설정 방법에 대해 알아보겠습니다.


SecurityConfiguration 구현


SecurityConfiguration 클래스 [HttpSecurity]

@Configuration
@RequiredArgsConstructor
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    private final JwtTokenProvider jwtTokenProvider;

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity.httpBasic().disable()
                .csrf().disable()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeHttpRequests()
                    .antMatchers("/sign-api/sign-in", "/sign-api/sign-up",
                    	"/sign-api/exception").permitAll()
                    .antMatchers(HttpMethod.GET, "/product/**").permitAll()
                    .antMatchers("**exception**").permitAll()
                    .anyRequest().hasRole("ADMIN")
                .and()
                    .exceptionHandling().accessDeniedHandler(new CustomAccessDeniedHandler())
                .and()
                    .exceptionHandling().authenticationEntryPoint(new CustomAuthenticationEntryPoint())
                .and()
                    .addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider),
                    	UsernamePasswordAuthenticationFilter.class);
    }
}
  • HttpSecurity를 설정하는 Configure 메서드이고, 스프링 시큐리티의 설정은 대부분 HttpSecurity를 통해 진행이 된다
  • HttpSecurity의 대표적인 기능
    1. 리소스 접근 권한 설정
    2. 인증 실패 시 발생하는 예외 처리
    3. 인증 로직 커스터마이징
    4. csrf, cors 등의 스프링 시큐리티 설정
  • configure 메서드에 작성되어 있는 코드의 설정별 설명
    1. httpBasic( ).disable( )
      • UI를 사용하는 것을 기본 값으로 가진 시큐리티의 설정을 비활성화 한다.
    2. csrf( ).disable( )
      • REST API에서는 CSRF 보안이 필요 없기 때문에 비활성화 하는 로직이다.
      • 스프링 시큐리티에의 csrf( ) 메서드는 기본적으로 CSRF 토큰을 발급해서 클라이언트로부터 요청을 받을 때마다 토큰을 검증하는 방식으로 동작한다.
      • 브라우저 사용 환경이 아니라면 비활성화해도 크게 문제가 되지 않는다.
    3. sessionManagement( ).sessionCreationPolicy(SessionCreationPolicy.STATELESS)
      • REST API 기반의 애플리케이션의 동작 방식을 설정
      • 현재 프로젝트에서는 JWT 토큰으로 인증 처리하며, 세션은 사용하지 않기에 STATELESS로 설정한다.
    4. authorizeRequest( )
      • 애플리케이션에 들어오는 요청에 대한 사용 권한을 체크한다.
      • antMatchers( ) 메서드는 antPattern을 통해 권한을 설정하는 역할을 한다.
      • "/sign-api/sign-in", "/sign-api/sign-up", "/sign-api/exception" 경로에 대해서는 모두에게 허용한다.
      • "/product" 로 시작하는 경로의 GET 요청은 모두 허용한다
      • "exception" 단어가 들어간 경로는 모두 허용한다.
      • 기타 요청은 인증된 권한을 가진 사용자에게 허용한다.
    5. exceptionHandling( ).accessDeniedHandler( )
      • 권한을 확인하는 과정에서 통과하지 못하는 예외가 발생할 경우 예외를 전달한다.
    6. exceptionHandling( ).auithenticationEntryPoint( )
      • 인증 과정에서 예외가 발생할 경우 예외를 전달한다.
    7. addFilterBefore( )
      • 어느 필터 앞에 추가할 것인지 설정
      • 현재의 설정은 스프링 시큐리티에서 인증을 처리하는 필터인 UsernamePasswordAuthenticationFilter 앞에 기존에 생성했던 JwtAuthenticationFitler를 추가하겠다는 의미이다.

 

SecurityConfiguration 클래스 [WebSecurity]

@Configuration
@RequiredArgsConstructor
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    private final JwtTokenProvider jwtTokenProvider;

    //** 생략
    
    @Override
    public void configure(WebSecurity webSecurity) {
        webSecurity.ignoring().antMatchers("/v2/api-docs", "/swagger-resources/**",
                "/swagger-ui.html","/webjars/**", "/swagger/**", "sign-api/exception");
    }
}
  • WebSecurity는 HttpSecurity 앞단에 적용되며, 전체적으로 스프링시큐리 영향권 밖에 있다.
  • 즉, 인증과 인가가 모두 적용되기 전에 동작하는 설정이다.
  • 다양한 곳에서 사용되지 않고 인증과 인가가 적용되지 않는 리소스 접근에 대해서만 사용한다.
  • [예시] Swagger에 적용되는 인증과 인가를 피하기 위해 ignoring( ) 메서드를 사용하여 Swagger 관련 경로에 대한 예외 처리를 수행한다.(인증과 인가를 무시하는 경로로 설정한 것이다)

 

커스텀 AccessDeniedHandler, AuthenticationEntryPoint 구현


CustomAccessDeniedHandler 클래스

@Component
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
    private final Logger LOGGER = LoggerFactory.getLogger(CustomAccessDeniedHandler.class);
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response,
                       AccessDeniedException accessDeniedException)
            throws IOException {
        
        LOGGER.info("[handle] 접근이 막혔을 경우 경로 리다이렉트");
        response.sendRedirect("/sign-api/exception");
    }
}
  • 기본적으로 handle( ) 메서드를 오버라이딩 하여 구현한다.
  • AccessDeniedException 은 엑세스 권한이 없는 리소스에 접근할 경우 발생하는 예외이다.
  • 예외를 처리하기 위해 AccessDeniedHandler 인터페이스가 사용되고,SecurityConfiguration에 exceptionHandling( ) 메서드를 통해 추가하였다.
  • 예시로 response 에서 리다이렉트하는 sendRedirect( ) 메서드를 활용하는 방식으로 구현이 되었다.

 

CustomAuthenticationEntryPoint 클래스

public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
    private final Logger LOGGER = LoggerFactory.getLogger(CustomAuthenticationEntryPoint.class);

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response,
                         AuthenticationException authException) throws IOException {

        ObjectMapper objectMapper = new ObjectMapper();
        LOGGER.info("[commence] 인증 실패로 response.sendError 발생");

        EntryPointErrorResponse errorResponse = new EntryPointErrorResponse();
        errorResponse.setMsg("인증에 실패하였습니다.");
        
        response.setStatus(401);
        response.setContentType("application/json");
        response.setCharacterEncoding("utf-8");
        response.getWriter().write(objectMapper.writeValueAsString(errorResponse));
    }
}
  • commence( ) 메서드는 httpServletRequest, HttpServletResponse, AuthenticationException을 매개변수로 받는다.
  • 해당 예시는 예외 처리를 위해 리다이렉트가 아니라 직접 Response를 생성해서 클라이언트에게 응답받는 방식이다.
  • 직접 Response 를 생성해서 클라이언트에게 응답하는 방식으로 구현되어 있다
    1. 메시지를 담기 위해 EntryPointErrorResponse 객체를 만들어 메시지를 설정
    2. response 에 상태 코드(status)와 콘텐츠 타입(content-type) 등을 설정
    3. ObjectMapper를 사용하여 EntryPointErrorResponse 객체를 바디 값으로 파싱한다.

 

EntryPointErrorResponse 클래스

@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class EntryPointErrorResponse {
    private String msg;
}

 

 

CustomAuthenticationEntryPoint 클래스 [응답 코드 설정]

public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
    private final Logger LOGGER = LoggerFactory.getLogger(CustomAuthenticationEntryPoint.class);

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response,
                         AuthenticationException authException) throws IOException {

        response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
    }
}
  • commence( ) 메서드 내부에 인증 실패 코드만 전달하는 방식이다.
반응형

댓글