JWT आधारित प्रमाणीकरण के साथ फ़ाइल डाउनलोड कैसे संभालें?


116

मैं Angular में एक webapp लिख रहा हूँ जहाँ प्रमाणीकरण को JWT टोकन द्वारा नियंत्रित किया जाता है, जिसका अर्थ है कि प्रत्येक अनुरोध में सभी आवश्यक जानकारी के साथ "प्रमाणीकरण" शीर्षक है।

यह REST कॉल के लिए अच्छी तरह से काम करता है, लेकिन मुझे समझ में नहीं आता है कि मुझे बैकएंड पर होस्ट की गई फ़ाइलों के लिए डाउनलोड लिंक को कैसे संभालना चाहिए (फाइलें उसी सर्वर पर रहती हैं जहां वेबसर्विस होस्ट की जाती हैं)।

मैं नियमित <a href='...'/>लिंक का उपयोग नहीं कर सकता क्योंकि वे कोई हेडर नहीं लेंगे और प्रमाणीकरण विफल हो जाएगा। के विभिन्न विसंगतियों के लिए भी window.open(...)

कुछ समाधान मैंने सोचा:

  1. सर्वर पर एक अस्थायी असुरक्षित डाउनलोड लिंक उत्पन्न करें
  2. प्रमाणीकरण सूचना को url पैरामीटर के रूप में पास करें और मामले को मैन्युअल रूप से हैंडल करें
  3. XHR के माध्यम से डेटा प्राप्त करें और फ़ाइल क्लाइंट पक्ष को सहेजें।

उपरोक्त सभी संतोषजनक से कम हैं।

1 समाधान मैं अभी उपयोग कर रहा हूँ। मुझे यह दो कारणों से पसंद नहीं है: पहला यह आदर्श सुरक्षा-वार नहीं है, दूसरा यह काम करता है, लेकिन इसके लिए विशेष रूप से सर्वर पर काफी काम करने की आवश्यकता होती है: कुछ डाउनलोड करने के लिए मुझे एक सेवा को कॉल करने की आवश्यकता होती है जो एक नया "यादृच्छिक" उत्पन्न करती है "url, इसे कहीं (संभवतः DB पर) कुछ समय के लिए संग्रहीत करता है, और इसे क्लाइंट को लौटाता है। क्लाइंट को url मिलता है, और उसके साथ window.open या समान का उपयोग करें। जब अनुरोध किया जाता है, तो नए url को जांचना चाहिए कि क्या यह अभी भी मान्य है, और फिर डेटा वापस लौटाएं।

2 कम से कम उतना काम लगता है।

3 बहुत काम लगता है, यहां तक ​​कि उपलब्ध पुस्तकालयों का उपयोग करते हुए, और बहुत सारे संभावित मुद्दे। (मुझे अपनी स्वयं की डाउनलोड स्थिति पट्टी प्रदान करने की आवश्यकता होगी, पूरी फ़ाइल को मेमोरी में लोड करें और फिर उपयोगकर्ता से स्थानीय रूप से फ़ाइल को बचाने के लिए कहें)।

हालांकि यह एक बहुत ही महत्वपूर्ण कार्य है, इसलिए मैं सोच रहा हूं कि क्या कुछ सरल है जिसका मैं उपयोग कर सकता हूं।

मैं जरूरी नहीं कि एक समाधान "कोणीय रास्ता" की तलाश कर रहा हूं। नियमित जावास्क्रिप्ट ठीक रहेगा।


रिमोट से क्या आपका मतलब है कि डाउनलोड करने योग्य फाइलें एंगुलर ऐप से अलग डोमेन पर हैं? क्या आप रिमोट को नियंत्रित करते हैं (यह बैकएंड को संशोधित करने के लिए एक्सेस है) या नहीं?
रोबर्टजड

मेरा मतलब है कि फ़ाइल डेटा क्लाइंट (ब्राउज़र) पर नहीं है; फ़ाइल उसी डोमेन पर होस्ट की गई है और मेरे पास बैकएंड का नियंत्रण है। मैं प्रश्न को कम अस्पष्ट बना दूंगा।
मार्को रिगले

