[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단위로 사용하는 경우가 드물어서 일반적으로 어노테이션을 활용하겠지만, 프로젝트마다 상황이 모두 다르니 함께 활용하거나 각각에 맞게 프로젝트에 적합한 방식을 선택하여 적용시켜 나가야 한다.
'Spring' 카테고리의 다른 글
헥사고날 아키텍처 with SpringBoot (0) | 2024.09.23 |
---|---|
[스프링 부트 + 시큐리티 + REST] STOMP를 통한 채팅 구현(JWT를 통한 사용자 인증) (0) | 2024.05.29 |
스프링 시큐리티 + Github Oauth + RESTAPI + JWT 활용(With Gradle) (0) | 2024.01.29 |
[Spring] 롬복을 사용한 Builder 패턴 Null체크 하며 사용하기 (0) | 2023.11.10 |
우아한 객체지향 [우아한테크세미나] 정리 5 (0) | 2023.11.08 |
소중한 공감 감사합니다