Отлов пользовательского исключения, генерируемого из подкласса OncePerRequestFilter в AuthenticationEntryPoint - PullRequest
0 голосов
/ 04 июня 2019

У меня есть JWTAuthFilter, который расширяет OncePerRequestFilter, где я проверяю токен.

Метод validateToken генерирует пользовательские исключения (CredentialsChangedException и TooManyDevicesException, которые расширяют org.springframework.security.core.AuthenticationException)

Эти исключения правильно фиксируются в фильтре, но когда они перемещаются к AuthenticationEntryPoint, AuthenticationException превращается в instanceof InsufficientAuthenticationException, и пользовательское сообщение об ошибке, которое я хочу вернуть в ответ, теряется.

@Component
public class JwtAuthFilter extends OncePerRequestFilter {

    private static final String BEARER = "Bearer ";

    private static final Logger logger = LoggerFactory.getLogger(JwtAuthFilter.class);

    @Autowired
    private UserDetailsService jwtUserDetailsService;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Value ("${jwt.http.request.header}")
    private String tokenHeader;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
                    throws ServletException, IOException {

        final String requestTokenHeader = request.getHeader(this.tokenHeader);

        String username = null;
        String jwtToken = null;

        if((requestTokenHeader != null) && requestTokenHeader.startsWith(BEARER)) {
            jwtToken = requestTokenHeader.substring(7);
            try {
                username = jwtTokenUtil.getUsernameFromToken(jwtToken);
                if((username != null) && (SecurityContextHolder.getContext()
                                                               .getAuthentication() == null)) {

                    UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username);


                    if(jwtTokenUtil.validateToken(jwtToken, userDetails)) {
                        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
                                        new UsernamePasswordAuthenticationToken(userDetails, null,
                                                                                userDetails.getAuthorities());
                        usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                        SecurityContextHolder.getContext()
                                             .setAuthentication(usernamePasswordAuthenticationToken);
                    }
                }
            }
            catch (IllegalArgumentException e) {
                logger.error("Unable to get username from JWT. ", e.getLocalizedMessage());
            }
            catch (ExpiredJwtException e) {
                logger.warn("Expired JWT. ", e.getLocalizedMessage());
            }
            catch (Exception e) {
                logger.error(e.getLocalizedMessage());
            }
        }

        chain.doFilter(request, response);
    }
}
@Component
public class JwtUnAuthorizedResponseAuthenticationEntryPoint implements AuthenticationEntryPoint , Serializable {

    private static final long serialVersionUID = - 8970718410437077606L;

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e)
                    throws IOException {

        if(e instanceof TooManyDevicesException) {
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, Constants.TOO_MANY_DEVICES);
        }

        else if(e instanceof CredentialsChangedException) {
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, Constants.CREDENTIALS_CHANGED);
        }
        else {
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, Constants.JWT_EXPIRED);
        }

    }
}

Я хочу отправить соответствующий несанкционированный ответ от моего фильтра, есть ли способ сделать это?

Ответы [ 2 ]

0 голосов
/ 05 июня 2019

В качестве временного решения я просто добавил свою пользовательскую ошибку из JwtAuthFilter в качестве атрибута request и извлек ее в AuthenticationEntryPoint

0 голосов
/ 04 июня 2019

Я думаю, что как только вы поймали AuthenticationException в JwtAuthFilter, вам не следует переходить к следующему фильтру, так как, скорее всего, AnonymousAuthenticationFilter будет находиться в более поздней части цепочки фильтров, и этот фильтр будет настраивать текущийзапросить анонимный пользователь, если SecurityContextHolder пусто (т.е. происходит при сбое аутентификации).InsufficientAuthenticationException, скорее всего, связано с тем, что Spring считает текущий запрос анонимным пользователем, который имеет доступ к некоторым защищенным URL-адресам или методам.

Вместо этого, как только вы поймаете AuthenticationException в вашем JwtAuthFilter, вам следует вызватьAuthenticationEntryPoint.commence() и конец цепочки фильтров.Это также, как BasicAuthenticationFilter делает сейчас.

Итак, я предлагаю изменить JwtAuthFilter на:

@Component
public class JwtAuthFilter extends OncePerRequestFilter {

  @Autowired
  private AuthenticationEntryPoint authenticationEntryPoint;


  @Override
  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
                    throws ServletException, IOException {
         try{

              //validate JWT

          } catch (AuthenticationException e) {
               logger.error(e.getLocalizedMessage());
                SecurityContextHolder.clearContext();
                this.authenticationEntryPoint.commence(request, response, e);
                return;
          }
    }
}
...