Почему я получаю нулевое `Authentication` как параметр метода @Controller в` @ WebMvcTest`? - PullRequest
0 голосов
/ 31 марта 2020

Контекст: я создал тестовую аннотацию @WithMockAuthentication, чтобы заполнить тестовый контекст безопасности экземпляром Authentication, очень похоже на @WithMockUser. Основное различие заключается в том, что в моем случае этот экземпляр является макетом Mockito.

Что я испытываю: как только я заменяю фактический экземпляр на макет, экземпляр Authentication, предоставленный в качестве параметра метода контроллера, имеет значение null в аннотированных тестах: в WithSecurityContextFactory, если я заменю:

    public Authentication workingAuthentication(WithMockAuthentication annotation) {
        return new TestAuthentication(annotation.name(), Stream.of(annotation.authorities()).map(SimpleGrantedAuthority::new).collect(Collectors.toSet()));
    }

на

    public Authentication bogousAuthentication(WithMockAuthentication annotation) {
        var auth = mock(Authentication.class);
        when(auth.getName()).thenReturn(annotation.name());
        when(auth.getAuthorities()).thenReturn((Collection) Stream.of(annotation.authorities()).map(SimpleGrantedAuthority::new).collect(Collectors.toSet()));
        when(auth.isAuthenticated()).thenReturn(true);
        return auth;
    }

Тогда я получу NPE в тестах контроллера на

    @RequestMapping("/method")
    @PreAuthorize("hasRole('ROLE_AUTHORIZED')")
    public ResponseEntity<String> securedMethod(Authentication auth) {
        // Here, auth is null if Authentication is a mock
        return ResponseEntity.ok(String.format("Hey %s, how are you?", auth.getName()));
    }

Я создал минимальный образец для воспроизведения . Запустите тест, чтобы увидеть ошибку.

Я почти уверен, что столкнулся с ошибкой. Достаточно создать проблему в проекте обеспечения безопасности Spring , но кажется, что у команды команды Spring нет времени расследовать ...

[ПРАВИТЬ] Это последнее утверждение бесполезно оскорбительно и совершенно неверно как ответ предоставлен Робом Уинчем, который является одним из основных членов Spring-Security: / My Bad

1 Ответ

1 голос
/ 31 марта 2020

Аргумент для SampleController имеет тип Authentication, который является экземпляром Principal, и поэтому ServletRequestMethodArgumentResolver попытается разрешить аргумент из HttpServletRequest.getUserPrincipal().

Создаваемый вами макет не заглушил метод Authentication.getPrincipal().

public Authentication bogousAuthentication(WithMockAuthentication annotation) {
    var auth = mock(Authentication.class);
    when(auth.getName()).thenReturn(annotation.name());
    when(auth.getAuthorities()).thenReturn((Collection) Stream.of(annotation.authorities()).map(SimpleGrantedAuthority::new).collect(Collectors.toSet()));
    when(auth.isAuthenticated()).thenReturn(true);
    return auth;
}

По этой причине Authentication.getPrincipal() равен null и, таким образом, SecurityContextHolderAwareRequestWrapper.getUserPrincipal() возвращает null. Почему он возвращает null, когда основным является null? Я не могу быть уверен в первоначальном намерении, так как код был добавлен до того, как я стал членом команды. Однако это имеет смысл в модели Spring Security. Authentication может представлять как аутентифицированного пользователя, так и учетные данные, используемые для аутентификации. Javado c из Authentication.getPrincipal() гласит (выделено мое):

Идентификация удостоверяемого субъекта. В случае запроса аутентификации с именем пользователя и паролем это будет имя пользователя. Ожидается, что вызывающие абоненты заполнят принципал для запроса аутентификации .

Реализация AuthenticationManager часто возвращает аутентификацию, содержащую более подробную информацию, в качестве принципала для использования приложением. Многие провайдеры аутентификации будут создавать объект UserDetails в качестве принципала.

Проверка null должна гарантировать, что Authentication действительно представляет аутентифицированного пользователя.

Чтобы исправить это, вы должны заглушить метод getPrincipal() чем-то вроде when(auth.getPrincipal()).thenReturn("bogus");. Изменение можно увидеть ниже и в моем запросе на получение .

public Authentication bogousAuthentication(WithMockAuthentication annotation) {
    var auth = mock(Authentication.class);
    when(auth.getPrincipal()).thenReturn("bogus");
    when(auth.getName()).thenReturn(annotation.name());
    when(auth.getAuthorities()).thenReturn((Collection) Stream.of(annotation.authorities()).map(SimpleGrantedAuthority::new).collect(Collectors.toSet()));
    when(auth.isAuthenticated()).thenReturn(true);
    return auth;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...