विकल्प 2 की कठिनाई आपके बैकएंड पर निर्भर है। यदि आप अपने बैकएंड को JWT के ऑथराइज़ेशन हेडर के अलावा क्वेरी स्ट्रिंग की जांच करने के लिए कह सकते हैं, जब यह ऑथेंटिकेशन लेयर से गुजरता है, तो आप कर रहे हैं। आप किस बैकेंड का उपयोग कर रहे हैं?
टेक्नेटियम

जवाबों:


47

यहां क्लाइंट पर डाउनलोड एट्रिब्यूट , fetch API और URL.createObjectURL का उपयोग करके इसे डाउनलोड करने का एक तरीका है । आप अपने JWT का उपयोग करके फ़ाइल लाएंगे, पेलोड को एक बूँद में बदल देंगे, बूँद को एक objecturL में डाल देंगे, उस ऑब्जेक्ट पर एक एंकर टैग का स्रोत सेट करेंगे और उस ऑब्जेक्ट पर jurascript में क्लिक करेंगे।

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विशेषता का मान अंतिम फ़ाइल नाम होगा। यदि वांछित है, तो आप अन्य उत्तरों में वर्णित सामग्री विवाद प्रतिक्रिया हेडर से बाहर एक इच्छित फ़ाइल नाम को खान कर सकते हैं ।


1
मैं सोचता रहता हूं कि कोई भी इस प्रतिक्रिया को क्यों नहीं मानता। यह सरल है और चूंकि हम 2017 में रह रहे हैं, इसलिए मंच का समर्थन काफी अच्छा है।
रफाल पास्टूसजक

1
लेकिन डाउनलोड विशेषता के लिए iosSafari समर्थन सुंदर लाल दिखता है :(
मार्टिन क्रमर

1
यह क्रोम में मेरे लिए ठीक काम किया। फ़ायरफ़ॉक्स के लिए यह काम करने के बाद मैंने एंकर को दस्तावेज़ में जोड़ा: डॉक्यूमेंट.बॉडी.प्पेंडचाइल्ड (एंकर); एज के लिए कोई हल नहीं मिला ...
टॉम्पी

12
यह समाधान काम करता है लेकिन क्या यह समाधान बड़ी फ़ाइलों के साथ यूएक्स चिंताओं को संभालता है? यदि मुझे कभी-कभी 300MB फ़ाइल डाउनलोड करने की आवश्यकता होती है, तो लिंक पर क्लिक करने से पहले उसे डाउनलोड करने और ब्रॉउजर के डाउनलोड मैनेजर पर भेजने में कुछ समय लग सकता है। हम लाने-ले जाने के प्रयास में खर्च कर सकते हैं और अपनी स्वयं की डाउनलोड प्रगति UI का निर्माण कर सकते हैं .. लेकिन फिर इसे डाउनलोड करने के लिए केवल हाथ में लाने के लिए एक 300mb फ़ाइल को js-land (मेमोरी में?) में लोड करने का सवाल है? प्रबंधक।
21

1
मैं भी @Tompi एज और IE के लिए यह काम नहीं कर सके
ज़प्पा

34

तकनीक

जेडब्ल्यूटी इंजीलवादी के रूप में जाने जाने वाले जेडब्ल्यूटी इंजील के मतिस वोलोस्की की इस सलाह के आधार पर , मैंने हॉक के लिए एक हस्ताक्षरित अनुरोध उत्पन्न करके इसे हल किया ।

वोटोस्की का उद्धरण:

जिस तरह से आप इसे हल करते हैं, उदाहरण के लिए, AWS की तरह एक हस्ताक्षरित अनुरोध उत्पन्न करके होता है।

यहां आपके पास इस तकनीक का एक उदाहरण है, जिसका उपयोग सक्रियण लिंक के लिए किया जाता है।

बैकएंड

मैंने अपने डाउनलोड यूआरएल पर हस्ताक्षर करने के लिए एक एपीआई बनाया:

निवेदन:

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}

सीमा ( जोजयूजी द्वारा )

इस तरह से आप यह सब एक उपयोगकर्ता क्लिक पर कर सकते हैं:

function clickedOnDownloadButton() {

  postToSignWithAuthorizationHeader({
    url: 'https://path.to/protected.file'
  }).then(function(signed) {
    window.location = signed.url;
  });

}

