1. 개요
이 짧은 기사에서는 Spring Security와 함께 Spring Boot Actuator 모듈과 인증 및 권한 부여 이벤트 게시 지원에 대해 살펴보겠습니다.
2. 메이븐 의존성
먼저 pom.xml 에 spring-boot-starter-actuator 를 추가해야 합니다.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>2.7.2</version>
</dependency>
최신 버전은 Maven Central 리포지토리에서 사용할 수 있습니다.
3. 인증 및 권한 부여 이벤트 수신
Spring Boot 애플리케이션에서 모든 인증 및 권한 부여 시도를 기록하려면 수신기 메서드로 빈을 정의하면 됩니다.
@Component
public class LoginAttemptsLogger {
@EventListener
public void auditEventHappened(
AuditApplicationEvent auditApplicationEvent) {
AuditEvent auditEvent = auditApplicationEvent.getAuditEvent();
System.out.println("Principal " + auditEvent.getPrincipal()
+ " - " + auditEvent.getType());
WebAuthenticationDetails details =
(WebAuthenticationDetails) auditEvent.getData().get("details");
System.out.println("Remote IP address: "
+ details.getRemoteAddress());
System.out.println(" Session Id: " + details.getSessionId());
}
}
사용 가능한 정보를 표시하기 위해 AuditApplicationEvent 에서 사용할 수 있는 항목 중 일부를 출력하고 있다는 점에 유의 하십시오. 실제 애플리케이션에서는 해당 정보를 추가로 처리하기 위해 리포지토리 또는 캐시에 저장하려고 할 수 있습니다.
모든 Spring bean이 작동합니다. 새로운 Spring 이벤트 지원의 기본은 매우 간단합니다.
- 메서드에 @EventListener 어노테이션을 추가합니다.
- 메서드의 유일한 인수로 AuditApplicationEvent 를 추가합니다.
애플리케이션 실행 결과는 다음과 같습니다.
Principal anonymousUser - AUTHORIZATION_FAILURE
Remote IP address: 0:0:0:0:0:0:0:1
Session Id: null
Principal user - AUTHENTICATION_FAILURE
Remote IP address: 0:0:0:0:0:0:0:1
Session Id: BD41692232875A5A65C5E35E63D784F6
Principal user - AUTHENTICATION_SUCCESS
Remote IP address: 0:0:0:0:0:0:0:1
Session Id: BD41692232875A5A65C5E35E63D784F6
이 예에서는 리스너가 세 개의 AuditApplicationEvent 를 수신했습니다.
- 로그인하지 않고 제한된 페이지에 대한 액세스를 요청했습니다.
- 로그온하는 동안 잘못된 암호가 사용되었습니다.
- 두 번째로 올바른 비밀번호를 사용했습니다.
4. 인증 감사 수신기
Spring Boot의 AuthorizationAuditListener 에 의해 노출된 정보 가 충분하지 않은 경우 더 많은 정보를 노출하기 위해 고유한 bean을 생성할 수 있습니다.
권한 부여에 실패했을 때 액세스된 요청 URL도 노출하는 예를 살펴보겠습니다.
@Component
public class ExposeAttemptedPathAuthorizationAuditListener
extends AbstractAuthorizationAuditListener {
public static final String AUTHORIZATION_FAILURE
= "AUTHORIZATION_FAILURE";
@Override
public void onApplicationEvent(AbstractAuthorizationEvent event) {
if (event instanceof AuthorizationFailureEvent) {
onAuthorizationFailureEvent((AuthorizationFailureEvent) event);
}
}
private void onAuthorizationFailureEvent(
AuthorizationFailureEvent event) {
Map<String, Object> data = new HashMap<>();
data.put(
"type", event.getAccessDeniedException().getClass().getName());
data.put("message", event.getAccessDeniedException().getMessage());
data.put(
"requestUrl", ((FilterInvocation)event.getSource()).getRequestUrl() );
if (event.getAuthentication().getDetails() != null) {
data.put("details",
event.getAuthentication().getDetails());
}
publish(new AuditEvent(event.getAuthentication().getName(),
AUTHORIZATION_FAILURE, data));
}
}
이제 리스너에 요청 URL을 기록할 수 있습니다.
@Component
public class LoginAttemptsLogger {
@EventListener
public void auditEventHappened(
AuditApplicationEvent auditApplicationEvent) {
AuditEvent auditEvent = auditApplicationEvent.getAuditEvent();
System.out.println("Principal " + auditEvent.getPrincipal()
+ " - " + auditEvent.getType());
WebAuthenticationDetails details
= (WebAuthenticationDetails) auditEvent.getData().get("details");
System.out.println(" Remote IP address: "
+ details.getRemoteAddress());
System.out.println(" Session Id: " + details.getSessionId());
System.out.println(" Request URL: "
+ auditEvent.getData().get("requestUrl"));
}
}
결과적으로 이제 출력에 요청된 URL이 포함됩니다.
Principal anonymousUser - AUTHORIZATION_FAILURE
Remote IP address: 0:0:0:0:0:0:0:1
Session Id: null
Request URL: /hello
이 예제에서는 추상 AbstractAuthorizationAuditListener 에서 확장 했으므로 구현에서 해당 기본 클래스 의 게시 메서드를 사용할 수 있습니다.
테스트하려면 소스 코드를 확인하고 다음을 실행하십시오.
mvn clean spring-boot:run
그런 다음 브라우저에서 http://localhost:8080/ 을 가리킬 수 있습니다 .
5. 감사 이벤트 저장
기본적으로 Spring Boot는 AuditEventRepository 에 감사 이벤트를 저장합니다 . 자체 구현으로 빈을 생성하지 않으면 InMemoryAuditEventRepository 가 자동으로 연결됩니다.
InMemoryAuditEventRepository 는 최근 4000개의 감사 이벤트를 메모리에 저장하는 일종의 순환 버퍼입니다. 그런 다음 이러한 이벤트는 관리 엔드포인트 http://localhost:8080/auditevents 를 통해 액세스할 수 있습니다 .
그러면 감사 이벤트의 JSON 표현이 반환됩니다.
{
"events": [
{
"timestamp": "2017-03-09T19:21:59+0000",
"principal": "anonymousUser",
"type": "AUTHORIZATION_FAILURE",
"data": {
"requestUrl": "/auditevents",
"details": {
"remoteAddress": "0:0:0:0:0:0:0:1",
"sessionId": null
},
"type": "org.springframework.security.access.AccessDeniedException",
"message": "Access is denied"
}
},
{
"timestamp": "2017-03-09T19:22:00+0000",
"principal": "anonymousUser",
"type": "AUTHORIZATION_FAILURE",
"data": {
"requestUrl": "/favicon.ico",
"details": {
"remoteAddress": "0:0:0:0:0:0:0:1",
"sessionId": "18FA15865F80760521BBB736D3036901"
},
"type": "org.springframework.security.access.AccessDeniedException",
"message": "Access is denied"
}
},
{
"timestamp": "2017-03-09T19:22:03+0000",
"principal": "user",
"type": "AUTHENTICATION_SUCCESS",
"data": {
"details": {
"remoteAddress": "0:0:0:0:0:0:0:1",
"sessionId": "18FA15865F80760521BBB736D3036901"
}
}
}
]
}
6. 결론
Spring Boot의 액추에이터 지원을 통해 사용자의 인증 및 권한 부여 시도를 기록하는 것이 쉬워집니다. 판독기는 또한 몇 가지 추가 정보에 대해 프로덕션 준비 감사 를 참조합니다.
이 기사의 코드는 GitHub 에서 찾을 수 있습니다 .