사이드 프로젝트/CafeMate프로젝트(SpringBoot)

10주차 CafeMate 시큐리티 인증 구현

EVO. 2023. 5. 9. 21:21
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-security', version: '3.0.5'

시큐리티를 구현하기 위해 먼저 MVN Repository에서 위의 코드를 따와 의존성 주입을 하였습니다.

 

현재 저희가 진행 중인 스프링부트 버전은 3.0.5 버전으로 2.7+ 버전에서부터는 Spring Security의 WebSecurityConfigureAdapter를 통해 security config를 override 할 때 오류가 발생합니다. 따라서 아쉽게도 제가 알고 있던 방식으로는 더 이상 진행할 수 없기에 이참에 새로운 버전인 시큐리티를 공부하며 인증구현을 하도록 하겠습니다.

 


비교

다음은 HTTP로 모든 엔드포인트를 보호하는 WebSecurityConfigurerAdapter를 사용한 구성 예시입니다.

@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests((authz) -> authz
                .anyRequest().authenticated()
            )
            .httpBasic(withDefaults());
    }

}

필터 체인 빈을 등록하는 현재 보안 방식입니다.

@Configuration
public class SecurityConfiguration {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests((authz) -> authz
                .anyRequest().authenticated()
            )
            .httpBasic(withDefaults());
        return http.build();
    }

}

기존의 방식과 다른 점은  SecurityFilterChain을 반환한다는 점과 빈으로 등록함으로써 컴포넌트 기반의 보안 설정이 가능해집니다.

 

그리고 나머지는 원래 했던 방식으로 하면 됩니다

 


이 코드를 활용하려고 했지만 이번엔 antMatchers 가 더 이상 사용할 수 없다는 에러가 생깁니다

@Configuration
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity
                .httpBasic().disable()
                .csrf().disable()
                .cors().and()
                .authorizeRequests()
                .antMatchers("/api/v1/users/join", "/api/v1/users/login").permitAll() // join, login은 언제나 가능
                .antMatchers(HttpMethod.GET, "/api/**").permitAll() // 모든 get요청 허용
                .antMatchers(HttpMethod.POST, "/api/**").authenticated() // 모든 post요청을 인증된사용자인지 check
                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS) // jwt사용하는 경우 씀
                .and()
                .build();
                /*.and()
                .addFilterBefore(new JwtTokenFilter(userService, secretKey), UsernamePasswordAuthenticationFilter.class) //UserNamePasswordAuthenticationFilter적용하기 전에 JWTTokenFilter를 적용 하라는 뜻 입니다.
                .build();*/
    }

}

 


antMatchers()와 mvcMathcers() 및 regexMatchers()는 Spring Security 6.0에서 더 이상 사용되지 않으며 제거되었습니다 

 

https://github.com/spring-projects/spring-framework/issues/28552

 

Deprecate trailing slash match and change default value from true to false · Issue #28552 · spring-projects/spring-framework

Whether to match to URLs irrespective of the presence of a trailing slash. If enabled a method mapped to "/users" also matches to "/users/". The default value is true. Even though this behavior has...

github.com

 

변경의 근거는 다음 예시 코드를 보면 알 수가 있습니다.

 @Bean
    SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity
                .authorizeHttpRequests(requests -> {
                    requests.antMatchers("/resources").hasRole("admin");
                    requests.antMatchers("/resources/**").hasRole("user");
                    requests.anyRequest().denyAll();
                })
                .httpBasic(Customizer.withDefaults())
                .build();
    }

기본 사용자(role user)는 GET/resources를 시도하면 403을 받지만 후행 슬래시 일치를 비활성화하면 GET/resources/를 실행하여 보호를 회피할 수 있습니다. 아무튼 antMatchers 역시 사용할 수가 없습니다.


antMatchers 대신에 requestMatchers를 이용하면 됩니다. 아래는 예시입니다.

@Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception{
        httpSecurity.authorizeHttpRequests()
                .requestMatchers("/user/**")
                .authenticated()
                .anyRequest()
                .permitAll();

        return httpSecurity.build();


    }

특정 url에는 권한이 필요하고 로그인, 가입페이지, 메인페이지, 물품페이지 등은 권한을 필요로 하지 않게 하기 위해서는 api작성이 필요해 보입니다. api작성은 다음 글에서 작성해보도록 하겠습니다.

 

 

접은 글 출처: https://fenderist.tistory.com/411

더보기
  • authenticated() ;  인증된 사용자의 접근을 허용
  • fullyAuthenticated(): 인증된 사용자의 접근을 허용,  rememberMe인증 제외
  • permitAll(): 무조건 허용
  • denyAll(): 무조건 차단
  • anonymous(): 익명사용자 허용
  • rememberMe(): rememberMe 인증 사용자 접근 허용
  • access(String): 주어진 SpEL표현식의 평가 결과가 true 이면 접근허용
  • hasRole(String): 사용자가 주어진 역할이 있다면 접근을 허용
  • hasAuthority(String): 사용자가 주어진 권한이 있다면 허용
  • hasAnyRole(String...): 사용자가 주어진 어떤권한이라도 있으면 허용
  • hasAnyAuthority(String...): 사용자가 주어진 권한중 어떤 것이라도 있다면 허용
  • hasIpAddress(String): 주어진 IP로 부터 요청이 왔다면 접근을 허용

 

이건 곧 지울 글

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    // 사용자 인증 처리를 위한 UserDetailsService 빈 등록
    @Bean
    public UserDetailsService userDetailsService() {
        // 여기서 사용자 정보를 로딩하는 DAO 등 구현체를 등록
        // 예시로 InMemoryUserDetailsManager를 사용합니다.
        UserDetails user = User.builder()
                                .username("user")
                                .password("{noop}password")
                                .roles("USER")
                                .build();
                                
        UserDetails admin = User.builder()
                                .username("admin")
                                .password("{noop}password")
                                .roles("ADMIN")
                                .build();

        return new InMemoryUserDetailsManager(user, admin);
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/admin/**").hasRole("ADMIN") // 관리자 전용 리소스를 특정 역할만 허용하도록 설정
                .antMatchers(HttpMethod.PUT, "/orders/**").hasRole("ADMIN") // 관리자만 주문 상태를 수정할 수 있도록 설정
                .antMatchers(HttpMethod.DELETE, "/orders/**").hasRole("ADMIN") // 관리자만 주문을 삭제할 수 있도록 설정
                .antMatchers(HttpMethod.POST, "/users/**/orders").hasRole("USER") // 회원만 상품을 구매할 수 있도록 설정
                .antMatchers("/user/**").authenticated() // 기존 코드대로 /user/** 인증된 사용자만 접근 가능하도록 설정
                .anyRequest().permitAll() // 이외의 모든 요청은 누구나 접근을 허용
                .and()
            .httpBasic(); // HTTP Basic 인증을 사용

        return http.build();
    }
}