새소식

반응형
Spring

[Spring Security] 스프링 시큐리티 권한처리 하기

  • -
반응형

스프링 시큐리티 인증 및 권한처리 개요

스프링에서 시큐리티를 활용한 인증 권한을 거치면 해당 사용자는 위 사진과 같은 과정을 통해 권한을 얻게 된다.

 

인증 / 인가 과정 중에 SecurityContextHolder 내부에 인증 객체를 저장하는데 이 인증 객체 내부에서 일반적으로 사용되는 UserDetails getAuthorities()를 통해 해당 유저의 권한을 확인 할 수 있다.

 

코드를 통해서도 아래와 같은 방식으로 권한을 확인할 수 있다.

if (authentication.getAuthorities().contains(new SimpleGrantedAuthority("ROLE_ADMIN"))) {
    redirectStrategy.sendRedirect(request, response, "/admin");
}

 

하지만 모든 api에서 위와 같이 인증 객체를 하나하나 검사하는 것은 매우 비효율적인 코드이다. 시큐리티에서 이를 어떠한 방법들로 처리할 수 있는지 알아보았다.

 

WebSecurityConfigurerAdapter을 활용한 권한 처리

스프링 시큐리티에서는  WebSecurityConfigurerAdapter를 상속받아 해당 권한을 처리할 수 있다. Spring Boot 2.7 이하에서는 WebSecurityConfigurerAdapter를 사용하고, Spring Boot 3.0 이후부터는 SecurityFilterChain@Bean을 사용한다.

 

