JWT 기반 인증으로 파일 다운로드를 처리하는 방법은 무엇입니까?
저는 인증이 JWT 토큰에 의해 처리되는 Angular에서 webapp을 작성하고 있습니다. 즉, 모든 요청에는 필요한 모든 정보가 포함 된 "Authentication"헤더가 있습니다.
이것은 REST 호출에서 잘 작동하지만 백엔드에서 호스팅되는 파일에 대한 다운로드 링크를 처리하는 방법을 이해하지 못합니다 (파일은 웹 서비스가 호스팅되는 동일한 서버에 있습니다).
일반 <a href='...'/>
링크는 헤더가 없어 인증이 실패하기 때문에 사용할 수 없습니다 . 의 다양한 주문에 대해서도 동일합니다 window.open(...)
.
내가 생각한 몇 가지 솔루션 :
- 서버에 보안되지 않은 임시 다운로드 링크 생성
- 인증 정보를 URL 매개 변수로 전달하고 케이스를 수동으로 처리합니다.
- XHR을 통해 데이터를 가져오고 파일 클라이언트 측에 저장합니다.
위의 모든 것이 만족스럽지 않습니다.
1은 내가 지금 사용하고있는 솔루션입니다. 나는 두 가지 이유로 좋아하지 않습니다. 첫째는 보안 상 이상적이지 않고, 둘째는 작동하지만 특히 서버에서 상당히 많은 작업이 필요합니다. 새로운 "무작위 생성 서비스를 호출해야하는 것을 다운로드하려면 "url, 어딘가에 (아마도 DB에) 저장 한 다음 클라이언트에 반환합니다. 클라이언트는 URL을 가져 와서 window.open 또는 이와 유사한 것을 사용합니다. 요청시 새 URL은 여전히 유효한지 확인한 다음 데이터를 반환해야합니다.
2는 적어도 일이 많은 것 같습니다.
3은 사용 가능한 라이브러리를 사용하는 경우에도 많은 작업과 많은 잠재적 문제로 보입니다. (내 자신의 다운로드 상태 표시 줄을 제공하고 전체 파일을 메모리에로드 한 다음 사용자에게 파일을 로컬에 저장하도록 요청해야합니다.)
이 작업은 매우 기본적인 작업으로 보이므로 사용할 수있는 훨씬 더 간단한 작업이 있는지 궁금합니다.
필자는 "각도 방식"솔루션을 반드시 찾고있는 것은 아닙니다. 일반 Javascript가 좋습니다.
다음 은 download 속성 , fetch API 및 URL.createObjectURL을 사용 하여 클라이언트에 다운로드하는 방법 입니다. JWT를 사용하여 파일을 가져오고, 페이로드를 blob으로 변환하고, blob을 objectURL에 넣고, 앵커 태그의 소스를 해당 objectURL로 설정하고, 자바 스크립트에서 해당 objectURL을 클릭합니다.
let anchor = document.createElement("a");
document.body.appendChild(anchor);
let file = 'https://www.example.com/some-file.pdf';
let headers = new Headers();
headers.append('Authorization', 'Bearer MY-TOKEN');
fetch(file, { headers })
.then(response => response.blob())
.then(blobby => {
let objectUrl = window.URL.createObjectURL(blobby);
anchor.href = objectUrl;
anchor.download = 'some-file.pdf';
anchor.click();
window.URL.revokeObjectURL(objectUrl);
});
download
속성 값은 최종 파일 이름이됩니다. 원하는 경우 다른 답변에 설명 된대로 콘텐츠 처리 응답 헤더에서 의도 한 파일 이름을 마이닝 할 수 있습니다 .
기술
JWT 전도사로 알려진 Auth0의 Matias Woloski의 조언 을 바탕으로 Hawk 와 서명 된 요청을 생성하여 문제를 해결했습니다 .
Woloski 인용 :
이를 해결하는 방법은 예를 들어 AWS와 같이 서명 된 요청을 생성하는 것입니다.
여기 활성화 링크에 사용되는이 기술 의 예가 있습니다.
백엔드
다운로드 URL에 서명하는 API를 만들었습니다.
의뢰:
POST /api/sign
Content-Type: application/json
Authorization: Bearer...
{"url": "https://path.to/protected.file"}
응답:
{"url": "https://path.to/protected.file?bewit=NTUzMDYzZTQ2NDYxNzQwMGFlMDMwMDAwXDE0NTU2MzU5OThcZDBIeEplRHJLVVFRWTY0OWFFZUVEaGpMOWJlVTk2czA0cmN6UU4zZndTOD1c"}
서명 된 URL로 파일을 가져올 수 있습니다.
의뢰:
GET https://path.to/protected.file?bewit=NTUzMDYzZTQ2NDYxNzQwMGFlMDMwMDAwXDE0NTU2MzU5OThcZDBIeEplRHJLVVFRWTY0OWFFZUVEaGpMOWJlVTk2czA0cmN6UU4zZndTOD1c
응답:
Content-Type: multipart/mixed; charset="UTF-8"
Content-Disposition': attachment; filename=protected.file
{BLOB}
프론트 엔드 (by jojoyuji )
이렇게하면 한 번의 사용자 클릭으로 모든 작업을 수행 할 수 있습니다.
function clickedOnDownloadButton() {
postToSignWithAuthorizationHeader({
url: 'https://path.to/protected.file'
}).then(function(signed) {
window.location = signed.url;
});
}
다운로드 할 토큰을 생성합니다.
angular 내에서 임시 토큰을 얻기 위해 인증 된 요청 (예 : 1 시간)을 만든 다음 URL에 get 매개 변수로 추가합니다. 이렇게하면 원하는 방식으로 파일을 다운로드 할 수 있습니다 (window.open ...).
An additional solution: using basic authentication. Although it requires a bit of work on the backend, tokens won't be visible in logs and no URL signing will have to be implemented.
Client Side
An example URL could be:
http://jwt:<user jwt token>@some.url/file/35/download
Example with dummy token:
http://jwt:eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIwIiwibmFtZSI6IiIsImlhdCI6MH0.KsKmQOZM-jcy4l_7NFsv1lWfpH8ofniVCv75ZRQrWno@some.url/file/35/download
You can then shove this in <a href="...">
or window.open("...")
- the browser handles the rest.
Server Side
Implementation here is up to you, and is dependent on your server setup - it's not too much different from using the ?token=
query parameter.
Using Laravel, I went the easy route and transformed the basic authentication password into the JWT Authorization: Bearer <...>
header, letting the normal authentication middleware handle the rest:
class CarryBasic
{
/**
* @param Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, \Closure $next)
{
// if no basic auth is passed,
// or the user is not "jwt",
// send a 401 and trigger the basic auth dialog
if ($request->getUser() !== 'jwt') {
return $this->failedBasicResponse();
}
// if there _is_ basic auth passed,
// and the user is JWT,
// shove the password into the "Authorization: Bearer <...>"
// header and let the other middleware
// handle it.
$request->headers->set(
'Authorization',
'Bearer ' . $request->getPassword()
);
return $next($request);
}
/**
* Get the response for basic authentication.
*
* @return void
* @throws \Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException
*/
protected function failedBasicResponse()
{
throw new UnauthorizedHttpException('Basic', 'Invalid credentials.');
}
}
'IT박스' 카테고리의 다른 글
ASP.NET MVC에서 동적 로그인 URL로 리디렉션하는 방법 (0) | 2020.08.29 |
---|---|
Java EE는 실제로 무엇입니까? (0) | 2020.08.29 |
문서 기반 데이터베이스와 키 / 값 기반 데이터베이스의 차이점은 무엇입니까? (0) | 2020.08.29 |
왜 map, fmap 및 liftM이 있습니까? (0) | 2020.08.29 |
32 비트 레지스터의 x86-64 명령어가 전체 64 비트 레지스터의 위쪽 부분을 0으로 만드는 이유는 무엇입니까? (0) | 2020.08.29 |