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

[Duvida] Api com API_KEY

Como seria um exemplo de uma api spring boot com api key? Seria necessário toda configuração que realizamos no projeto do curso?

3 respostas

Oi!

Pode dar mais detalhes sobre API_KEY? Qual seria o cenário de uso que você está pensando?

Oi Rodrigo, o cenário seria o seguinte, precisava passar valores no cabeçalho da requisição para que um fiiltro realizasse essa validação. Mas nesse caso seriam 2 cabeçalhos distintos, pois isso iria depender de onde a API seria chamada, nesse caso tenho 2 cenários: 1- ela será chamada de dentro do banco de dados oracle, ae neste caso ele passaria um cabecalho. 2 - ela seria chamada via postman ou ser consumida por outra api.

Eu consegui fazer com apenas um cabeçalho, só alterando o valor de acordo com ela é chamada, mas minha duvida seria como melhorar este código para incluir 2 cabeçalhos distintios ao invés de ficar setando os valroes.

Segue os códigos

public class KeyAuthFilter extends AbstractPreAuthenticatedProcessingFilter{

    private final String headerName;
    
     public KeyAuthFilter(final String headerName) {
            this.headerName = headerName;
        }

     @Override
        protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
            return request.getHeader(headerName);
        }

        @Override
        protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
            // No creds when using API key
            return null;
        }


}
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {

    
        @Value("${http.auth-token-header-name}")
        private String principalRequestHeader;

        @Value("${http.auth-token}")
        private String principalRequestValue;

        @Value("${http.auth-token-credenciais-list}")
        private List<String> credenciais;
        
        private boolean isAutenticar;
        
        @Bean
        @Order
        public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        	
        	KeyAuthFilter filter = new KeyAuthFilter(principalRequestHeader);
        	
        	
        
        	  
        	filter.setAuthenticationManager(new AuthenticationManager() {
                
                @Override
                public Authentication authenticate(Authentication authentication) throws AuthenticationException {
                    String principal = (String) authentication.getPrincipal();
                    
                    
                                        
                    if(verificarCredencial(principal)) {						
                        authentication.setAuthenticated(true);
                        return authentication;
                    }
                    
                    if (!principalRequestValue.equals(principal)) {
                        authentication.setAuthenticated(false);
                        throw new BadCredentialsException("The API key was not found or not the expected value.");
                    }
                    
                        authentication.setAuthenticated(true);
                        return authentication;

                }
            });
        	
        
        
        	
        	
        	 http.csrf((csrf) -> csrf.disable())
             	 .httpBasic(b-> b.disable())
             	 .formLogin(f-> f.disable())
             	 .logout(l-> l.disable())
                 .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                 .authorizeHttpRequests(authorize -> authorize.anyRequest().authenticated())
                 .addFilter(filter)
                ;
                 
              
                http.requiresChannel(
                        req -> req.requestMatchers(r -> r.getHeader("X-Forwarded-Proto") != null).requiresSecure());
                 
                
                 return http.build();
     

             
        }

        
       
        
        @Bean
        public WebSecurityCustomizer webSecurityCustomizer() {
            return (web) -> web.ignoring().requestMatchers("/actuator", "/actuator/**","/v3/api-docs/**", "/swagger-ui.html", "/swagger-ui/**");
        }

        
        private boolean verificarCredencial(String segundaApiKey) {
            isAutenticar = false;

            credenciais.forEach(e -> {

                if (e.equals(segundaApiKey)) {
                    isAutenticar = true;

                }
            });

            return isAutenticar;
        }
        
        
       
        
        
}
solução!

Para incluir dois cabeçalhos distintos no seu código, você pode fazer algumas modificações no filtro e na configuração de segurança.

Primeiro, vamos modificar a classe KeyAuthFilter para receber dois cabeçalhos ao invés de apenas um. Podemos fazer isso criando dois atributos headerName1 e headerName2 na classe e modificando os métodos getPreAuthenticatedPrincipal() e getPreAuthenticatedCredentials() para retornar os valores dos cabeçalhos correspondentes:

public class KeyAuthFilter extends AbstractPreAuthenticatedProcessingFilter {

    private final String headerName1;
    private final String headerName2;

    public KeyAuthFilter(final String headerName1, final String headerName2) {
        this.headerName1 = headerName1;
        this.headerName2 = headerName2;
    }

    @Override
    protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
        String header1 = request.getHeader(headerName1);
        String header2 = request.getHeader(headerName2);
        // Retorne uma classe que encapsule os valores dos cabeçalhos, se necessário
        return new HeaderValues(header1, header2);
    }

    @Override
    protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
        // No creds when using API key
        return null;
    }
}

A classe HeaderValues é uma classe que você pode criar para armazenar os valores dos cabeçalhos. Ela pode ter os atributos header1 e header2 e os respectivos métodos getters e setters.

Em seguida, vamos modificar a classe SecurityConfig para criar uma instância de KeyAuthFilter com os dois cabeçalhos e configurar o AuthenticationManager com base nesses valores:

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {

    @Value("${http.auth-token-header-name1}")
    private String principalRequestHeader1;

    @Value("${http.auth-token-header-name2}")
    private String principalRequestHeader2;

    @Value("${http.auth-token1}")
    private String principalRequestValue1;

    @Value("${http.auth-token2}")
    private String principalRequestValue2;

    // Resto do código...

    @Bean
    @Order
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        KeyAuthFilter filter = new KeyAuthFilter(principalRequestHeader1, principalRequestHeader2);

        filter.setAuthenticationManager(new AuthenticationManager() {

            @Override
            public Authentication authenticate(Authentication authentication) throws AuthenticationException {
                HeaderValues headerValues = (HeaderValues) authentication.getPrincipal();
                String header1 = headerValues.getHeader1();
                String header2 = headerValues.getHeader2();

                if (verificarCredencial(header1, header2)) {
                    authentication.setAuthenticated(true);
                    return authentication;
                }

                if (!principalRequestValue1.equals(header1) || !principalRequestValue2.equals(header2)) {
                    authentication.setAuthenticated(false);
                    throw new BadCredentialsException("The API key was not found or not the expected value.");
                }

                authentication.setAuthenticated(true);
                return authentication;
            }
        });

        // Resto do código...
    }

    // Resto do código...

    private boolean verificarCredencial(String segundaApiKey1, String segundaApiKey2) {
        isAutenticar = false;

        credenciais.forEach(e -> {
            if (e.equals(segundaApiKey1) && e.equals(segundaApiKey2)) {
                isAutenticar = true;
            }
        });

        return isAutenticar;
    }
}

Lembre-se de ajustar as propriedades http.auth-token-header-name1, http.auth-token-header-name2, http.auth-token1 e http.auth-token2 no arquivo de configuração para corresponderem aos nomes dos cabeçalhos e aos valores esperados.

Essas modificações permitem que você tenha dois cabeçalhos distintos e execute a validação correta de acordo com o cenário em que a API é chamada.