2
यह अच्छा है, लेकिन मुझे यह समझ में नहीं आता कि ओपी के विकल्प # 2 (टोकन स्ट्रिंग पैरामीटर के रूप में टोकन) की तुलना में यह सुरक्षा के दृष्टिकोण से अलग कैसे है। वास्तव में, मैं कल्पना कर सकता हूं कि हस्ताक्षरित अनुरोध अधिक प्रतिबंधात्मक हो सकता है, अर्थात सिर्फ एक विशेष समापन बिंदु तक पहुंचने की अनुमति दी गई है। लेकिन ओपी का # 2 आसान / कम कदम लगता है, इसमें गलत क्या है?
टायलर कोलियर

4
आपके वेब सर्वर के आधार पर पूरा URL इसकी लॉग फ़ाइलों में लॉग इन हो सकता है। आप अपने आईटी लोगों को सभी टोकन तक पहुँच नहीं चाहते हो सकता है।
एज़ेकिएस डिएनेला

2
इसके अतिरिक्त क्वेरी स्ट्रिंग वाला URL आपके उपयोगकर्ता के इतिहास में सहेजा जाएगा, जिससे एक ही मशीन के अन्य उपयोगकर्ता URL तक पहुँच सकते हैं।
एज़ेकिएस डिएनेला

1
अंत में और जो इसे बहुत असुरक्षित बनाता है, वह URL किसी भी संसाधन, यहां तक ​​कि तीसरे पक्ष के संसाधनों के लिए सभी अनुरोधों के रेफर शीर्षलेख में भेजा जाता है। इसलिए यदि आपका उदाहरण के लिए Google Analytics का उपयोग कर रहा है, तो आप Google को URL टोकन और उन सभी को भेज देंगे।
एज़ेकिएस डिएनेला

1
यह पाठ यहाँ से लिया गया: stackoverflow.com/questions/643355/…
Ezequias Dinella

10

पहले से ही उल्लिखित "भ्रूण / createObjectURL" और "डाउनलोड-टोकन" दृष्टिकोण का एक विकल्प एक मानक फॉर्म पोस्ट है जो एक नई विंडो को लक्षित करता है । एक बार जब ब्राउज़र सर्वर प्रतिक्रिया पर अटैचमेंट हेडर पढ़ता है, तो यह नया टैब बंद कर देगा और डाउनलोड शुरू कर देगा। यह वही दृष्टिकोण एक नए टैब में एक पीडीएफ जैसे संसाधन को प्रदर्शित करने के लिए अच्छी तरह से काम करने के लिए भी होता है।

यह पुराने ब्राउज़रों के लिए बेहतर समर्थन है और एक नए प्रकार के टोकन का प्रबंधन करने से बचता है। URL पर बेसिक कोर से बेहतर दीर्घकालिक समर्थन भी होगा, क्योंकि ब्राउज़र द्वारा यूआरएल पर उपयोगकर्ता नाम / पासवर्ड का समर्थन हटाया जा रहा है

पर क्लाइंट साइड उपयोग हम target="_blank"भी विफलता मामलों, जो स्पा (एकल पृष्ठ एप्लिकेशन) के लिए विशेष रूप से महत्वपूर्ण है में से बचने के नेविगेशन के लिए।

प्रमुख चेतावनी है कि सर्वर साइड जेडब्ल्यूटी मान्यता से टोकन प्राप्त करने है पोस्ट डेटा और हैडर से नहीं । यदि आपका फ्रेमवर्क स्वचालित रूप से ऑथेंटिकेशन हेडर का उपयोग करते हुए रूट हैंडलर तक पहुंच का प्रबंधन करता है, तो आपको अपने हैंडलर को अनऑथेंटेड / अनाम के रूप में चिह्नित करने की आवश्यकता हो सकती है ताकि आप उचित प्राधिकरण सुनिश्चित करने के लिए JWT को मैन्युअल रूप से मान्य कर सकें।

फ़ॉर्म को गतिशील रूप से बनाया जा सकता है और तुरंत नष्ट कर दिया जाता है ताकि इसे ठीक से साफ किया जा सके (ध्यान दें: यह सादे JS में किया जा सकता है, लेकिन JQuery का उपयोग स्पष्टता के लिए किया जाता है) -

