Fixando 401s com comprovações CORS e segurança de mola
1. Visão geral
Neste breve tutorial, aprenderemos como resolver o erro “Resposta para comprovação com código de status HTTP inválido 401”, que pode ocorrer em aplicativos que suportam comunicação de origem cruzada e usam Spring Security.
Primeiro, veremos o que são solicitações de origem cruzada e, em seguida, corrigiremos um exemplo problemático.
2. Solicitações de origem cruzada
Solicitações de origem cruzada, em resumo, são solicitações HTTP em que a origem e o destino da solicitação são diferentes. É o caso, por exemplo, quando um aplicativo Web é servido em um domínio e o navegador envia uma solicitação AJAX para um servidor em outro domínio.
Para gerenciar solicitações de origem cruzada, o servidor precisa habilitar um mecanismo específico conhecido como CORS, ou Compartilhamento de Recursos de Origem Cruzada.
A primeira etapa no CORS é uma solicitaçãoOPTIONS para determinar se o destino da solicitação oferece suporte. This is called a pre-flight request.
O servidor pode responder à solicitação de pré-vôo com uma coleção de cabeçalhos:
-
Access-Control-Allow-Origin: Define quais origens podem ter acesso ao recurso. A ‘* 'representa qualquer origem
-
Access-Control-Allow-Methods: Indica os métodos HTTP permitidos para solicitações de origem cruzada
-
Access-Control-Allow-Headers: Indica os cabeçalhos de solicitação permitidos para solicitações de origem cruzada
-
Access-Control-Max-Age: Define o tempo de expiração do resultado da solicitação de comprovação em cache
Portanto, se a solicitação pré-voo não atender às condições determinadas a partir desses cabeçalhos de resposta, a solicitação de acompanhamento real gerará erros relacionados à solicitação de origem cruzada.
É fáciladd CORS support to our Spring-powered service, mas se configurado incorretamente, essa solicitação pré-voo sempre falhará com um 401.
3. Criando uma API REST ativada para CORS
Para simular o problema, vamos primeiro criar uma API REST simples que suporte solicitações de origem cruzada:
@RestController
@CrossOrigin("http://localhost:4200")
public class ResourceController {
@GetMapping("/user")
public String user(Principal principal) {
return principal.getName();
}
}
A anotação@CrossOrigin garante que nossas APIs sejam acessíveis apenas a partir da origem mencionada em seu argumento.
4. Protegendo nossa API REST
Agora vamos proteger nossa API REST com Spring Security:
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic();
}
}
Nesta classe de configuração, aplicamos a autorização a todas as solicitações recebidas. Como resultado, rejeitará todas as solicitações sem um token de autorização válido.
5. Fazendo uma solicitação antes do voo
Agora que criamos nossa API REST, vamos tentar uma solicitação pré-voo usandocurl:
curl -v -H "Access-Control-Request-Method: GET" -H "Origin: http://localhost:4200"
-X OPTIONS http://localhost:8080/user
...
< HTTP/1.1 401
...
< WWW-Authenticate: Basic realm="Realm"
...
< Vary: Origin
< Vary: Access-Control-Request-Method
< Vary: Access-Control-Request-Headers
< Access-Control-Allow-Origin: http://localhost:4200
< Access-Control-Allow-Methods: POST
< Access-Control-Allow-Credentials: true
< Allow: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH
...
A partir da saída deste comando, podemos ver quethe request was denied with a 401.
Como este é um comandocurl, não veremos o erro “Resposta para comprovação com código de status HTTP inválido 401” na saída.
Mas podemos reproduzir esse erro exato criando um aplicativo front-end que consome nossa API REST de um domínio diferente e executando-o em um navegador.
6. A solução
We haven’t explicitly excluded the preflight requests from authorization in our Spring Security configuration. Lembre-se de que o Spring Security protegeall pontos de envio por padrão.
Como resultado,our API expects an authorization token in the OPTIONS request as well.
O Spring fornece uma solução pronta para excluir pedidos OPTIONS das verificações de autorização:
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// ...
http.cors();
}
}
O métodocors() adicionará oCorsFilter fornecido pelo Spring ao contexto do aplicativo que, por sua vez, ignora as verificações de autorização para solicitações OPTIONS.
Agora podemos testar nosso aplicativo novamente e ver se ele está funcionando.
7. Conclusão
Neste breve artigo, aprendemos como corrigir o erro “Resposta para comprovação com código de status HTTP inválido 401”, que está vinculado ao Spring Security e solicitações de origem cruzada.
Note that, with the example, the client and the API should run on different domains or ports to recreate the problem. Por exemplo, podemos mapear o nome do host padrão para o cliente e o endereço IP da máquina para nossa API REST ao executar em uma máquina local.
Como sempre, o exemplo mostrado neste tutorial pode ser encontradoover on Github.