1. 개요

Spring Security는 WebSecurityConfigurerAdapter 클래스 를 확장하여 엔드포인트 권한 부여 또는 인증 관리자 구성과 같은 기능에 대한 HTTP Security을 사용자 정의할 수 있습니다 . 그러나 최신 버전에서 Spring은 이 접근 방식을 더 이상 사용하지 않으며 구성 요소 기반 Security 구성을 권장합니다.

이 사용방법(예제)에서는 Spring Boot 애플리케이션에서 이 사용 중단을 대체하고 일부 MVC 테스트를 실행하는 방법을 알아봅니다.

2. WebSecurityConfigurerAdapter 가 없는 스프링 Security

우리는 일반적으로 WebSecurityConfigureAdapter 클래스 를 확장하는 Spring HTTP Security 구성 클래스를 봅니다.

그러나 버전 5.7.0-M2부터 Spring은 WebSecurityConfigureAdapter 의 사용을 더 이상 사용하지 않으며 WebSecurityConfigureAdapter 없이 구성을 생성할 것을 제안 합니다.

메모리 내 인증을 사용하여 이 새로운 유형의 구성을 보여 주는 예제 Spring Boot 애플리케이션을 만들어 보겠습니다.

먼저 구성 클래스를 정의합니다.

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class SecurityConfig {

    // config

}

다양한 역할을 기반으로 처리할 수 있도록 메서드 Security 어노테이션을 추가 합니다.

2.1. 인증 구성

WebSecurityConfigureAdapter 와 함께 AuthenticationManagerBuilder 를 사용하여 인증 컨텍스트를 설정합니다.

이제 지원 중단을 피하려면 UserDetailsManager 또는 UserDetailsService 구성 요소 를 정의할 수 있습니다 .

@Bean
public UserDetailsService userDetailsService(BCryptPasswordEncoder bCryptPasswordEncoder) {
    InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
    manager.createUser(User.withUsername("user")
      .password(bCryptPasswordEncoder.encode("userPass"))
      .roles("USER")
      .build());
    manager.createUser(User.withUsername("admin")
      .password(bCryptPasswordEncoder.encode("adminPass"))
      .roles("USER", "ADMIN")
      .build());
    return manager;
}

또는 UserDetailService 가 주어지면 AuthenticationManager 를 설정할 수도 있습니다 .

@Bean
public AuthenticationManager authenticationManager(HttpSecurity http, BCryptPasswordEncoder bCryptPasswordEncoder, UserDetailService userDetailService) 
  throws Exception {
    return http.getSharedObject(AuthenticationManagerBuilder.class)
      .userDetailsService(userDetailsService)
      .passwordEncoder(bCryptPasswordEncoder)
      .and()
      .build();
}

마찬가지로 JDBC 또는 LDAP 인증을 사용하는 경우에도 작동합니다.

2.2. HTTP Security 구성

더 중요한 것은 HTTP Security에 대한 지원 중단을 피하려면 SecurityFilterChain을 생성할 수 있다는 것 입니다.

예를 들어 역할에 따라 엔드포인트를 보호하고 로그인을 위한 익명 진입점만 남기고 싶다고 가정합니다. 또한 모든 삭제 요청을 관리자 역할로 제한합니다. 기본 인증 을 사용합니다 .

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http.csrf()
      .disable()
      .authorizeRequests()
      .antMatchers(HttpMethod.DELETE)
      .hasRole("ADMIN")
      .antMatchers("/admin/**")
      .hasAnyRole("ADMIN")
      .antMatchers("/user/**")
      .hasAnyRole("USER", "ADMIN")
      .antMatchers("/login/**")
      .anonymous()
      .anyRequest()
      .authenticated()
      .and()
      .httpBasic()
      .and()
      .sessionManagement()
      .sessionCreationPolicy(SessionCreationPolicy.STATELESS);

    return http.build();
}

HTTP Security은 DefaultSecurityFilterChain 개체를 빌드하여 요청 일치자와 필터를 로드합니다.

2.3. 웹 Security 구성

웹 Security을 위해 이제 콜백 인터페이스 WebSecurityCustomizer를 사용할 수 있습니다.

디버그 수준을 추가하고 이미지나 스크립트와 같은 일부 경로를 무시합니다.

@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
    return (web) -> web.debug(securityDebug)
      .ignoring()
      .antMatchers("/css/**", "/js/**", "/img/**", "/lib/**", "/favicon.ico");
}

3. 엔드포인트 컨트롤러

