Spring security 다중 provider 적용

freddie·2021년 4월 25일
2

spring

목록 보기
3/4
post-custom-banner

Spring프로젝트에서 2가지 방식으로 인증을 해야하는 경우가 있었다.
root path를 구분지어서 /server 로 들어오는 요청은 appkey라는 파라미터를 통해 인증을 진행하고 싶었고,
/api로 들어오는 요청은 JWT토큰을 통해 사용자 인증을 진행하고 싶었다.

Spring security란?

먼저 Spring security가 뭔지 간단히 알아보자.

  • spring security는 spring에서 제공하는 보안/인증과 관련된 모듈이다.
  • spring security는 Filter레이어에서 동작하는데, 원래라면 Filter레이어는 spring의 컨텍스트 바깥에 위치한다.
  • spring security는 필터 내부에서 spring context와 관련된 기능을 편하게 사용하기위해 DelegatingFilterProxy를 사용한다.

기본 설정

@Configuration
@EnableWebSecurity
class WebSecurityConfig : WebSecurityConfigurerAdapter() {
    override fun configure(http: HttpSecurity) {
        http.authorizeRequests()
            .antMatchers("/home").permitAll()
            .anyRequest().authenticated()
            .and()
        .httpBasic()
    }

    @Bean
    override fun userDetailsService(): UserDetailsService {
        val user = User.withUsername("user")
            .password("{bcrypt}\$2a\$10\$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG")
            .roles("USER")
            .build()

        return InMemoryUserDetailsManager(user)
    }
}

@EnableWebSecurity어노테이션을 통해서 Spring security에 필요한 설정들을 활성화 할 수 있고, 세부적인 설정들을 구현하려면 WebSecurityConfigurerAdapter를 상속받아서 각 기능에 맞는 메서드를 구현하면 된다.

configure(HttpSecurity)메서드를 통해서 path별로 어떤 권한/인증을 적용할지 설정할 수 있다.

UserDetailsService는 입력받은 인증정보를 기반으로 사용자 정보를 가져오는 역할을 하는 서비스인데, 여기서는 테스트용이기 때문에 메모리에 사용자정보를 미리 만들어놓는 InMemoryUserDetailsManager를 사용했다.

Multi Provider 적용하기

사실 어려운 부분은 없이 WebSecurityConfigurer를 두번 정의해주면 된다.
다만 custom filter를 정의하면서 문제가 있었는데, filter를 bean으로 등록했더니 spring에서 자동으로 필터를 등록하면서 문제가 되었었다.
filter를 bean으로 등록하지 말자

@Configuration
@EnableWebSecurity
class WebSecurityConfig {
    @Order(1)
    @Configuration
    class InternalSecurityConfiguration: WebSecurityConfigurerAdapter() {
        override fun configure(http: HttpSecurity) {
            http
                .antMatcher("/server")
                .csrf().disable()
                .formLogin().disable()
                .httpBasic().disable()
                .addFilterBefore(serverTokenAuthenticationFilter(), UsernamePasswordAuthenticationFilter::class.java)
                .authenticationProvider(preAuthProvider())
                .authorizeRequests()
                .anyRequest().authenticated()
        }
    }

    @Order(2)
    @Configuration
    class SecurityConfiguration : WebSecurityConfigurerAdapter() {
        override fun configure(http: HttpSecurity) {
            http
                .requestMatchers { requestMatchers ->
                    requestMatchers
                        .antMatchers("/api")
                }
                .cors()
                .and()
                .csrf().disable()
                .formLogin().disable()
                .httpBasic().disable()
                .addFilterBefore(jwtTokenAuthenticationFilter(), UsernamePasswordAuthenticationFilter::class.java)
                .authenticationProvider(jwtAuthProvider())
                .authorizeRequests()
                .anyRequest().authenticated()
        }
    }
}

참고

profile
하루에 하나씩만 배워보자
post-custom-banner

0개의 댓글