Skip to content

Subclasses of AbstractAuthenticationEvent are not fired anymore  #11420

@michael-simons

Description

@michael-simons

Given the following application

import java.security.Principal;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.event.AuthenticationFailureBadCredentialsEvent;
import org.springframework.security.authentication.event.AuthenticationSuccessEvent;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@Configuration
@RestController
public class AuthlistenerApplication {

    @GetMapping("/")
    public String hello(final Principal principal) {
        return "Hello, " + principal.getName() + ".";
    }

    @Bean
    public ApplicationListener<AuthenticationSuccessEvent> onSuccessListener() {
        return (AuthenticationSuccessEvent event) -> {
            System.out.println("Yeah!");
        };
    }

    @Bean
    public ApplicationListener<AuthenticationFailureBadCredentialsEvent> onBadCredentialsListener() {
        return (AuthenticationFailureBadCredentialsEvent event) -> {
            System.out.println("Oh no...");
        };
    }

    public static void main(String[] args) {
        SpringApplication.run(AuthlistenerApplication.class, args);
    }
}

Running this on Spring Boot 1.5.9.RELEASE triggers either AuthenticationSuccessEvent or an AuthenticationFailureBadCredentialsEvent event, depending if one uses the correct password.

Neither of those events are triggered on 2.0.0.BUILD-SNAPSHOT and probably not on the Milestone as well. Here's a test to confirm this:

import java.util.Base64;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.rule.OutputCapture;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;

import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = RANDOM_PORT)
@TestPropertySource(properties = {
    "security.user.name = testuser",
    "security.user.password = testpassword",
    "spring.security.user.name = testuser",
    "spring.security.user.password = testpassword",})
public class AuthlistenerApplicationTests {

    @Rule
    public OutputCapture output = new OutputCapture();

    @Autowired
    TestRestTemplate restTemplate;

    @Test
    public void logsSuccess() throws Exception {
        final HttpHeaders headers = new HttpHeaders();
        headers.add("Authorization", "Basic " + Base64.getEncoder().encodeToString("testuser:testpassword".getBytes()));
        headers.add("Accept", MediaType.APPLICATION_JSON_VALUE);

        final ResponseEntity<String> r = restTemplate
                .exchange("/", HttpMethod.GET, new HttpEntity<>(headers), String.class);
        assertThat(r.getStatusCode(), is(equalTo(HttpStatus.OK)));
        assertThat(r.getBody(), is(equalTo("Hello, testuser.")));
        assertThat("Output did not contain 'Yeah!'", output.toString(), containsString("Yeah!"));
    }

    @Test
    public void logsFailure() throws Exception {
        final HttpHeaders headers = new HttpHeaders();
        headers.add("Authorization", "Basic " + Base64.getEncoder().encodeToString("testuser:fump".getBytes()));
        headers.add("Accept", MediaType.APPLICATION_JSON_VALUE);

        final ResponseEntity<String> r = restTemplate
                .exchange("/", HttpMethod.GET, new HttpEntity<>(headers), String.class);        
        assertThat(r.getStatusCode(), is(equalTo(HttpStatus.UNAUTHORIZED)));
        assertThat("Output did not contain 'Oh no...'", output.toString(), containsString("Oh no..."));        
    }
}

I used the snapshot for the test as the test makes use of the new old properties to configure the user (see #10963).

I already checked if the DefaultAuthenticationEventPublisher is still part of SecurityAutoConfiguration and yes, it is. So I assume that those events should still be published.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions