[스프링 시큐리티] SecurityConfig

jjuya·2023년 12월 21일
0
post-thumbnail

Spring Security 환경 설정을 구성하기 위한 클래스이다.


/**
 * Spring Security 환경 설정을 구성하기 위한 클래스
 * 웹 서비스가 로드 될때 Spring Container 의해 관리가 되는 클래스
 * 사용자에 대한 '인증', '인가'에 대한 구성을 Bean 메서드로 주입함
 *
 */


@Slf4j
@RequiredArgsConstructor
@Configuration // ** 현재 클래스를 (설정 클래스)로 설정
public class SecurityConfig {

    @Bean
    public PasswordEncoder passwordEncoder(){
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }


    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {

        return authenticationConfiguration.getAuthenticationManager();
        /* BCrypt : 기본으로 사용. 가장 많이 사용되는 알고리즘.
         * SCrypt : 개발자가 직접 필요에 따라 변경 가능.
         * Argon2
         * PBKDF2
         * MD5
         * SHA-1, SHA-256 등
         */
    }

    public class CustomSecurityFilterManager extends AbstractHttpConfigurer<CustomSecurityFilterManager, HttpSecurity>{

        @Override
        public void configure(HttpSecurity httpSecurity) throws Exception {

            AuthenticationManager authenticationManager = httpSecurity.getSharedObject(
                    AuthenticationManager.class
            );

            httpSecurity.addFilter(new JwtAuthenticationFilter(authenticationManager));

            super.configure(httpSecurity);
        }
    }

    /*
     * HTTP에 대해서 '인증'과 '인가'를 담당하는 메서드
     * 필터를 통해 인증 방식과 인증 절차에 대해서 등록하며 설정을 담당하는 메서드
     * */

    @Bean // 스프링 빈으로 등록
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        // 1. CSRF 해제 - 서버에 인증정보를 저장하지 않기때문에
        http.csrf().disable(); // postman 접근해야 함!! - CSR 할때!!

        // 2. iframe 거부 설정
        http.headers().frameOptions().sameOrigin();

        // 3. cors 재설정
        http.cors().configurationSource(configurationSource());

