Spring Boot 2.4.0 — Установка SecurityContextHolder с использованием BasicAuthenticationFilter вызывает исключение IllegalStateException

#java #spring #spring-boot #spring-security #java-15

#java #spring #spring-boot #spring-безопасность #java-15

Вопрос:

В настоящее время выполняется обновление с версии Spring Boot v2.3.5 до версии v2.4.0. Однако я сталкиваюсь с проблемой, связанной с установкой SecurityContextHolder, когда пользователь аутентифицируется и фактически проходит через цепочку фильтров.

Проблемный код является частью класса, который расширяет BasicAuthenticationFilter и выглядит следующим образом:

     /**
     * Perform the filter for authentication
     *
     * @param request  The HttpServletRequest
     * @param response The HttpServletResponse
     * @param chain    The FilterChain
     * @throws IOException      When the filter could not be performed
     * @throws ServletException When the filter could not be performed
     */
    @Override
    protected void doFilterInternal(final HttpServletRequest request,
                                    final HttpServletResponse response,
                                    final FilterChain chain) throws IOException, ServletException {
        final String header = request.getHeader(jwtConfiguration.getHeader());

        if (header == null || !header.startsWith(jwtConfiguration.getTokenPrefix())) {
            chain.doFilter(request, response);
            return;
        }

        final UsernamePasswordAuthenticationToken authentication = getAuthentication(request);

        if (authentication == null) {
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
        } else {
            final SecurityContext context = SecurityContextHolder.createEmptyContext();

            context.setAuthentication(authentication);
            SecurityContextHolder.setContext(context);

            chain.doFilter(request, response); // The exception is thrown here, but only when setting the SecurityContextHolder
        }
    }


...

    /**
     * Get the UsernamePasswordAuthenticationToken from the HttpServletRequest
     *
     * @param request The HttpServletRequest
     * @return The UsernamePasswordAuthenticationToken that was filtered from the HttpServletRequest
     */
    private UsernamePasswordAuthenticationToken getAuthentication(final HttpServletRequest request) {
        final String token = request.getHeader(jwtConfiguration.getHeader());
        if (token != null amp;amp; !token.isEmpty()) {
            final UserDto user = authorizationService.getCurrentUser(token);

            if (user != null) {
                final Set<GrantedAuthority> grantedAuthorities = new HashSet<>();

                if (user.getRoles() != null) {
                    for (final RoleDto role : user.getRoles()) {
                        if (role.getPermissions() != null) {
                            for (final PermissionDto permission : role.getPermissions()) {
                                grantedAuthorities.add(new SimpleGrantedAuthority(permission.getName()));
                            }
                        }
                    }
                }

                return new UsernamePasswordAuthenticationToken(user, null, grantedAuthorities);
            }
            return null;
        }
        return null;
    }
  

Без установки SecurityContextHolder код работает просто отлично, но, к сожалению, аутентификация не будет установлена, что не является результатом, который мне нужен, поскольку я хотел бы защитить определенные конечные точки.

Класс, который расширяет WebSecurityConfigurerAdapter, настроен следующим образом:

 @Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true, jsr250Enabled = true, prePostEnabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    private final JwtConfiguration jwtConfiguration;
    private final IAuthorizationService userService;

    /**
     * Initialize a new WebSecurityConfiguration
     *
     * @param jwtConfiguration The JwtConfiguration object
     * @param userService      The IAuthorizationService implementation
     */
    public WebSecurityConfiguration(@Autowired final JwtConfiguration jwtConfiguration,
                                    @Autowired final IAuthorizationService userService) {
        if (jwtConfiguration == null)
            throw new NullPointerException("JwtConfig cannot be null!");
        if (userService == null)
            throw new NullPointerException("IAuthorizationService cannot be null!");

        this.jwtConfiguration = jwtConfiguration;
        this.userService = userService;
    }

    /**
     * Configure the HttpSecurity
     *
     * @param http The HttpSecurity object
     * @throws Exception When the configuration failed
     */
    @Override
    protected void configure(final HttpSecurity http) throws Exception {
        final AuthenticationManager authenticationManager = authenticationManager();

        http
                .csrf().disable().cors().and()
                .authorizeRequests()
                .antMatchers("/authentication/**",
                        "/v2/api-docs",
                        "/configuration/ui",
                        "/swagger-resources/**",
                        "/configuration/security",
                        "/swagger-ui.html",
                        "/webjars/**")
                .permitAll()
                .anyRequest().authenticated()
                .and()
                .addFilterBefore(new JwtAuthorizationFilter(authenticationManager, jwtConfiguration, userService), JwtAuthorizationFilter.class)
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }
}
  

Само исключение, возникающее при установке SecurityContextHolder, при передаче по цепочке выглядит следующим образом:

java.lang.Исключение IllegalStateException: для интерфейса org.springframework.security.core не найден основной или единственный общедоступный конструктор.Аутентификация — и конструктор по умолчанию также не найден

Трассировка стека исключений

Комментарии:

1. Можете ли вы показать нам, что внутри getAuthentication(...) ?

2. @Aman Да, но только частично.

3. Та часть, где вы создаете экземпляр Authentication объекта?

4. Конечно, добавил это к вопросу @Aman

5. Вы когда-нибудь решали эту проблему? Я испытываю точно такую же трассировку стека.