function DownloadWithJwtViaFormPost(url, id, token) {
    var jwtInput = $('<input type="hidden" name="jwtToken">').val(token);
    var idInput = $('<input type="hidden" name="id">').val(id);
    $('<form method="post" target="_blank"></form>')
                .attr("action", url)
                .append(jwtInput)
                .append(idInput)
                .appendTo('body')
                .submit()
                .remove();
}

बस किसी भी अतिरिक्त डेटा को आपको छिपे हुए इनपुट के रूप में प्रस्तुत करना होगा और यह सुनिश्चित करना होगा कि वे फॉर्म में संलग्न हैं।


1
मेरा मानना ​​है कि यह समाधान बहुत कम है। यह आसान, साफ और पूरी तरह से काम करता है।
यूरा फेडोरिव

6

मैं डाउनलोड के लिए टोकन जेनरेट करूंगा।

कोणीय के भीतर एक अस्थायी टोकन प्राप्त करने के लिए एक प्रमाणित अनुरोध करें (एक घंटे का कहना है) फिर इसे यूआरएल में एक गेट पैरामीटर के रूप में जोड़ें। इस तरह से आप अपनी पसंद के अनुसार किसी भी तरह से फाइल डाउनलोड कर सकते हैं (window.open ...)


2
यह वह समाधान है जिसका उपयोग मैं अभी कर रहा हूं, लेकिन मैं इससे संतुष्ट नहीं हूं क्योंकि यह काफी काम की
चीज है

3
मुझे लगता है कि यह सबसे साफ समाधान उपलब्ध है और मैं वहां बहुत काम नहीं देख सकता। लेकिन मैं या तो टोकन की एक छोटी वैधता का समय चुनूंगा (जैसे 3 मिनट) या सर्वर पर टोकन की सूची रखकर इसे एक बार का टोकन बनाऊंगा और उपयोग किए गए टोकन को हटा दूंगा (मेरी सूची में नहीं आने वाले टोकन को स्वीकार करना )।
नबिनका

5

एक अतिरिक्त समाधान: मूल प्रमाणीकरण का उपयोग करना। हालाँकि इसके लिए बैकएंड पर थोड़ा काम करने की आवश्यकता है, टोकन लॉग में दिखाई नहीं देंगे और किसी भी URL हस्ताक्षर को लागू नहीं करना होगा।


ग्राहक की ओर

एक उदाहरण URL हो सकता है:

http://jwt:<user jwt token>@some.url/file/35/download

डमी टोकन के साथ उदाहरण:

http://jwt:eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIwIiwibmFtZSI6IiIsImlhdCI6MH0.KsKmQOZM-jcy4l_7NFsv1lWfpH8ofniVCv75ZRQrWno@some.url/file/35/download

इसके बाद आप में इस धक्का कर सकते हैं <a href="...">या window.open("...")- ब्राउज़र बाकी संभालती है।


सर्वर साइड

यहां कार्यान्वयन आपके ऊपर है, और आपके सर्वर सेटअप पर निर्भर है - यह ?token=क्वेरी पैरामीटर का उपयोग करने से बहुत अलग नहीं है ।

लारवेल का उपयोग करते हुए, मैंने आसान रास्ता तय किया और बुनियादी प्रमाणीकरण पासवर्ड को JWT Authorization: Bearer <...>हेडर में बदल दिया , जिससे सामान्य प्रमाणीकरण मिडलवेयर को आराम से संभालना पड़ा:

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.');
    }
}

यह दृष्टिकोण आशाजनक लगता है, लेकिन मुझे इस तरह से जेडब्ल्यूटी टोकन तक पहुंचने का कोई रास्ता नहीं दिखता है। क्या आप मुझे कुछ संसाधन के बारे में बता सकते हैं कि कैसे सर्वर इस अजीब यूआरएल को देखता है और jwt टोकन मूल्य का उपयोग कहां से करता है?
जिरी वेटेस्का

1
@JiriVetyska LOL PROMISING? टोकन हेडर आहाहा
तरल कोर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.