        // 4. jSessionId 사용 거부
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); // 세션 정책

        // 5. form 로긴 해제 (UsernamePasswordAuthenticationFilter 비활성화) (폼 로그인 비활성화)
        http.formLogin().disable();

        // 6. 로그인 인증창이 뜨지 않게 비활성화(기본 인증 비활성화)
        http.httpBasic().disable(); //bearer 방식으로

        // 7. 커스텀 필터 적용 (시큐리티 필터 교환) 커스텀 필터 적용
        http.apply(new CustomSecurityFilterManager());

        // 8. 인증 실패 처리
        http.exceptionHandling().authenticationEntryPoint((request, response, authException) -> {
            log.warn("인증되지 않은 사용자가 자원에 접근하려 합니다 : " + authException.getMessage());
            FilterResponseUtils.unAuthorized(response, new Exception401("인증되지 않았습니다"));
        });

        // 9. 권한 실패 처리
        http.exceptionHandling().accessDeniedHandler((request, response, accessDeniedException) -> {
            log.warn("권한이 없는 사용자가 자원에 접근하려 합니다 : " + accessDeniedException.getMessage());
            FilterResponseUtils.forbidden(response, new Exception403("권한이 없습니다"));
        });

        // 10. 인증, 권한 필터 설정 - 경로에 대한 인증 설정
        http.authorizeRequests(
                authorize -> authorize
                        .antMatchers("/carts/**", "/options/**", "/orders/**")
                        .authenticated()

                        .antMatchers("/admin/**")
                        .access("hasRole('ADMIN')")
                        // ("/admin/**")에 대한 요청은 ADMIN권한을 가진 회원만 승인한다.
                        // 회원 권한을 설정할때 , 반드시 "ROLE_"을 붙여야만 Security가 인식함
                        .anyRequest().permitAll() //다른 주소는 모두 허용
                // 모든 요청에 대해 인증을 요구 하지 않는다
                // 위에서 설정한 특정 경로에 대한 권한 인증 확인만 하고 다른 경로는 확인하지 않는다
        );

        // 12. 로그아웃 관련 설정 (이 부분 추가)
        http.logout()
                .logoutUrl("/logout")
                .logoutSuccessHandler((request, response, authentication) -> {
                    response.sendRedirect("/");
                })
                .deleteCookies("jwtToken");

        return http.build();
    }

    // ** 규칙: 헤더(Authorization), 메서드, IP 주소, 클라이언트으 쿠키 요청을 허용
    public CorsConfigurationSource configurationSource() {
        CorsConfiguration corsConfigurationSource = new CorsConfiguration();
        corsConfigurationSource.addAllowedHeader("*"); // 모든 헤더를 허용
        corsConfigurationSource.addAllowedMethod("*"); // GET, POST, PUT, DELETE 등의 모든 메서드를 허용
        corsConfigurationSource.addAllowedOriginPattern("*"); // 모든 IP주소를 허용
        corsConfigurationSource.setAllowCredentials(true); // 클라이언트 쿠키 요청 허용
        corsConfigurationSource.addExposedHeader("Authorization"); // 헤더

        UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource
                = new UrlBasedCorsConfigurationSource();

        // ** (/) 들어오는 모든 유형의 URL 패턴을 허용.
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfigurationSource);
        return urlBasedCorsConfigurationSource;
    }
}
  1. @Configuration: 이 클래스가 구성 클래스임을 나타내는 어노테이션

  2. passwordEncoder() : 패스워드 인코더를 빈으로 등록

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }
  3. 인증 매니저 빈으로 등록
    : 실질적으로는 AuthenticationManager에 등록된 AuthenticationProvider에 의해 인증이 처리됨

  1. CustomSecurityFilterManager
    : AbstractHttpConfigurer를 확장하여 커스텀 보안 필터 매니저를 정의 함
  1. SecurityFilterChain 빈
    : SecurityFilterChain을 사용하여 전반적인 보안 설정을 구성함
    : HttpSecurity 객체를 사용하여 다양한 구성이 적용
  • http.csrf().disable();
    : CSRF 비활성화

  • http.headers().frameOptions().sameOrigin();
    : iframe 거부 설정

  • http.cors().configurationSource(configurationSource());
    : cors 설정

  • http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    : 세션을 사용하지 않음

  • http.formLogin().disable();
    : 폼 로그인 비활성화

  • http.httpBasic().disable();
    : 기본 HTTP 인증 비활성화

  • http.apply(new CustomSecurityFilterManager());
    : 커스텀 필터 적용

  • 인증 및 권한 설정

      http.authorizeRequests(
              authorize -> authorize
                      .antMatchers("/petsitter/**","/carts/**", "/options/**", "/orders/**").authenticated()
                      .antMatchers("/admin/**").access("hasRole('ADMIN')")
                      .anyRequest().permitAll()
      );
    

    .antMatchers("").authenticated(): 해당 경로는 인증이 필요함
    .antMatchers("/admin/**").access("hasRole('ADMIN')") : 해당 경로는 ADMIN권한을 가진 사용자만 접근 가능
    그외 모든 사용자에게 허용

  • 인증 및 권한 실패 처리

    http.exceptionHandling().authenticationEntryPoint((request, response, authException) -> {
        // 인증 실패 처리
    });
    
    http.exceptionHandling().accessDeniedHandler((request, response, accessDeniedException) -> {
        // 권한 실패 처리
    });

    : 인증 실패 및 권한 실패 시의 처리를 정의
    : 인증 실패 시 401응답을 보내고, 권한 실패시 403응답을 보냄

  • 로그아웃 관련

    http.logout()
           .logoutUrl("/logout")
           .logoutSuccessHandler((request, response, authentication) -> {
               // 로그아웃 성공 시 처리
           })
           .deleteCookies("jwtToken");

    : 로그아웃 URL을 "/logout"으로 지정하고, 로그아웃 성공 시 처리 및 쿠키 삭제를 설정

  1. CORS 구성 메서드
public CorsConfigurationSource configurationSource() {
        CorsConfiguration corsConfigurationSource = new CorsConfiguration();
        corsConfigurationSource.addAllowedHeader("*"); // 모든 헤더를 허용
        corsConfigurationSource.addAllowedMethod("*"); // GET, POST, PUT, DELETE 등의 모든 메서드를 허용
        corsConfigurationSource.addAllowedOriginPattern("*"); // 모든 IP주소를 허용
        corsConfigurationSource.setAllowCredentials(true); // 클라이언트 쿠키 요청 허용
        corsConfigurationSource.addExposedHeader("Authorization"); // 헤더

        UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource
                = new UrlBasedCorsConfigurationSource();

        // ** (/) 들어오는 모든 유형의 URL 패턴을 허용.
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfigurationSource);
        return urlBasedCorsConfigurationSource;
    }

: CORS(Cross-Origin Resource Sharing) 설정을 정의함
: 모든 헤더, 메서드, IP 주소, 클라이언트 쿠키 요청을 허용하도록 설정

CORS(Cross-Origin Resource Sharing)
웹 페이지에서 실행중인 스크립트가 다른 도메인에 있는 리소스에 접근하는 권한을 부여하는 보안기술
동일 출처 정책(same-Origin Policy)에 따라 다른 도메인에서 온 요청에 대한 보안을 강화 함

profile
Review the Record⭐

0개의 댓글