Spring Security에서 AuthorizationDeniedEvent와 AuthorizationGrantedEvent는 권한 확인(Authorization) 과정에서 발생하는 이벤트를 처리하는 기능을 제공합니다. 이를 통해 권한 승인 여부에 따라 특정 로직을 실행하거나 추가 작업을 수행할 수 있습니다. 이를 구현하는 방법에 대해 단계별로 자세히 설명드리겠습니다.
Spring Security에서는 권한 확인 시, 다음과 같은 두 가지 주요 이벤트를 발생시킬 수 있습니다.
AuthorizationDeniedEvent
AuthorizationGrantedEvent
Spring Security의 AuthorizationEventPublisher는 이러한 이벤트를 발행하는 역할을 합니다. 이를 활성화하기 위해 Spring의 기본 구현체인 SpringAuthorizationEventPublisher를 등록합니다.
@Bean
public AuthorizationEventPublisher authorizationEventPublisher
(ApplicationEventPublisher applicationEventPublisher) {
return new SpringAuthorizationEventPublisher(applicationEventPublisher);
}
ApplicationEventPublisher 주입
ApplicationEventPublisher를 사용합니다.SpringAuthorizationEventPublisher 생성
ApplicationEventPublisher를 통해 발행하도록 설계되어 있습니다.@Bean으로 등록
AuthorizationEventPublisher는 Spring 컨텍스트에 Bean으로 등록되어야 합니다. 이를 통해 Spring Security가 권한 확인 중 이벤트를 자동으로 발행합니다.이벤트를 수신하려면 Spring의 @EventListener를 활용해야 합니다. 이를 통해 권한 관련 이벤트가 발생했을 때 사용자 정의 로직을 실행할 수 있습니다.
@Component
public class AuthenticationEvents {
@EventListener
public void onFailure(AuthorizationDeniedEvent failure) {
// 권한 거부 시 수행할 로직
System.out.println("Authorization Denied: " + failure);
}
@EventListener
public void onSuccess(AuthorizationGrantedEvent success) {
// 권한 승인 시 수행할 로직
System.out.println("Authorization Granted: " + success);
}
}
@Component 등록
@EventListener 어노테이션
AuthorizationDeniedEvent와 AuthorizationGrantedEvent를 각각 처리하는 두 메서드를 정의했습니다.이벤트 처리 로직
onFailure 메서드: 권한 거부 시 동작할 로직을 구현합니다.onSuccess 메서드: 권한 승인 시 동작할 로직을 구현합니다.Spring Security가 권한을 확인하는 과정에서 특정 리소스 접근이 거부되거나 승인되면, 위에서 정의한 이벤트 리스너가 호출됩니다.
예를 들어:
1. 사용자가 /admin 리소스에 접근.
2. @PreAuthorize("hasRole('ADMIN')")로 권한 제한 설정.
3. 권한 거부 시:
AuthorizationDeniedEvent 발생 → onFailure 메서드 실행.AuthorizationGrantedEvent 발생 → onSuccess 메서드 실행.커스텀 로직 추가
테스트 환경 설정
성능 최적화
@Configuration
public class SecurityConfig {
@Bean
public AuthorizationEventPublisher authorizationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
return new SpringAuthorizationEventPublisher(applicationEventPublisher);
}
}
@Component
public class AuthenticationEvents {
@EventListener
public void onFailure(AuthorizationDeniedEvent failure) {
System.out.println("Authorization Denied: " + failure);
// 추가 로직 작성
}
@EventListener
public void onSuccess(AuthorizationGrantedEvent success) {
System.out.println("Authorization Granted: " + success);
// 추가 로직 작성
}
}
이렇게 설정하면 권한 관련 이벤트를 처리할 수 있는 유연한 시스템을 구축할 수 있습니다.
이 코드는 Spring Security에서 권한 확인이 성공했을 때 발생하는 AuthorizationGrantedEvent를 제어하여 필요하지 않은 이벤트를 필터링하고, 필요한 경우에만 이벤트를 발행하도록 사용자 정의 이벤트 퍼블리셔를 구현한 예제입니다.
AuthorizationGrantedEvent는 기본적으로 비활성화되어 있는데, 이를 활성화하면 이벤트가 너무 자주 발생할 수 있으므로 필터링이 중요합니다. 아래에서 코드의 동작 원리를 자세히 설명하겠습니다.
AuthorizationEventPublisher란?AuthorizationGrantedEvent, AuthorizationDeniedEvent)를 발행하는 역할을 합니다.SpringAuthorizationEventPublisher이며, 이는 모든 권한 이벤트를 발행합니다.AuthorizationGrantedEvent 중 ROLE_ADMIN 권한과 관련된 이벤트만 발행하도록 필터링하는 커스텀 이벤트 퍼블리셔를 구현합니다.@Component
public class MyAuthorizationEventPublisher implements AuthorizationEventPublisher {
private final ApplicationEventPublisher publisher;
private final AuthorizationEventPublisher delegate;
public MyAuthorizationEventPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
this.delegate = new SpringAuthorizationEventPublisher(publisher);
}
}
ApplicationEventPublisher:
AuthorizationEventPublisher 인터페이스 구현:
MyAuthorizationEventPublisher는 AuthorizationEventPublisher를 구현하여 Spring Security에서 사용할 수 있도록 합니다.delegate:
SpringAuthorizationEventPublisher)를 포함.AuthorizationDeniedEvent)와 같은 기본 이벤트는 여전히 처리하도록 위임합니다.publishAuthorizationEvent 메서드 구현@Override
public <T> void publishAuthorizationEvent(Supplier<Authentication> authentication,
T object, AuthorizationDecision decision) {
if (decision == null) {
return;
}
if (!decision.isGranted()) {
this.delegate.publishAuthorizationEvent(authentication, object, decision);
return;
}
if (shouldThisEventBePublished(decision)) {
AuthorizationGrantedEvent granted = new AuthorizationGrantedEvent(
authentication, object, decision);
this.publisher.publishEvent(granted);
}
}
decision == null 처리:
AuthorizationDecision)이 없으면 아무 작업도 하지 않고 반환.권한 거부 처리:
if (!decision.isGranted()) {
this.delegate.publishAuthorizationEvent(authentication, object, decision);
return;
}
decision.isGranted() == false), 기본 퍼블리셔를 통해 AuthorizationDeniedEvent를 발행합니다.필터링된 권한 승인 처리:
if (shouldThisEventBePublished(decision)) {
AuthorizationGrantedEvent granted = new AuthorizationGrantedEvent(
authentication, object, decision);
this.publisher.publishEvent(granted);
}
shouldThisEventBePublished 메서드를 사용해 특정 조건에 부합하는 경우만 AuthorizationGrantedEvent를 생성하고 발행.shouldThisEventBePublished 메서드 구현private boolean shouldThisEventBePublished(AuthorizationDecision decision) {
if (!(decision instanceof AuthorityAuthorizationDecision)) {
return false;
}
Collection<GrantedAuthority> authorities = ((AuthorityAuthorizationDecision) decision).getAuthorities();
for (GrantedAuthority authority : authorities) {
if ("ROLE_ADMIN".equals(authority.getAuthority())) {
return true;
}
}
return false;
}
결정 타입 확인:
if (!(decision instanceof AuthorityAuthorizationDecision)) {
return false;
}
AuthorizationDecision이 AuthorityAuthorizationDecision 타입인지 확인.AuthorityAuthorizationDecision만 권한 리스트를 확인할 수 있으므로, 다른 타입일 경우 이벤트를 발행하지 않음.권한 필터링:
for (GrantedAuthority authority : authorities) {
if ("ROLE_ADMIN".equals(authority.getAuthority())) {
return true;
}
}
AuthorityAuthorizationDecision 객체에서 권한 리스트(authorities)를 가져옴.ROLE_ADMIN이 포함되어 있는 경우에만 true 반환.AuthorizationDecision)을 생성.MyAuthorizationEventPublisher가 호출되어 권한 결정에 따라 이벤트를 발행.AuthorizationGrantedEvent는 shouldThisEventBePublished 메서드를 통해 ROLE_ADMIN 권한에 한해서만 발행.AuthorizationDeniedEvent)는 기본 퍼블리셔를 통해 그대로 발행.위 코드를 Spring Bean으로 등록 (@Component 사용).
이벤트 리스너에서 필요한 AuthorizationGrantedEvent를 처리:
@Component
public class AuthorizationEventListener {
@EventListener
public void handleAuthorizationGranted(AuthorizationGrantedEvent event) {
System.out.println("Granted Event: " + event);
}
@EventListener
public void handleAuthorizationDenied(AuthorizationDeniedEvent event) {
System.out.println("Denied Event: " + event);
}
}
AuthorizationGrantedEvent는 이제 ROLE_ADMIN과 관련된 이벤트에만 발행됩니다.
필터 조건
shouldThisEventBePublished 메서드의 필터 조건을 명확히 설정해야 합니다. 필요에 따라 조건을 추가하거나 수정하여 더 세밀한 필터링을 구현하세요.이벤트 과다 발행 방지
테스트
이 구현을 통해 권한 승인 이벤트를 필요에 따라 필터링하고 효율적으로 관리할 수 있습니다.