Spring Boot + Spring Security authorization success audit - java

Has anyone managed to get Spring Boot w/ Spring Security to handle AuthorizedEvent's (i.e. for audit log)?
I have implemented the following application event listener:
#Component
public class AuthorizationSuccessAudit implements ApplicationListener<AuthorizedEvent> {
private static Logger auditLogger = LoggerFactory.getLogger("audit");
#Override
public void onApplicationEvent(AuthorizedEvent event) {
auditLogger.info("Authorization granted to user: {} - {}", event.getAuthentication().getName(), event.getConfigAttributes());
}
}
and have a test MVC endpoint annotated with #PreAuthorize. I was expecting that the spring security grants would show up on the log. While this works for every other event I used (AuthenticationSuccessEvent, AuthenticationFailureEvent, AbstractAuthenticationFailureEvent) it does not for the AuthorizedEvent.
I tried browsing the Spring Boot source and it seems this event is not handled in AuthorizationAuditListener.java, is this possibly a bug or am I hacking at it the wrong way?

As per spring boot documentation, Use Spring Boot Actuator (audit framework for Spring Boot), and provide your own implementations of AbstractAuthorizationAuditListener.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>1.3.0.RELEASE</version>
</dependency>
And something similar to this..
class TestAuthorizationAuditListener extends AbstractAuthorizationAuditListener {
#Override
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
}
#Override
public void onApplicationEvent(AbstractAuthorizationEvent event) {
}
}

It looks like spring boot can not realize that here you want to handle event.
Try to annotate method so that spring knows that here you want to handle event
#EventListener(value = {AuthorizedEvent.class})
public void onApplicationEvent(AuthorizedEvent event) {
auditLogger.info("Authorization granted to user: {} - {}", event.getAuthentication().getName(), event.getConfigAttributes());
}

On successful authorization AuthorizedEvent should be triggered. make sure
FilterSecurityInterceptor should set setPublishAuthorizationSuccess true

Related

Problem with automate refresh Azure App Configuration with Spring Boot and enabled global method security

I have a Spring Boot (2.6.6) application which is connected to the Azure App Configuration. I also use the automated refresh mechanisms from the Azure App Configuration, which works fine - I change a value in Azure App Configuration and also update the sentinel value, then the application detects the changes and update the bean values (properties) by calling the setter methods without restarting the application.
But after I added a class with #EnableGlobalMethodSecurity(prePostEnabled = true), then the refresh mechanisms is not working anymore. Only during startup, the values are set, then never again. I see in the log that the application detects the changes, but never calls the setter of the bean to update the values.
How can I solve this problem to have the automated refresh mechanisms and the PreAuthorize working together?
MethodSecurityConfiguration.class:
#Configuration
#AllArgsConstructor
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfiguration extends GlobalMethodSecurityConfiguration {
...
}
ConfigurationProperties.class:
public interface AppConfigurationProperties {
void setExample(String example);
String getExample();
}
AzureAppConfigurationProperties.class:
#Configuration
#ConfigurationProperties(prefix = "config")
public class AzureAppConfigurationProperties implements AppConfigurationProperties {
private String example;
#Override
public void setExample(String example) {
this.example = example;
}
#Override
public String getExample() {
return this.example;
}
}

How to use actuator endpoints programmatically?

I need some metrics in to the spring boot application. So I used MetricsEndpoint like below. But I can't access to metrics. invoke() method does not appear.
spring boot version: 2.2.6.RELEASE
#Controller
public class DashboardControllerImpl implements IDashboardController {
#Autowired
private MetricsEndpoint metricsEndpoint;
#Override
public ResponseEntity listDiskUsage() {
metricsEndpoint.invoke(); // invoke does not appear
// code block
}
}
I found some thing like that, that solved my problem!
((MetricsEndpoint.Sample)metricsEndpoint.metric("process.uptime", null).getMeasurements().get(0)).getValue()

spring boot actuator health endpoint + dynamic resilience4j circuit breakers

I have a spring boot app, which uses resilience4j AOP-based #CircuitBreakers.
Now I would like to make the circuit breakers' information available in the /actuator/health endpoint, but I'm not seeing the details.circuitBtreakers objects described in the docs in the JSON output.
What am I doing wrong?
By comparison, getting dynamic cache information to appear in the /actuator/metrics endpoint required a small amount of custom wiring, but this is well documented. I wonder if there is a similar trick that I can apply for dynamically defined #CircuitBreakers to be registerd with the /actuator/health endpoint.
MyService.java:
#Service
public class MyService {
#Autowired
private CacheManager cacheManager;
#Autowired
private CacheMetricsRegistrar cacheMetricsRegistrar;
#PostConstruct
public void postConstruct() {
// On-the-fly defined (annotation-based) caches are not auto-registered with micrometer metrics.
final Cache cache = cacheManager.getCache("myCache");
cacheMetricsRegistrar.bindCacheToRegistry(cache);
}
#CircuitBreaker(name = "myCB", fallbackMethod = "fallbackCallAnApi")
public String callAnApi() throws RestClientException {
// ...
}
#Cacheable("myCache")
public String getSomethingCacheable() {
// ...
}
}
application.properties:
resilience4j.circuitbreaker.configs.default.registerHealthIndicator=true
management.endpoints.web.expose=health,metrics
management.endpoints.web.exposure.include=health,metrics
management.endpoint.health.enabled=true
management.endpoint.metrics.enabled=true
management.metrics.enable.resilience4j.circuitbreaker.calls=true
management.health.circuitbreakers.enabled=true
Dynamically registering CircuitBreakers for the HealthIndicator endpoint doesn't work at the moment.
Unfortunately you have to configure them:
resilience4j.circuitbreaker:
configs:
default:
registerHealthIndicator: true
instances:
myCB:
baseConfig: default
You could say it's a bug.
https://github.com/resilience4j/resilience4j/blob/master/resilience4j-spring-boot2/src/main/java/io/github/resilience4j/circuitbreaker/monitoring/health/CircuitBreakersHealthIndicator.java#L99-L102

