4
respostas

Cabeçalho na requisição nullo

Tenho uma aplicação cliente (Angular2) que envia um header com um token jwt, acontece que meus filtros parecem executar duas vezes na mesma requisição, e a primeira o header ta sempre null, na segundo consigo recuperar o token normalmente, como corrigir isso ?

public class SecurityConfig extends WebSecurityConfigurerAdapter 
 @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.csrf().disable();

        http.authorizeRequests()
            .antMatchers("/home").permitAll()
            .antMatchers(HttpMethod.POST, "/login").permitAll()
            .antMatchers(HttpMethod.GET, "/doc/listar/**").hasRole("USER")
            .antMatchers(HttpMethod.GET, "/doc/enviar/**").hasRole("USER")
            .antMatchers(HttpMethod.GET, "/doc/autenticar/**").hasRole("USER")
            .antMatchers(HttpMethod.GET, "/doc/excluir/**").hasRole("USER")
            .anyRequest().authenticated()
            .and()

        .addFilterBefore(new CORSFilter(), ChannelProcessingFilter.class)

        .addFilterBefore(new JWTLoginFilter("/login", authenticationManager(),tokenAuthenticationService),
                UsernamePasswordAuthenticationFilter.class)

        .addFilterBefore(new JWTAuthenticationFilter(tokenService),
                UsernamePasswordAuthenticationFilter.class);

    }

//Filtro que verifica o token
public class JWTAuthenticationFilter extends GenericFilterBean

 @Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {

    try {
        Authentication authentication = tokenAuthenticationService.getAuthentication((HttpServletRequest) req);
            if(authentication != null){
            SecurityContextHolder.getContext().setAuthentication(authentication);
            if(authentication != null && !authentication.isAuthenticated()){
                SecurityContextHolder.clearContext();
                ((HttpServletResponse) res).setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            }else {
                chain.doFilter(req, res);
            }
        }
    } catch (AuthenticationException | JwtException e) {
        SecurityContextHolder.clearContext();
        ((HttpServletResponse) res).setStatus(HttpServletResponse.SC_UNAUTHORIZED);
    }
}

Método que verifica o token
public Authentication getAuthentication(HttpServletRequest request) {
        String token = request.getHeader(HEADER_STRING);

        //Verificar se esta nulo (primeira execuçãp sempre nulo)
        if (token != null) {
            if(token.isEmpty()) {
                return new UsernamePasswordAuthenticationToken(token, token);
            }
            String user = Jwts.parser()
                    .setSigningKey(SECRET)
                    .parseClaimsJws(token.replace(TOKEN_PREFIX, ""))

                    .getBody()
                    .getSubject();

            if (user != null) {
                return new UsernamePasswordAuthenticationToken(user, null, Collections.emptyList());
            }
        }
            return null;
    }
4 respostas

Olá Raphael, tudo bem?

Quem gera o JWT inicial? A aplicação Angular ou sua parte Java?

Pergunto pois, se for o Java, sempre que for a primeira requisição o header vai ser nulo. Motivo: o Token seria gerado no Java, devolvido pro Angular e só a partir daí é que ele seria enviado pelo Angular (2o request em diante).

Se não for isso, daria uma olhada nessas linhas: ".addFilterBefore(new JWTLoginFilter ..." e ".addFilterBefore(new JWTAuthenticationFilter ...". Ambas parecem lidar com token JWT. Os dois filtros são realmente necessários?

Abraço,

Rafael.

Sim o java gera o token e envia para o Angular, no angular eu consigo pegar o token sem problemas, o problema acontece quando eu faço uma requisição do angular para o java, em uma pesquisa por exemplo, o filtro

.addFilterBefore(new JWTAuthenticationFilter

serve para inteceptar essas requisições e verifacar se existe um token, no debug quando chega no trecho

 String token = request.getHeader(HEADER_STRING);

o conteudo está nulo, eu dou um resume no debug e a aplicação entra nesse trecho de código uma segunda vez, agora com o token preenchido. Eu não estou conseguindo entender porque acontece essa segunda vez, e só nela existe um token, por isso eu fiz varios verificações de

algumCodigo != null

o filtro

new JWTLoginFilter

eu uso quando a aplicação faz um post na url /login para autenticar o usuario.

Ah tá. Entendi melhor os dois filtros, Raphael.

Então, quando você faz if(authentication != null && !authentication.isAuthenticated()), você não deveria adicionar o objeto de autenticação no contexto? Algo como:

SecurityContextHolder.getContext().setAuthentication(authentication);

Vi que você limpa o contexto. Acho que é pra fazer justamente o oposto.

Além disso, vi que você altera o status da Response em:

((HttpServletResponse) res).setStatus(HttpServletResponse.SC_UNAUTHORIZED);

Não deveria também adicionar um header a ela? Algo como:

res.addHeader("Token", token.serialize());

Talvez o "SecurityContextHolder.clearContext();" também não seja necessário.

Tenta adicionar isso também no método configure:

http.csrf().disable();

Avisa aí quando tiver novas.

Abraço,

Rafael.

Olá consegui resolver este meu problema, precisei fazer algumas mudanças no angular e uma no java

No angular depois de algumas implemetações para "customizar" o cabeçalho esta funcionou para mim

https://medium.com/beautiful-angular/angular-2-and-jwt-authentication-d30c21a2f24f

já no java eu não sei bem explicar o porque funcionou com a mudança que eu fiz, mas funcionou, eu mudei a forma como implementava o cors filter

de:

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CORSFilter implements Filter {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Allow-Methods", "*");
        //response.setHeader("Access-Control-Allow-Headers","authorization, Content-Type");
        response.setHeader("Access-Control-Allow-Headers", "Authorization,authorization, Content-Type, Access-Control-Allow-Origin, Access-Control-Allow-Credentials, Access-Control-Allow-Headers,Access-Control-Allow-Methods");
        //response.setHeader("Access-Control-Expose-Headers","Authorization,authorization");
        filterChain.doFilter(servletRequest, servletResponse);
    }

    public void destroy() {}

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

}

para:

@Bean
    public FilterRegistrationBean corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedOrigin("*");
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        config.addExposedHeader("Authorization");
        source.registerCorsConfiguration("/**", config);
        FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
        //jogando a ordem lá para baixo, tem que rodar do filtro do security.
        bean.setOrder(-100000);
        return bean;
    }

eu já tinha testado com a segunda forma e não tinha funcionado, mas um trecho de código parece está fazendo toda a diferença que eu não tinha utilizado antes.

bean.setOrder(-100000);

na primeira forma eu anotei com @Order(Ordered.HIGHEST_PRECEDENCE), eu coloquei a precedencia bem alta porque eu achava que era muito importante o CorsFilter funcionar primeiro para não acontecer problema de CROSS ORIGIN, e a segunda forma que é tirado do curso de React da própria plataforma, joga a ordem la pra baixa com diz o próprio comentário, não sei exatamente o porque, mas agora está funcionando. :)

Ah, a implementação do Cors que eu tirei do exemplo do react não tinha o trecho:

config.addExposedHeader("Authorization");

precisei adicionar também.