Издевательский JwtDecoder Spring Security OAuth2

У меня есть собственная аннотация, которая используется в нескольких контроллерах:

      @Target({ElementType.PARAMETER, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@AuthenticationPrincipal(expression = "@applicationUserDetailsService.loadUserByUsername(#this.getSubject()).getId()")
public @interface CurrentUserId {

}

Также у меня есть JwtEncoder:

      @Bean
    public JwtEncoder jwtEncoder() {
        final JWK jwk = new RSAKey.Builder(keys.getPublicKey()).privateKey(keys.getPrivateKey()).build();
        final JWKSource<SecurityContext> jwkSource = new ImmutableJWKSet<>(new JWKSet(jwk));
        return new NimbusJwtEncoder(jwkSource);
    }

И JwtDecoder:

      @Bean
    public JwtDecoder jwtDecoder() {
        return NimbusJwtDecoder.withPublicKey(keys.getPublicKey()).build();
    }

Мой собственный статический метод служит для создания имитируемых вызовов:

      public static ResultActions makeMockMvcRequest(final MockMvc mockMvc,
                                                   final HttpMethod requestType,
                                                   final String endpoint,
                                                   final Map<String, Object> params,
                                                   final Object body,
                                                   final User user) throws Exception {
        final var requestBuilder = defineRequestBuilder(requestType, endpoint);
        addBodyIfExists(body, requestBuilder);
        addUserIfExists(user, requestBuilder);
        return mockMvc.perform(requestBuilder
                .contentType(MediaType.APPLICATION_JSON)
                .params(convertToMultiValueMap(params)));
    }

    private static MockHttpServletRequestBuilder defineRequestBuilder(final HttpMethod requestType, final String endpoint) {
        MockHttpServletRequestBuilder requestBuilder;
        if (POST.equals(requestType)) {
            requestBuilder = post(endpoint);
        } else if (GET.equals(requestType)) {
            requestBuilder = get(endpoint);
        } else if (PUT.equals(requestType)) {
            requestBuilder = put(endpoint);
        } else {
            throw new IllegalArgumentException("Unsupported HTTP request type: " + requestType);
        }
        return requestBuilder;
    }

    private static void addBodyIfExists(final Object body, final MockHttpServletRequestBuilder requestBuilder) {
        if (body != null) {
            requestBuilder.content(asJsonString(body));
        }
    }

    private static void addUserIfExists(final User user, final MockHttpServletRequestBuilder requestBuilder) {
        if (user != null) {
            final String[] roles = user.getRoles().stream()
                    .map(Role::name)
                    .distinct()
                    .toArray(String[]::new);

            final List<GrantedAuthority> authorities = user.getRoles().stream()
                    .map(role -> (GrantedAuthority) role::name)
                    .toList();

            requestBuilder
                    .with(user(user.getEmail()).roles(roles))
                    .with(jwt().jwt(j -> j.subject(user.getEmail()).tokenValue("strong-token").build()).authorities(authorities));
        }
    }

Но когда я пытаюсь сделать имитацию вызова MVC, я получаю следующую ошибку:

      jakarta.servlet.ServletException: Request processing failed: org.springframework.expression.spel.SpelEvaluationException: EL1011E: Method call: Attempted to call method getId() on null context object

Как я могу имитировать декодер, чтобы разрешить данный идентификатор пользователя? Или есть лучший и более удобный способ достичь издевательской цели?

1 ответ

Однако если кого-то заинтересует мой подход, то после нескольких часов отладки я уже нашел решение:

  1. Вам необходимо указать bean-компонент для загрузки пользователя по имени пользователя:

    @TestConfiguration общественный класс SecurityMockConfiguration {

            @Bean
    public UserDetailsService applicationUserDetailsService() {
        return username -> Stream.of(NEW_USER, FIRST_STUDENT, SECOND_STUDENT, INSTRUCTOR, ADMIN)
                .filter(actor -> Objects.equals(username, actor.getEmail()))
                .findFirst()
                .map(ApplicationUserDetails:: new)
                .orElse(new ApplicationUserDetails(new User()));
    }
    

    }

  2. Добавьте для себя высмеиваемый Jwt (mockMvc):

                if (user != null) {
            final List<GrantedAuthority> authorities = user.getRoles().stream()
                    .map(role -> (GrantedAuthority) () -> "ROLE_" + role.name())
                    .toList();
            requestBuilder.with(jwt().jwt(j -> j.subject(user.getEmail())).authorities(authorities));
        }
    

После этого вы сможете делать запросы на основе какого-либо пользователя:

      makeMockMvcRequest(mockMvc, GET, COURSE_ENDPOINT, FIRST_STUDENT)
                                    .andExpect(responseBody().containsObjectsAsJson(userCourses, UserCourseDto.class))

 
Другие вопросы по тегам