1. Overview
1.概述
In this short article, we’ll explore the Spring Boot Actuator module and the support for publishing authentication and authorization events in conjunction with Spring Security.
在这篇短文中,我们将探讨Spring Boot Actuator模块以及对与Spring Security一起发布认证和授权事件的支持。
2. Maven Dependencies
2.Maven的依赖性
First, we need to add the spring-boot-starter-actuator to our pom.xml:
首先,我们需要将spring-boot-starter-actuator添加到我们的pom.xml:。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>2.7.2</version>
</dependency>
The latest version is available in the Maven Central repository.
最新版本可在Maven Central资源库中找到。
3. Listening for Authentication and Authorization Events
3.监听认证和授权事件
To log all authentication and authorization attempts in a Spring Boot application, we can just define a bean with a listener method:
要记录Spring Boot应用程序中的所有认证和授权尝试,我们只需定义一个带有监听器方法的Bean。
@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());
}
}
Note that we’re just outputting some of the things that are available in AuditApplicationEvent to show what information is available. In an actual application, you might want to store that information in a repository or cache to process it further.
请注意,我们只是输出了AuditApplicationEvent中的一些东西,以显示哪些信息是可用的。在一个实际的应用程序中,你可能想把这些信息存储在一个存储库或缓存中以进一步处理。
Note that any Spring bean will work; the basics of the new Spring event support are quite simple:
请注意,任何Spring Bean都可以工作;新的Spring事件支持的基础知识非常简单。
- annotate the method with @EventListener
- add the AuditApplicationEvent as the sole argument of the method
The output of running the application will look something like to this:
运行应用程序的输出将看起来像这样。
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
In this example, three AuditApplicationEvents have been received by the listener:
在这个例子中,监听器已经收到了三个AuditApplicationEvents。
- Without logging on, access has been requested to a restricted page
- A wrong password has been used while logging on
- A correct password has been used the second time around
4. An Authentication Audit Listener
4.一个认证审计监听器
If the information exposed by Spring Boot’s AuthorizationAuditListener is not enough, you can create your own bean to expose more information.
如果Spring Boot的AuthorizationAuditListener暴露的信息不够,你可以创建自己的Bean来暴露更多信息。
Let’s have a look at an example, where we also expose the request URL that was accessed when the authorization fails:
让我们来看看一个例子,我们也暴露了授权失败时被访问的请求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));
}
}
We can now log the request URL in our listener:
我们现在可以在我们的监听器中记录请求的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"));
}
}
As a result, the output now contains the requested URL:
结果是,现在的输出包含了请求的URL。
Principal anonymousUser - AUTHORIZATION_FAILURE
Remote IP address: 0:0:0:0:0:0:0:1
Session Id: null
Request URL: /hello
Note that we extended from the abstract AbstractAuthorizationAuditListener in this example, so we can use the publish method from that base class in our implementation.
注意,在这个例子中,我们从抽象的AbstractAuthorizationAuditListener扩展而来,所以我们可以在实现中使用该基类的publish方法。
If you want to test it, check out the source code and run:
如果你想测试它,请查看源代码并运行。
mvn clean spring-boot:run
Thereafter you can point your browser to http://localhost:8080/.
此后,你可以将你的浏览器指向http://localhost:8080/。
5. Storing Audit Events
5.存储审计事件
By default, Spring Boot stores the audit events in an AuditEventRepository. If you don’t create a bean with an own implementation, then an InMemoryAuditEventRepository will be wired for you.
默认情况下,Spring Boot将审计事件存储在一个AuditEventRepository中。如果你不创建一个有自己实现的bean,那么InMemoryAuditEventRepository将为你接线。
The InMemoryAuditEventRepository is a kind of circular buffer that stores the last 4000 audit events in memory. Those events can then be accessed via the management endpoint http://localhost:8080/auditevents.
InMemoryAuditEventRepository是一种循环缓冲器,在内存中存储最后4000个审计事件。然后可以通过管理端点http://localhost:8080/auditevents访问这些事件。
This returns a JSON representation of the audit events:
这将返回一个审计事件的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. Conclusion
6.结论
With the actuator support in Spring Boot, it becomes trivial to log the authentication and authorization attempts from users. The reader is also referred to production ready auditing for some additional information.
有了Spring Boot中的执行器支持,记录用户的认证和授权尝试就变得微不足道了。读者还可参阅生产准备审计以了解一些额外的信息。
The code from this article can be found over on GitHub.
本文的代码可以在GitHub上找到over。