1

I want to write some kind of unit test which depends on Spring Security.

For example, I have some service method which uses some repository and marked with @PreAuthorize annotation. Repository I can mock with Mockito, there is no problem. Also I can mock Security Context by @WithSecurityContext annotation. But when I run test, the @PreAuthorize annotation is just ignored. Of course I can run that test with @SpringBootTest annotation as an integration test and in this case the Security Context is up but this way is heavy and slow.

Is there a way to run unit test with only Spring Security Context raised?

UPDATE

Made an example of such kind of test. Thanks to @Sam Brannen for giving right direction.

@ActiveProfiles("method-security-test")
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {ExampleService.class, ExampleServiceTest.MethodSecurityConfiguration.class})
public class ExampleServiceTest {

    private ExampleService service;

    @Autowired
    public void setService(ExampleService service) {
        this.service = service;
    }

    @Test
    @WithMockUser(username = "john_doe")
    public void testAuthenticated() {
        String actualMessage = service.example();
        Assert.assertEquals("Message of john_doe", actualMessage);
    }

    @Test(expected = AuthenticationException.class)
    public void testNotAuthenticated() {
        service.example();
        Assert.fail();
    }

    @TestConfiguration
    @EnableGlobalMethodSecurity(prePostEnabled = true)
    static class MethodSecurityConfiguration extends GlobalMethodSecurityConfiguration {
    }
}

@Service
class ExampleService {
    @PreAuthorize("isAuthenticated()")
    String example() {
        Principal principal = SecurityContextHolder.getContext().getAuthentication();
        return "Message of " + principal.getName();
    }
MarkHuntDev
  • 181
  • 3
  • 21

1 Answers1

3

The @PreAuthorize annotation from Spring Security will only be honored if Spring Security proxies your component (e.g., service bean).

The simplest way to make that happen is by annotating an @Configuration class with @EnableGlobalMethodSecurity(prePostEnabled = true), having your component registered as a bean (e.g., via component scanning or an @Bean method), and including your AuthenticationManager setup.

You can then create a focused integration test using @ContextConfiguration (without Spring Boot testing support) to load an ApplicationContext from your @Configuration class. And you can use @Autowired to get access to your proxied component which will be advised with the @PreAuthorize security check support.

You might find this old blog post useful as well for background information: https://spring.io/blog/2013/07/04/spring-security-java-config-preview-method-security/

Sam Brannen
  • 29,611
  • 5
  • 104
  • 136