Spring Boot 2.7 이하: WebSecurityConfigurerAdapter 사용

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/admin/**").hasRole("ADMIN")
                .antMatchers("/user/**").hasRole("USER")
                .anyRequest().authenticated()
            .and()
            .formLogin()
                .loginPage("/login")
                .permitAll()
            .and()
            .logout()
                .permitAll();
    }
    
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/resources/**", "/static/**");
    }
}

 

 

Spring Boot 3.0 이상: SecurityFilterChain 사용

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(authorize -> authorize
                .requestMatchers("/admin/**").hasRole("ADMIN")
                .requestMatchers("/user/**").hasRole("USER")
                .anyRequest().authenticated()
            )
            .formLogin(form -> form
                .loginPage("/login")
                .permitAll()
            )
            .logout(logout -> logout
                .permitAll()
            );

        return http.build();
    }
}

 

 

 

접근제한 메서드

 

메서드 체이닝을 통해 접근제한을 더 세부적으로 처리 할 수 있다. 각 메서드는 특정 상황에 맞게 접근 권한을 설정할 수 있도록 도와준다.

 

메서드 설명 예제코드
antMatchers 특정 URL 패턴에 대한 접근 권한을 설정합니다. 패턴 매칭은 기본적으로 ANT 스타일입니다. .antMatchers("/admin/**").hasRole("ADMIN")
requestMatchers antMatchers와 유사하지만, 더 다양한 매처를 사용할 수 있습니다. Spring Boot 3.0 이상에서 사용됩니다. .requestMatchers("/admin/**").hasRole("ADMIN")
anyRequest 모든 요청에 대해 권한을 설정합니다. 주로 기본 설정을 위해 사용됩니다. .anyRequest().authenticated()
permitAll 모든 사용자가 접근할 수 있도록 설정합니다. .permitAll()
denyAll 모든 사용자의 접근을 차단합니다. .denyAll()
authenticated 인증된 사용자만 접근할 수 있도록 설정합니다. .authenticated()
fullyAuthenticated 완전 인증된 사용자만 접근할 수 있도록 설정합니다. 보통 Remember Me 기능을 사용하지 않은 경우에 사용됩니다. .fullyAuthenticated()
anonymous 익명 사용자만 접근할 수 있도록 설정합니다. .anonymous()
hasRole 특정 역할을 가진 사용자만 접근할 수 있도록 설정합니다. .hasRole("ADMIN")
hasAnyRole 지정된 역할 중 하나라도 가지고 있는 사용자가 접근할 수 있도록 설정합니다. .hasAnyRole("ADMIN", "USER")
hasAuthority 특정 권한을 가진 사용자만 접근할 수 있도록 설정합니다. .hasAuthority("ROLE_ADMIN")
hasAnyAuthority 지정된 권한 중 하나라도 가지고 있는 사용자가 접근할 수 있도록 설정합니다. .hasAnyAuthority("ROLE_ADMIN", "ROLE_USER")

 

 

@Secured를 활용한 메서드별 권한 처리

 

@Secured 어노테이션은 메서드 수준의 보안 처리를 간단하게 할 수 있는 방법 이다. 이 어노테이션은 특정 역할을 가진 사용자만 메서드에 접근할 수 있도록 제한한다.

먼저 Security 설정파일에서 메서드 보안처리를 활성화 한 후 메서드에 @Secured 어노테이션을 적용함으로써 권한 처리를 할 수 있다.

 

1. 메서드 보안처리 활성화

@Configuration
@EnableMethodSecurity(securedEnabled = true)
public class MethodSecurityConfig {
}

 

 

2. Secured 어노테이션 적용

@Secured("ROLE_USER")
@GetMapping()
public ResponseEntity<Map<String, Object>> getMyInfo(Authentication authentication) {

    int userId = ((CustomUserDetails)authentication.getPrincipal()).getUser().getUserId();

    Map<String, Object> resultMap = new HashMap<>();
    MyInfoResponseDto user = userService.selectMyInfo(userId);
    resultMap.put("msg", "내 정보 조회에 성공하였습니다.");
    resultMap.put("data", user);
    return ResponseEntity.ok().body(resultMap);
}

 

  • 역할이름은 기본적으로 ‘ROLE_’ 접두사를 사용한다.
  • 다중 역할 설정: 여러 역할을 설정할 경우, 배열 형태로 @Secured({"ROLE_USER", "ROLE_ADMIN"}) 과 같이 설정할 수 있다.

 

 

@PreAuthorize, @PostAuthorize

 

@Secured는 사용하기에 편리하지만, 복잡한 권한 처리에 사용하기 어렵다는 점이 존재한다. 이에 SpEL(Spring Expression Language)를 사용하여 복잡한 권한처리가 가능한 @PreAuthorize@PostAuthorize를 활용할 수 있다.

 

@PreAuthorize

메서드 호출 전에 접근 권한을 검증한다.

@Service
public class ExampleService {

    @PreAuthorize("hasRole('ADMIN')")
    public String adminMethod() {
        return "Admin access granted.";
    }

    @PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
    public String userMethod() {
        return "User or Admin access granted.";
    }

    @PreAuthorize("#username == authentication.name")
    public String userMethodWithParam(String username) {
        return "Access granted for user: " + username;
    }
}

 

 

@PostAuthorize

메서드 호출 후에 접근 권한을 검증한다. 주로 반환 값을 기준으로 접근 권한을 검증하는 데에 사용한다.

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;

@Service
public class ExampleService {
	@PreAuthorize("hasRole('ADMIN')")
	public String adminMethod() {
	    return "Admin access granted.";
	}
	
	@PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
	public String userMethod() {
	    return "User or Admin access granted.";
	}
	
	@PreAuthorize("#username == authentication.name")
	public String userMethodWithParam(String username) {
	    return "Access granted for user: " + username;
	}
}

 

 

정리

 

특징 SecurityConfig 권한 설정 @Secured 어노테이션 활용
중앙 집중 관리 모든 보안 설정을 한 곳에서 관리 가능 권한 설정이 여러 클래스/메서드에 분산될 수 있음
유연성 다양한 HTTP 보안 설정과 URL 패턴에 대한 전역 설정 가능 특정 메서드에 대해 세밀한 권한 제어 가능
메서드 수준 제어 메서드 수준의 세밀한 제어가 어려움 메서드에 직접 권한 설정 가능
간결함 설정이 길어지고 복잡해질 수 있음 간단한 역할 기반 접근 제어를 쉽게 설정 가능
포괄적인 설정 로그인, 로그아웃, 예외 처리, 세션 관리 등 다양한 보안 관련 설정을 통합적으로 관리 가능 복잡한 보안 규칙 설정에 한계가 있음
→ @PreAuthorize, @PostAuthorize 사용
유지보수 설정이 복잡해질 경우 관리가 어려울 수 있음 보안 설정이 코드에 분산되어 있어 유지보수가 어려울 수 있음

 

결과적으로 URL단위로 권한처리를 하는 경우는 Configuration을 활용한 방법이 적합할 것이며, 개별 API마다 각각 적용시켜야하는 경우면 @Secured@PreAuthorize, @PostAuthorize를 사용하는게 적합할 것이다.

보통은 "/admin", "/user" 와 같이 URL단위로 사용하는 경우가 드물어서 일반적으로 어노테이션을 활용하겠지만, 프로젝트마다 상황이 모두 다르니 함께 활용하거나 각각에 맞게 프로젝트에 적합한 방식을 선택하여 적용시켜 나가야 한다. 

반응형
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.