How to Get All Endpoints List After Startup, Spring Boot

I have a rest service written with spring boot. I want to get all endpoints after start up. How can i achieve that?
Purpose of this, i want to save all endpoints to a db after start up (if they are not already exist) and use these for authorization. These entries will be inject into roles and roles will be used to create tokens.
You can get RequestMappingHandlerMapping at the start of the application context.
#Component
public class EndpointsListener implements ApplicationListener<ContextRefreshedEvent> {
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
ApplicationContext applicationContext = event.getApplicationContext();
applicationContext.getBean(RequestMappingHandlerMapping.class).getHandlerMethods()
.forEach(/*Write your code here */);
}
}
Alternately you can also Spring boot actuator(You can also use actutator even though you are not using Spring boot) which expose another endpoint(mappings endpoint) which lists all endpoints in json. You can hit this endpoint and parse the json to get the list of endpoints.
https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-endpoints.html#production-ready-endpoints
You need 3 steps to exposure all endpoints:
enable Spring Boot Actuator
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
enable endpoints
In Spring Boot 2, Actuator comes with most endpoints disabled, the only 2 available by default are :
/health
/info
If you want to enable all of the endpoints, just set:
management.endpoints.web.exposure.include=*
For more details, refer to:
https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-endpoints.html
go!
http://host/actuator/mappings
btw, In Spring Boot 2, Actuator simplifies its security model by merging it with the application one.
For more details, refer to this article:
https://www.baeldung.com/spring-boot-actuators
As an addition to the above comments, since Spring 4.2 you may use the #EventListener annotation like this:
#Component
public class EndpointsListener {
private static final Logger LOGGER = LoggerFactory.getLogger(EndpointsListener.class);
#EventListener
public void handleContextRefresh(ContextRefreshedEvent event) {
ApplicationContext applicationContext = event.getApplicationContext();
applicationContext.getBean(RequestMappingHandlerMapping.class)
.getHandlerMethods().forEach((key, value) -> LOGGER.info("{} {}", key, value));
}
}
If you want to find out more about how to use the Spring Events and to create custom events, please check out this article: Spring Events
In the application.properties, we need
management.endpoints.web.exposure.include=mappings
Then we can see all the endpoints at:
http://localhost:8080/actuator/mappings
Don't forget to add the actuator to the POM.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
Late to the party but you can directly use
#Autowired
private RequestMappingHandlerMapping requestHandlerMapping;
this.requestHandlerMapping.getHandlerMethods()
.forEach((key, value) -> /* whatever */));

Spring MVC Swagger issue

I am using Spring MVC + Swagger version 1.0.2 integration. I am facing issue of duplicates API being seen on the REST docs API. Not sure why?
But I debug the issue, I following link https://dzone.com/articles/documenting-your-spring-api and A 'simple' way to implement Swagger in a Spring MVC application, as per this link I added following code
#Configuration
#EnableSwagger
public class SwaggerConfig {
private SpringSwaggerConfig springSwaggerConfig;
#Autowired
public void setSpringSwaggerConfig(SpringSwaggerConfig springSwaggerConfig) {
this.springSwaggerConfig = springSwaggerConfig;
}
#Bean
// Don't forget the #Bean annotation
public SwaggerSpringMvcPlugin customImplementation() {
return new SwaggerSpringMvcPlugin(this.springSwaggerConfig).apiInfo(
apiInfo()).includePatterns(".*");
}
private ApiInfo apiInfo() {
return new ApiInfo("API", "API",
null, "test#yahoo.com",
"License", "http://test.license");
}
}
But when I added, since then I see same API is getting loaded twice like below
Mapped "{[/api/student],methods=[POST],produces=[application/json]}" onto public org.springframework.http.ResponseEntity<java.lang.String> com.test.StudentController.getStudentDetails(com.test.model.StudentDetails) throws java.lang.Exception
Mapped "{[/api/student],methods=[POST],produces=[application/json]}" onto public org.springframework.http.ResponseEntity<java.lang.String> com.test.StudentController.getStudentDetails(com.test.model.StudentDetails)
throws java.lang.Exception
..................
How to prevent loading beans twice ?
I was able to solve this issue. The issue was nothing to do with your applicationContext.xml or mvc-rest.xml etc, your application code should not be using #EnableSpring at all, then it worked. Even this link https://github.com/springfox/springfox/issues/565#issuecomment-172956702 also suggests the same.

Categories