이제 애플리케이션에 대한 간단한 REST 컨트롤러 클래스를 정의합니다.

@RestController
public class ResourceController {
    @GetMapping("/login")
    public String loginEndpoint() {
        return "Login!";
    }

    @GetMapping("/admin")
    public String adminEndpoint() {
        return "Admin!";
    }

    @GetMapping("/user")
    public String userEndpoint() {
        return "User!";
    }

    @GetMapping("/all")
    public String allRolesEndpoint() {
        return "All Roles!";
    }

    @DeleteMapping("/delete")
    public String deleteEndpoint(@RequestBody String s) {
        return "I am deleting " + s;
    }
}

앞에서 HTTP Security을 정의할 때 언급한 것처럼 누구나 액세스할 수 있는 일반 /login Endpoints, 관리자 및 사용자를 위한 특정 Endpoints, 역할로 보호되지는 않지만 여전히 인증이 필요한 /all Endpoints을 추가할 것입니다.

4. 테스트 종점

엔드포인트를 테스트하기 위해 MVC 모형을 사용하여 Spring Boot Test 에 새 구성을 추가해 보겠습니다 .

4.1. 익명 사용자 테스트

익명 사용자는 /login 엔드포인트에 액세스할 수 있습니다. 다른 항목에 액세스하려고 하면 권한이 부여되지 않습니다( 401 ).

@Test
@WithAnonymousUser
public void whenAnonymousAccessLogin_thenOk() throws Exception {
    mvc.perform(get("/login"))
      .andExpect(status().isOk());
}

@Test
@WithAnonymousUser
public void whenAnonymousAccessRestrictedEndpoint_thenIsUnauthorized() throws Exception {
    mvc.perform(get("/all"))
      .andExpect(status().isUnauthorized());
}

또한 /login 을 제외한 모든 Endpoints의 경우 /all Endpoints 과 같이 항상 인증이 필요합니다 .

4.2. 테스트 사용자 역할

사용자 역할은 일반 엔드포인트와 이 역할에 대해 부여한 다른 모든 경로에 액세스할 수 있습니다.

@Test
@WithUserDetails()
public void whenUserAccessUserSecuredEndpoint_thenOk() throws Exception {
    mvc.perform(get("/user"))
      .andExpect(status().isOk());
}

@Test
@WithUserDetails()
public void whenUserAccessRestrictedEndpoint_thenOk() throws Exception {
    mvc.perform(get("/all"))
      .andExpect(status().isOk());
}

@Test
@WithUserDetails()
public void whenUserAccessAdminSecuredEndpoint_thenIsForbidden() throws Exception {
    mvc.perform(get("/admin"))
      .andExpect(status().isForbidden());
}

@Test
@WithUserDetails()
public void whenUserAccessDeleteSecuredEndpoint_thenIsForbidden() throws Exception {
    mvc.perform(delete("/delete"))
      .andExpect(status().isForbidden());
}

사용자 역할이 관리자 Security Endpoints에 액세스하려고 하면 사용자에게 "금지됨"( 403 ) 오류가 표시된다는 점에 유의해야 합니다.

반대로 이전 예의 익명과 같이 자격 증명이 없는 사람은 "권한 없음" 오류( 401 )를 받게 됩니다.

4.3. 테스트 관리자 역할

보시다시피 관리자 역할을 가진 사람은 모든 엔드포인트에 액세스할 수 있습니다.

@Test
@WithUserDetails(value = "admin")
public void whenAdminAccessUserEndpoint_thenOk() throws Exception {
    mvc.perform(get("/user"))
      .andExpect(status().isOk());
}

@Test
@WithUserDetails(value = "admin")
public void whenAdminAccessAdminSecuredEndpoint_thenIsOk() throws Exception {
    mvc.perform(get("/admin"))
      .andExpect(status().isOk());
}

@Test
@WithUserDetails(value = "admin")
public void whenAdminAccessDeleteSecuredEndpoint_thenIsOk() throws Exception {
    mvc.perform(delete("/delete").content("{}"))
      .andExpect(status().isOk());
}

5. 결론

이번 글에서는 WebSecurityConfigureAdapter 를 사용하지 않고 스프링 시큐리티 설정을 생성하고 , 인증, HTTP Security, 웹 Security을 위한 컴포넌트를 생성하면서 교체하는 방법을 알아보았습니다.

항상 그렇듯이 GitHub에서 작업 코드 예제를 찾을 수 있습니다 .

Security footer banner