Estou escrevendo um webapp em Angular em que a autenticação é controlada por um token JWT, o que significa que cada solicitação tem um cabeçalho de "Autenticação" com todas as informações necessárias.
Isso funciona bem para chamadas REST, mas não entendo como devo lidar com links de download para arquivos hospedados no back-end (os arquivos residem no mesmo servidor onde os serviços da web estão hospedados).
Não posso usar <a href='...'/>
links regulares, pois eles não carregam nenhum cabeçalho e a autenticação falhará. O mesmo vale para os vários encantamentos de window.open(...)
.
Algumas soluções que pensei:
- Gerar um link de download não seguro temporário no servidor
- Passe as informações de autenticação como um parâmetro de url e lide manualmente com o caso
- Obtenha os dados por meio de XHR e salve o arquivo do lado do cliente.
Todos os itens acima são menos que satisfatórios.
1 é a solução que estou usando agora. Não gosto disso por dois motivos: primeiro não é o ideal em termos de segurança, segundo funciona, mas requer bastante trabalho, especialmente no servidor: para baixar algo, preciso chamar um serviço que gera um novo "aleatório "url, armazena em algum lugar (possivelmente no banco de dados) por algum tempo e o devolve ao cliente. O cliente obtém o url e usa window.open ou semelhante com ele. Quando solicitado, o novo url deve verificar se ainda é válido e, em seguida, retornar os dados.
2 parece pelo menos tanto trabalho.
3 parece muito trabalhoso, mesmo usando as bibliotecas disponíveis, e muitos problemas potenciais. (Eu precisaria fornecer minha própria barra de status de download, carregar o arquivo inteiro na memória e então pedir ao usuário para salvar o arquivo localmente).
A tarefa parece bastante básica, então estou me perguntando se há algo muito mais simples que eu possa usar.
Não estou necessariamente procurando uma solução "do jeito Angular". Javascript regular seria bom.
fonte
Respostas:
Esta é uma maneira de baixá-lo no cliente usando o atributo download , a API fetch e URL.createObjectURL . Você deve buscar o arquivo usando seu JWT, converter a carga útil em um blob, colocar o blob em um objectURL, definir a origem de uma tag âncora para esse objectURL e clicar nesse objectURL em javascript.
O valor do
download
atributo será o nome do arquivo eventual. Se desejar, você pode extrair um nome de arquivo pretendido do cabeçalho de resposta de disposição de conteúdo, conforme descrito em outras respostas .fonte
Técnica
Com base neste conselho de Matias Woloski de Auth0, conhecido evangelista do JWT, resolvi o problema gerando um pedido assinado com Hawk .
Citando Woloski:
Aqui você tem um exemplo dessa técnica, usada para links de ativação.
Processo interno
Eu criei uma API para assinar meus urls de download:
Solicitação:
Resposta:
Com um URL assinado, podemos obter o arquivo
Solicitação:
Resposta:
frontend (por jojoyuji )
Dessa forma, você pode fazer tudo com um único clique do usuário:
fonte
Uma alternativa às abordagens existentes "fetch / createObjectURL" e "download-token" já mencionadas é um Form POST padrão que visa uma nova janela . Assim que o navegador ler o cabeçalho do anexo na resposta do servidor, ele fechará a nova guia e iniciará o download. Essa mesma abordagem também funciona bem para exibir um recurso como um PDF em uma nova guia.
Isso oferece melhor suporte para navegadores mais antigos e evita a necessidade de gerenciar um novo tipo de token. Isso também terá um suporte de longo prazo melhor do que a autenticação básica no URL, uma vez que o suporte para nome de usuário / senha no url está sendo removido pelos navegadores .
No lado do cliente , usamos
target="_blank"
para evitar a navegação mesmo em casos de falha, o que é particularmente importante para SPAs (aplicativos de página única).A principal ressalva é que a validação do JWT do lado do servidor deve obter o token dos dados POST e não do cabeçalho . Se sua estrutura gerencia o acesso aos roteadores de rota automaticamente usando o cabeçalho de autenticação, pode ser necessário marcar seu manipulador como não autenticado / anônimo para que possa validar manualmente o JWT para garantir a autorização adequada.
O formulário pode ser criado dinamicamente e imediatamente destruído para que seja devidamente limpo (observação: isso pode ser feito em JS simples, mas JQuery é usado aqui para maior clareza) -
Basta adicionar quaisquer dados extras que você precisa enviar como entradas ocultas e certifique-se de que eles sejam anexados ao formulário.
fonte
Gostaria de gerar tokens para download.
No angular, faça uma solicitação autenticada para obter um token temporário (digamos uma hora) e adicione-o ao url como um parâmetro get. Desta forma, você pode baixar arquivos da maneira que quiser (window.open ...)
fonte
Uma solução adicional: usando autenticação básica. Embora exija um pouco de trabalho no back-end, os tokens não serão visíveis nos logs e nenhuma assinatura de URL terá que ser implementada.
Cliente
Um exemplo de URL poderia ser:
http://jwt:<user jwt token>@some.url/file/35/download
Exemplo com token fictício:
http://jwt:eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIwIiwibmFtZSI6IiIsImlhdCI6MH0.KsKmQOZM-jcy4l_7NFsv1lWfpH8ofniVCv75ZRQrWno@some.url/file/35/download
Você pode então enfiar isso
<a href="...">
ouwindow.open("...")
- o navegador cuida do resto.Lado do Servidor
A implementação aqui depende de você e depende da configuração do seu servidor - não é muito diferente de usar o
?token=
parâmetro de consulta.Usando o Laravel, peguei o caminho mais fácil e transformei a senha de autenticação básica no
Authorization: Bearer <...>
cabeçalho JWT , deixando o middleware de autenticação normal cuidar do resto:fonte