Solucionado (ver solução)
Solucionado
(ver solução)
6
respostas

Erro IllegalArgumentException quando acesso o caminho do Swagger

Bom dia.

Quando acesso o caminho do Swagger, está estourando o seguinte erro:

java.lang.IllegalArgumentException: JWT String argument cannot be null or empty.

Ele esta passando pela classe (AutenticacaoTokenFilter) de filtro do token.

Implementações das classes:

@Configuration
public class SwaggerConfigurations {

    @Bean
    public Docket api(){
        return new Docket(DocumentationType.SWAGGER_2)
               .select()
               .apis(RequestHandlerSelectors.basePackage("br.com.formula.bancaria.api"))
               .paths(PathSelectors.ant("/**"))
               .build()
               .ignoredParameterTypes(Usuario.class)
               .globalOperationParameters(
                    Arrays.asList(
                       new ParameterBuilder()
                            .name("Authorization")
                            .description("Header para Token JWT")
                            .modelRef(new ModelRef("string"))
                            .parameterType("header")
                            .required(false)
                            .build()
                    )
                );
    }
}

===========================================================

@EnableWebSecurity
@Configuration
public class SecurityConfigurations extends WebSecurityConfigurerAdapter {

    @Autowired
    private TokenService tokenService;

    @Autowired
    private UsuarioRepository usuarioRepository;

    @Autowired
    private AutenticacaoService autenticacaoService;

    @Override
    @Bean
    protected AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }

    //Configura autenticação
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(autenticacaoService).passwordEncoder(new BCryptPasswordEncoder());
    }

    // Configura autorização
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers(HttpMethod.POST, "/auth").permitAll()
            .anyRequest()
            .authenticated()
            .and()
            .csrf().disable()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .addFilterBefore(new AutenticacaoTokenFilter(tokenService, usuarioRepository), 
                             UsernamePasswordAuthenticationFilter.class); 
    }

    // Configura os arquivos estaticos
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/**.html", "/v2/api-docs", "/webjars/**", "/configuration/**", "/swagger-resources/**");
    }
}

=================================================

@SpringBootApplication
@EnableSpringDataWebSupport
@EnableCaching
@EnableSwagger2
public class ApiApplication {

    public static void main(String[] args) {
        SpringApplication.run(ApiApplication.class, args);
    }

}

O caminho do Swagger não era para estar livre, sem precisar passar pelo filtro?

6 respostas

Oi Rafael,

Era para liberar mesmo o Swagger.

Talvez seja alguma detalhe no seu código.

Consegue postar aqui o código das suas classes TokenService e AutenticacaoViaTokenFilter?

package br.com.formula.bancaria.api.service;

import java.util.Date;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;

import br.com.formula.bancaria.api.model.entity.Usuario;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
@Service
public class TokenService {

    private Date hoje;

    @Value("${formula.bancaria.api.jwt.expiration}")
    private String expiration;

    @Value("${formula.bancaria.api.jwt.secret}")
    private String secret;

    private Date dataExpiracao;

    public String token(Authentication authentication) {
        Usuario usuarioLogado = (Usuario) authentication.getPrincipal();
        hoje = new Date();
        dataExpiracao = new Date(hoje.getTime() + Long.parseLong(expiration));
        return Jwts.builder()
                   .setIssuer("API Formula Bancaria")
                   .setSubject(usuarioLogado.getId().toString())
                   .setIssuedAt(hoje)
                   .setExpiration(dataExpiracao)
                   .signWith(SignatureAlgorithm.HS256, secret)
                   .compact();
    }

    public boolean isValido(String token) {
        try{
            Jwts.parser().setSigningKey(secret).parseClaimsJws(token);
            return true;
        }catch(Exception e){
            e.printStackTrace();
            return false;
        }
    }

    public Long getIdUsuario(String token) {
        Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
        return Long.parseLong(claims.getSubject());
    }

}
======================================================================================================================

package br.com.formula.bancaria.api.config.filter;

import java.io.IOException;
import java.util.Optional;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;

import br.com.formula.bancaria.api.model.entity.Usuario;
import br.com.formula.bancaria.api.repository.UsuarioRepository;
import br.com.formula.bancaria.api.service.TokenService;

public class AutenticacaoTokenFilter extends OncePerRequestFilter{

    private TokenService tokenService;
    private UsuarioRepository repository;

    public AutenticacaoTokenFilter(TokenService tokenService, UsuarioRepository repository) {
        this.tokenService = tokenService;
        this.repository = repository;
    }

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

        String token = recuperaToken(request);

        if(tokenService.isValido(token)){
            autenticar(token);
        }

        filterChain.doFilter(request, response);
    }

    private void autenticar(String token) {
        Long idUsuario = tokenService.getIdUsuario(token);
        Usuario usuario = repository.findById(idUsuario).get();
        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(usuario, null, usuario.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(authentication);
    }

    private String recuperaToken(HttpServletRequest request){
        String token = request.getHeader("Authorization");
        if(!Optional.ofNullable(token).isPresent() || !token.startsWith("Bearer ")){
            return null;
        }

        return token.split(" ")[1];
    }
}

Oi Rafael,

No seu método isValido da classe TokenService, dentro do catch tem um e.printStackTrace(); e por isso o Java vai imprimir a exceção no console quando ela ocorrer.

Quando acessamos o Swagger pelo navegador, não estamos enviando o token jwt e por isso ele vai chegar null e causar a exception.

Mas isso não é problema, pois no catch devolvemos false e o usuario não será autenticado, mas o Spring na sequencia vai verificar que a URL está liberada e deveria deixar acessar normalmente.

A tela do Swagger chegou a aparecer para você no browser?

Apareceu.

Quando estoura essa exceção, me da sensação de ter implementado algo errado.

Ah então está ok!

Para não acontecer a exception você pode fazer esse tratamento:

public boolean isValido(String token) {
    if (token == null || token.trim().isEmpty()) {
        return false;
    }

    try {
        Jwts.parser().setSigningKey(secret).parseClaimsJws(token);
        return true;
    } catch(Exception e) {
        e.printStackTrace();
        return false;
    }
}

Bons estudos!

solução!

Obrigado!